InsoBank

The intended solution to this challenge involve using parser differential between how decimals are treated between postgres and MySQL.

Challenge :

file-archive
42KB

In challenge, A new logged in user gets a balance of 10$, with 3 types of accounts

  1. Savings Account

  2. Checkings Account

  3. Current Account

User can transfer the balance only to any of these 3 account, not into any other user's account. To retrieve the flag, we must increase our balance to 13.37$ (LINE 16)

The application uses

  1. MYSQL to manage account and user balance ,&

  2. POSTGRES to manage transaction.

Basically every transaction is recorded in both Postgres & Mysql and later a cronjob updates the final balance in mysql accounts table.

After a thorough code review, here are the vulnerabilities that we can exploit:

  1. Parser deferential : Discrepancies in Data Precision

There are differences in the data precision of the amount field between two batch_transactions tables.

In the MySQL database, the data type of amount is decimal(10,2), while in the PostgreSQL database, it is just decimal. This causes a floating-point number like 0.014 to be stored in MySQL as 0.01, but as 0.014 in PostgreSQL.

While adding a transaction, if in transaction table sum(amount in current batch) + user entered amount <= user balance , we insert this transaction with user entered amount into transaction table. Straight forward.

The /validate route then does the final deduction of money from current account's balance, and a seperate cronjob add balance to correct account.

The amount to deduct is taken from mysql db based on decimal(10,2), while the amount to increase is based on decimal. The following cronjob increase the balance, which as you can see is getting value from postgres.

Lets understand the flow

So , If account1 transfer 0.014$ from it to account2,

  1. mysql will store 0.01$ in transaction table and postgres will have 0.014

  2. when we validate this transaction, 0.01$ will be deducted from balance

  3. crontab will run, sum all the values to be added to account2 , in this case only 0.014$ and insert this into mysql table which will round it off to 0.01$ and store that in balance..

hmm now consider doing 2 transactions to account2

  1. mysql will store 2 transactions of 0.01$ in transaction table and postgres will have 0.014 and 0.014.

  2. when we validate this transaction, 0.02$ will be deducted from balance

  3. crontab will run, sum all the values to be added to account2 , in this case only 0.014+0.014=0.028$ and insert this into mysql table which will round it off to 0.03$ .

So here amount deducted is 0.02 and amount added is 0.03. PROFIT $_$.

Now challenge doesnt allow us do 2 transaction to same account in single batch. what now?

2. Race Condition :

There is no lock when looking up the table to check for redundant transfers.

Lets try transferring 0.014$ into same account using turbo intruder.

Great, we have 3 sucessfull transfers.. So we can combine 1st and 2nd bug to siphon any amount of money.

GG

SOLUTION 2

Another solution was :

  1. Create a transaction batch, create a batch line with 9.0000000000000000000000000000000000000000000000000000000099999999999999999 as an amount, will 500 error, create a 2nd line to the same bank account to 1 and confirm

  2. 9.0000000000000000000000000000000000000000000000000000000099999999999999999 is not a valid float on mysql

  3. mysql errors out during inserting it into batch_transactions, but postgresql will

  4. You add a line of 1 to avoid the tables to be locked indefinitely during /validate route

Last updated