Unoffical Browser Based Python API of 5paisa

Well, 5paisa’s normal API sucks to its core.

  1. It will log you out at random times.
  2. The response to the order book is not proper.
  3. More importantly, It is not giving the correct price of entry sometimes.
  4. It will tell Your own birthday is wrong which is 2FA. So, when you get yourself confused and try few times it will tell you to change the password.
  5. Now, if you wait for 15 mins, it will let you retry again.

Here is how to crash the entire broker like Thanos’ Snap.

Just write a Python Requests scrip and burst an attack to all the userIDs at once. Its basic permutation combination of six numbers. Everyone will get error at once.

Don’t worry. It really will happen. I tested it.

========

So, despite having a shitty UI they decided to focus on increasing complexity. If you have seen the Youtube video https://youtube.com/watch?v=2PXSfi0Rsho they used to give a RequestToken after we login and the home page is refreshed right?

Now they are using 500% brain.

The RequestToken is generated upon visit of the website in form of cookies as usual and used as an Encryption Key.

from Crypto.Cipher import AES
import base64
from pbkdf2 import PBKDF2
ENCRYPTION_KEY ='l9LxB58COlrc3NJ5ukGte5PcKr24_qLmea2KYI-Wrpv993Z47CSmdVg25nXRVHqESjF9JOWp_0ZQ-Lx_Aroh8y5Zhvw1'
zername=''
zerpass=''
zer2fa=''
#ENCRYPTION_KEY = zer2fa
class EncryptionClient:
    def __init__(self):
        self.iv = bytes([83, 71, 26, 58, 54, 35, 22, 11,
                         83, 71, 26, 58, 54, 35, 22, 11])
        self.enc_key = ENCRYPTION_KEY
    def _pad_and_convert_to_bytes(self, text):
        return bytes(text+chr(16-len(text) % 16)*(16-len(text) % 16), encoding="utf-8")
    def encrypt(self, text):
        padded_text = self._pad_and_convert_to_bytes(text)
        key_gen = PBKDF2(self.enc_key, self.iv)
        aesiv = key_gen.read(16)
        aeskey = key_gen.read(32)
        cipher = AES.new(aeskey, AES.MODE_CBC, aesiv)
        return str(base64.b64encode(cipher.encrypt(padded_text)), encoding="utf-8")
encryption_client = EncryptionClient()
secret_email = encryption_client.encrypt(zername)
secret_passwd = encryption_client.encrypt(zerpass)
secret_dob = encryption_client.encrypt(zer2fa)

I have modified the code I found on github.com/5paisa/py5paisa/blob/master/py5paisa/auth.py.

So, the Password is only getting modified now to encryption, and the payload changes slightly.
It will look something like this -

data = {
  '__RequestVerificationToken': 'AKa6MktupjDt_UHDJywvC57ogJRvQkgSeQR-GjJ_PukA6lNu5RwgG5hZx1oPE6msq8uwTYEDhM_FjTfyHk3EdIZ7rIQ1',
  'login.UserName': 'xxxxxxx',
  'login.Password': 'XczugGNeBl/1YcGHKCzRsw==',
  'login.DOB': 'xxxxx',
  'login.OTP': '',
  'login.Token': 'AKa6MktupjDt_UHDJywvC57ogJRvQkgSeQR-GjJ_PukA6lNu5RwgG5hZx1oPE6msq8uwTYEDhM_FjTfyHk3EdIZ7rIQ1'
}

I took the liberty of changing the user, pass, and dob. Also, notice their dumbness as the Login token is the same!

A work of a sloppy coder using a junk variable of the same output. Right?

Now, We have two ways of solving it. You can actually fix the Request Verification Token yourself as hardcoded. Now the thing about cryptology is - If your key is fixed, the outcome of password encryption is also fixed.

So, just use the request token and encrypted password and avoid the crypto auth module completely.

Now, the core question is - If I update my Github, I am pretty much sure they will change it again breaking the flow. (I am their partner; So I always stay in touch with various employees.)

You can try your hands on reverse engineering. It’s quite a quick but tricky fix. Let’s hope they don’t break up again. Do share about your journey if you try hand and forgive me for not sharing the rest of the browser based method in Github for the said reasons.