Credit Card Rejection & AES Decryption

Key Takeaways & TL;DR

Bank credit card rejection responses were AES-encrypted on the client. Decrypting the response revealed the rejection reason Posidex Reject, indicating rejection based on demographic deduplication against existing customer profiles.

Wow, what a thrilling experience it was to apply for the same credit card multiple times and get rejected every single time for no apparent reason, even though I passed all the necessary parameters such as Good Credit Score, Payment History, Existing Customer of that bank, Turnover, Average Monthly Balance, etc.

I mean, who doesn't enjoy the feeling of being repeatedly rejected for no reason, right?

So, I decided to do some detective work and opened the browser's network tab to check the API responses. Lo and behold, all the responses were encrypted, and all of them included only one key: EncryptedString. Oh, what a joy! I just love trying to decrypt things, don't you?

Encrypted response payload with EncryptedString key from bank server
Encrypted response from bank's server containing EncryptedString

After tirelessly searching for how the response was decrypted on the client side, I checked the browser's source tab and looked through the bundled files. After finding the client-libraries (cryptography) and understanding the code, I finally stumbled upon the function used to decrypt it. And guess what? The algorithm used was AES! It's almost like they wanted to make it difficult for me to figure it out or something.

Decryption function in client-side javascript bundle
Locating the AES decryption logic inside client bundle

I ran the decryption code on my local machine and after the third attempt, I finally found the response that contained a substantial amount of data, and what do you know, it contained the magical key:

Running standalone NodeJS script with crypto-js to decrypt the payload
Decrypting the payload locally using crypto-js
"subStage": "Posidex Reject" (which included the text REJECT).
Decrypted JSON showing Posidex Reject in subStage
Decrypted JSON payload highlighting "subStage": "Posidex Reject"

Now, what in the world could that mean? A quick Google search turns up nothing of value, but eventually, I found a developer page from another bank that explains the brilliant concept of Posidex Dedupe. Apparently, they use demographic data to match up potential customers with existing ones, and if they don't like what they see, they reject the application.

Posidex Customer Dedupe documentation explaining customer demographic matching
Posidex dedupe system documentation explaining credit matching logic

So, in essence, I got rejected because of what other existing bank customers have done in the past. Isn't that just fantastic? Nothing like being held accountable for the actions of complete strangers. Thanks for yet another thrilling rejection experience.

Ali Abbas

Software engineer who likes building close to the metal, reverse engineering APIs, and exploring client-side security.