Just a little summary about the server-side of the application in bullet-points because that is the part I studied the less:
- Code is written in Java using the classes of the Framework.
- App is deployed in container such as Tomcat.
- Everything is the same as in case of other Java web applications, like the WEB-INF directory or the config files.
There are four important message types:
- Framework module download
- File upload
- File download
As you could have guessed already the synchronization messages are the most important. It is not really straightforward when will a message be sent, for instance one would think that posting a form by clicking the submit button will do the syncing, but actually syncing is independent from buttons. The client will even sync when you fill an input field and leave it. So you always have to keep one eye on your proxy or firebug or whatever you use. To give an example here is a simple synchronization message together with the response:
POST /NumberGuess/app?serviceId=Echo.Synchronize HTTP/1.1 Host: 192.168.109.104:8080 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Proxy-Connection: keep-alive Content-Type: text/xml; charset=UTF-8 Referer: http://192.168.109.104:8080/NumberGuess/app Content-Length: 462 Cookie: JSESSIONID=FDF8EA7C30FCC052A52A90489D5A646E Pragma: no-cache Cache-Control: no-cache <client-message xmlns="http://www.nextapp.com/products/echo2/climsg" trans-id="1" focus="c_7"> <message-part xmlns="" processor="EchoPropertyUpdate"> <property component-id="c_7" name="text">22</property> <property component-id="c_7" name="horizontalScroll" value="0"/> <property component-id="c_7" name="verticalScroll" value="0"/> </message-part> <message-part xmlns="" processor="EchoAction"> <action component-id="c_8" name="click"/> </message-part> </client-message> HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Pragma: no-cache Cache-Control: no-store Expires: 0 Content-Type: text/xml;charset=UTF-8 Date: Fri, 01 Jun 2012 15:42:28 GMT Content-Length: 1858 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <server-message xmlns="http://www.nextapp.com/products/echo2/svrmsg/servermessage" async-interval="disable" modal-id="" trans-id="2"> <libraries/> <message-part-group id="init"> <message-part processor="EchoServerDelayMessage.MessageProcessor"> <set-message> <content> <div xmlns="http://www.w3.org/1999/xhtml" id="serverDelayMessage" style="position:absolute;top:0px;left:0px;width:100%;height:100%;cursor:wait;margin:0px;padding:0px;visibility:hidden;z-index:10000;"> <table style="width:100%;height:100%;border:0px;padding:0px;"> <tbody> <tr> <td> <div id="serverDelayMessageLong" style="margin-top:40px;margin-left:auto;margin-right:auto;background-color:#afafbf;color:#000000;padding:40px;width:200px;border:groove 2px #bfbfcf;font-family:verdana,arial,helvetica,sans-serif;font-size:10pt;text-align:center;">Please wait...</div> </td> </tr> </tbody> </table> </div> </content> </set-message> </message-part> </message-part-group> <message-part-group id="preremove"/> <message-part-group id="remove"> <message-part processor="EchoDomUpdate.MessageProcessor"> <dom-remove target-id="c_4"/> <dom-remove target-id="c_5"/> <dom-remove target-id="c_6"/> </message-part> </message-part-group> <message-part-group id="update"> <message-part processor="EchoDomUpdate.MessageProcessor"> <dom-add> <content parent-id="c_2_cell_c_4"> <span xmlns="http://www.w3.org/1999/xhtml" id="c_4">Your guess, 22 was too low. Try again:</span> </content> </dom-add> <dom-add> <content parent-id="c_2_cell_c_5"> <span xmlns="http://www.w3.org/1999/xhtml" id="c_5">You have made 1 guess.</span> </content> </dom-add> <dom-add> <content parent-id="c_2_cell_c_6"> <span xmlns="http://www.w3.org/1999/xhtml" id="c_6">Guess a number between 23 and 100:</span> </content> </dom-add> </message-part> </message-part-group> <message-part-group id="postupdate"/> </server-message>
Mind that the XMLs are normally not pretty formatted I just wanted to make it readable. As you can see it is not so difficult to understand what happens. The client sends the values and in the ‘processor’ attribute tells who should deal with the data. In the response the server simply tells the client what to change in his state in DOM. And here you could see the first thing that makes the pentester’s life miserable: the field IDs. I will talk about them later. And more or less that was the synchronization.
Framework modul download
GET /NumberGuess/app?serviceId=Echo.ClientEngine HTTP/1.1
When you have a file upload form then the system gets everything ready in the background for the file upload such as creating an ‘uploaduid’. Without ‘uploaduid’ there is no fun, and it will be created when the upload form is opened. Here is an example request:
POST /testapp/app?serviceId=Echo.Upload&uploaduid=3503 HTTP/1.1 Host: 192.168.109.104:8080 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive Referer: https://192.168.109.104:8080/testapp/app?serviceId=uploadformUid&uploadformUid=3503 Cookie: JSESSIONID=CAF49DC8F468C21858EE7E45C77704EF Content-Type: multipart/form-data; boundary=---------------------------169443243924626 Content-Length: 211 -----------------------------169443243924626 Content-Disposition: form-data; name="Echo.UploadForm.File"; filename="test2.txt" Content-Type: text/plain test -----------------------------169443243924626--
The response will be a 302 Moved Temporarily which will redirect you back to your upload form.
That is somewhat the same as the upload. A server-side ‘downloaduid’ is created just before the download so it could be sent to the client. As the user clicks on the download a GET request is sent like the following:
GET /testapp/app?serviceId=Echo.Download&downloaduid=568bf3ec_1371258a8cd_1b53 HTTP/1.1
The response will be something like that:
HTTP/1.1 200 OK Date: Tue, 08 May 2012 15:21:24 GMT Server: Apache/2.2.22 (EL) Pragma: cache Cache-Control: no-store Expires: 0 Content-Disposition: attachment; filename="test2.txt" Connection: close Content-Type: text/plain; charset=UTF-8 Content-Length: 4 test
To summarize it in one sentence: pentesting an Echo2 application is a pain in the ass. The developers could take it as a compliment but that will not make it better for you if you are a pentester. So let see why.
As you could see in the synchronization examples the system uses generated IDs for the fields on the UI. I can tell you right now that it changes all the time, so when you open a window then close it and open it again the IDs will be different. The only thing that seems to be fixed is the difference between two IDs because the number in it is just an incremented value and probably the IDs are assigned always in the same order.
If the component ID was not enough to make you cry then here is another one. The transaction ID is used to keep track the requests. There is always a trans-id in the request and in the response of it will be incremented by 1. The next request should use the ID of the last response. Whenever a request is received from the server which has an unexpected transaction ID then the server will force the client to do a full synchronization to fix it.
Automation … nope
I’m sure you have already figured out that automation will not work easily. Due to the use of the component IDs and the transaction IDs replaying requests and automating it is really difficult I would say almost impossible. Guessing the right component ID is possible but difficult. I tried to check the last transaction ID and setup Burp Intruder by using it but it takes a lot of time and if anything goes wrong the server will go to an error state when it waits for the client to re-synchronize until that no request will do anything. Also many times the data is not sent at the same time when the action is performed. For instance in case of a form when you click the ‘submit’ button then only this fact will be sent to the server (like <action component-id=”c_2297″ name=”click”/>) because the input data have already been synchronized to the server.
CSRF … not really
Although there is no dedicated protection against CSRF such as special token or hidden field, still it is more or less infeasible. To execute a successful CSRF the attacker will have to know the correct component IDs the current transaction IDs and the whole state of the application when the attack is executed.
XSS … you try it
XML injection … still no
XML injection seems like a good idea because the whole communication is done using XML so at least there are XMLs to inject into. However after thinking it through one just won’t find the target. On server-side there is no XML to inject into one could only attack the parser. Data could be stored in XML on server-side but again it’s up to the application. So if your app does that then there is an attack surface but otherwise there isn’t. However injecting in the server response XML could be interesting but in my case it was well protected by the server-side validation and escaping.
Ok so far I wrote down everything that makes a pentester cry. So let me make up for that and add something constructive to this post.
- When planning the test take into account that without automation it will take much longer time to test a few things. Probably you will need to plan more time for simple tasks.
- Focus on the application itself instead of the Echo framework. So instead of testing every input field for XSS, try to test the application behind the framework. Business logic or anything that is not implemented in the framework will potentially have more vulnerabilities then Echo.
- Test features and interfaces that are not offered by the framework. If there is another interface to the system then you will have a greater chance to find something there because the developers usually get used to the fact that the framework does everything for them and they tend to forget when they step outside of the protection of it.
- Since the normal testing routine and automation with Burp doesn’t work I did most of the things manually. For me the fastest (but still awkwardly slow) way to test requests was to rewrite them in Burp proxy instead of storing it in Repeater and testing from there.
So that is all about the Echo2 framework. I can assure you that there are bugs and vulnerabilities in Echo applications as well just the framework makes it a bit difficult to find them. However testing the framework itself could be an interesting project…maybe if I have some time.