bank  Real World CTF Quals 2019
bank
by goulov and ice
Points: 94 (Dynamic, 56 solves)
Description:
You’ve earned a lot of cash from investing bitcoins! Why not try our coolest multisignature bank?
nc tcp.realworldctf.com 20014
Given: bank.zip
Solution:
TLDR

Check that to get the flag we must sign WITHDRAW in a multisignature together with the bank.

The multisignature is done by computing an aggregated key from the composition of the two keys using the binary group operation.

Use a socalled roguekey attack to forge a signature that looks like it was signed by both users.
Attack
Description
The first thing we must do is to provide our public key to the server. Here we have a Schnorr signature scheme implemented using Elliptic Curves, so this is a curve point. At this step this does not really matter and we provide a random point just to make the program continue.
Then we are greeted by the following menu:
1. Deposit a coin into your account, you can sign a message 'DEPOSIT' and send us the signature.
2. Withdraw a coin from your account, you need to provide us a message 'WITHDRAW' signed by both of you and our RESPECTED BANK MANAGER.
3. Find one of our customer support representative to assist you.
From reading the code from multischnorr.py
we can see that in order to get the flag, we must make a WITHDRAW, and for this we must sign a message in a multisignature together with the bank
.
Now, are going to forge this multisignature. We start by asking for option 3
, which will provide us the public key of the bank
.
At this instant, the most crucial detail takes place… We are asked again for our public key. The point is, since we now know the bank’s public key, we can choose our new key depending on the key of the bank. This is what’s called a roguekey attack.
The way we forge our new key public key is as follows (
is the inverse of the point in the curve):
fake_key = our_pk  bank_pk
Since the multisignature implemented here combines the public key we provide (aka fake_key
) with the bank’s public key, composing both using the group law, we get:
Verify(fake_key + bank_pk, sig, M) = Verify((our_pk  bank_pk) + bank_pk, sig, M) = Verify(our_pk, sig, M)
Execution
Finally, we execute the attack by setting our public key as our_pk
, and making a DEPOSIT of a coin by sending Sig(our_sk, 'DEPOSIT')
, which will increment the balance of our account. Our balance must be nonzero in order to call the WITHDRAW command.
Then, when asked again, set our public key as fake_key
and send the signature of WITHDRAW computed as sig = Sig(our_sk, 'WITHDRAW')
.
As shown above, this will make the bank_pk
cancel, and we are left with a signature verification that only uses our_pk
to verify sig
. Since we know the matching our_sk
, we use exacly this secret key to compute sig
, which we send to the server… And we are given the flag !!
rwctf{P1Ain_SChNorr_n33Ds_m0re_5ecur1ty!}
Resources:
Exploit in solve.py.