# bank

by icemonster and majo

Points: 94 (Dynamic, 56 solves)

Description:

You’ve earned a lot of cash from investing bitcoins! Why not try our coolest multi-signature bank?

nc tcp.realworldctf.com 20014

Given: bank.zip

# Solution:

### TLDR

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

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

3. Use a so-called rogue-key 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 `multi-schnorr.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 multi-signature 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 rogue-key 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 non-zero 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.