Bypassing Asymmetric Client Side Encryption Without Private Key

Sourav Kalal
InfoSec Write-ups
Published in
7 min readMar 10, 2023

--

Keys

I recently wrote an article on how we can bypass client-side encryption. With the help of the PyCript burp suite extension, we can make manual and automated pentesting or bug bounty much easier on applications with client-side encryption. The use of the PyCript extension fails when the application uses asymmetric encryption.

Since asymmetric encryption uses private and public key mechanisms. It's not possible to decrypt the request without having both keys. The application will store the public on the client side and will use the public key to encrypt the request. To decrypt we need a private key and in most cases, we won’t have the private key since it's stored on the server side.

The only options left to test the application with asymmetric encryption are using the browser console with breakpoint and in the case of mobile application, we need to use the Frida to log the plain text.

Solution

The only possible solution I was able to figure out was using the Chrome override feature with PyCript configured in Burp Suite. The Chrome browser allows us to edit the JavaScript file and load the JavaScript file from the local system. We can use it to modify our application JavaScript file to send a plain text request instead of an encrypted request.

Browser Flow

The above flow is simple where JavaScript code will send the HTTP request for each action we perform on the browser UI. The same code will call another JavaScript code to encrypt the request and return the encrypted value. The main code will now send the encrypted request and the same will be visible in the burp suite proxy.

Modified Flow

In the above, we have modified the encryption code using the chrome override feature to return the original plain text instead of the encrypted request. In this case, we can see the decrypted request in the burp suite proxy. Since the server will expect the encrypted request we will configure the PyCript to encrypt the request using the same public key and encryption logic as the application's JavaScript code.

Using the above approach we will have a plain text request in the burp suite proxy history and we can use the same plain text request everywhere like for repeater or intruder. The application on the server side will receive the encrypted request with the help of the PyCript extension.

Example

I have the below application by intercepting the request in the proxy I can confirm that the application is using some kind of encryption.

Encrypted Request

After looking into the JavaScript code and searching for keywords like encrypt, key, encryption,decrypt etc. I got the encryption code.

Encryption Code

The above code looks quite simple and easy to understand. The function takes one argument and it will be the plain text data. Next, the code converts the PEM public key and encrypts the plain text data using the key. Lastly, the code base64 encodes the encrypted data and returns it. After doing some research I found that the application is using the node-forge library for encryption.

Debug

Now just to confirm that the same code is responsible for encryption, I add a breakpoint and submit the request in the browser. The browser stops when the breakpoint code is triggered. I can confirm that the same code is used for encryption and the function is taking plain text value.

Values

I keep the breakpoint the same as it is and call the variables from the console and I got the public key used for encryption. Now we want that the function should return the plain text or the same value instead of the encrypted value.

Override

Now to modify the JavaScript code we need to use the override from the chrome browser. Select the Overridesfrom the source tab. Click on the Select folder for overrides.

Override

Once you select the folder you need to approve it. Click on the allow and it will allow you to modify the files. Now go to the file which we want to edit.

Save override

Right-click on the file and select Save for overrides. and now if we back to the override tab we can see the same file is added which allows us to edit the JavaScript code.

Edit Encryption Logic

Now we need to edit the JavaScript code. We modify the code and remove the encryption code and return the same plain text value that was passed to the function. Once we complete the editing we can save it with ctrl+s.

Once it's completed I need to verify if it's working as expected. I go back to the application and perform any action so any request will be sent. In the burp suite, I need to verify if the request is in plain text or not.

Plain the request

Now I can confirm that I was able to modify the client-side logic and can see the data in plain text format in the burp suite proxy. The application is giving an error as it was expecting the encrypted data and we have sent the plain text data. Now I need to implement the PyCript to auto-encrypt the request before sending it to the server. but before that, we need to use the same encryption logic to write the encryption code for PyCript.

Encryption with PyCript

As mentioned the application is using the node-forge library for encryption, I need to install it using the command provided in the node-forge readme file.

var forge = require('node-forge');
const program = require("commander");
const { Buffer } = require('buffer');
program
.option("-d, --data <data>", "Data to process")
.parse(process.argv);

const options = program.opts();
const requestbody = Buffer.from(options.data, 'base64').toString('utf8');


var mypubkey = "-----BEGIN PUBLIC KEY-----MIIwYIYquwxIqzkgkI+oA9oyrbYQIDAQAB-----END PUBLIC KEY-----"
var m = forge.pki.publicKeyFromPem(mypubkey)

var encoutput = m.encrypt(requestbody)

console.log(forge.util.encode64(encoutput));

In the above code, I am using the same format required for PyCript and with node-forge, I am encrypting the data and lastly printing the encrypted data after encoding it with base64. The encryption method and logic is the same as the application was using with some modification based on PyCript requirements.

PyCript

Since in my case the request is already in plain text format and I just need to encrypt it and doesn't require decryption. I don’t need to write the decryption code but since the extension requires both codes to work, I can provide the same code for both encryption and decryption just to ignore the restrictions. Once I load the script I can select the request type in my case only values are encrypted in JSON I will select the appropriate type.

Lastly, I need to select the tool type, since all requests will come from the browser I select the proxy and turn on the auto encrypt.

Plain text

I go back to the browser and perform any action and verify that again the browser will send the plain text request. But now I am not getting any error as an invalid request and the application is giving valid responses even though my request is in plain text format.

Encrypted Request

If I go to the logger in the burp suite I can see that request is encrypted even though I can see the plain text request in the proxy. This approach will keep the plain text request in the burp proxy so I can use it later and will not require any private key to decrypt it.

The approach is not the best but still works and allows us to ignore symmetric encryption. A similar approach can use used in the mobile application as well. For the mobile application, we need to return the plain text value instead of the encrypted value same as we did for the web. But in the case of mobile we either need to use Frida or we can modify the mobile application code

All the tools can be found below.

Twitter — https://twitter.com/ano_f_

GitHub — https://github.com/Anof-cyber/

Linkedin — https://www.linkedin.com/in/sourav-kalal/

--

--

• Application Security • Security Engineer and Consultant • Open Source Developer • Security Automation