Skip to content

Latest commit

 

History

History
83 lines (54 loc) · 3.53 KB

File metadata and controls

83 lines (54 loc) · 3.53 KB

Challenge name: Oracle of Blair

Description:

Not to be confused with the ORACLE of Blair. Source

nc crypto.2021.chall.actf.co 21112

P.S: given files: server.py

Solution:

My favorite challenge in this CTF! We have server code, which take an input from us, replace any "{}" occurrence with flag string and then apply some cryptography to it.

First I tried to get flag size. Sending '{}' to server and get 32bytes of data, meaning that flag length is in range 17 to 32. All we need to do is adding character to the string one by one and see the results.

for i in range(16):
    pd1 = 'a'*i + '{}'
    y = f_send(pd1.encode().hex())
    print ('i: {0}, len(y): {1}'.format(i, len(y)))

With i=8 we get 96 instead of 72, which mean the flag length is 32 - 7=25. Then I thought this is a Padding Oracle challenge. After a little implementation, I realized server is decrypting inp, not encrypting! So I came up with a new attack. First let's have a look at Block Cipher CBC mode decryption schema:

block_cipher_cbc_decryption

Assume we are giving server pd1 and flag is something like this:

pd1 = 'a'*7 + '{}' + 'a'*32
flag = 'actf{XXXXXXXXXXXXXXXXXXX}

Then inp will be:

aaaaaaaactf{XXXX XXXXXXXXXXXXXXX} aaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaa

Now, let's call first 16 characters of 'a' C1 and second 16 characters of 'a' C2 and "XXXXXXXXXXXXXXX}" C0. The server response will be 128 hex characters representing 64bytes of data. According to previous sentence, the last 16bytes of server response will be P2, the 16bytes before that will be P1. We know I2=I1 (C1=C2 and CBC mode basics!). We also know that C1^I2=P2 ('^' is XOR operator). So we have C1^P2=I2.

We know all of this for previous block too, C0^P1=I1. We know C1, P1, P2. Then we can calculate C0 as:

C0 = (P2 ^ C1) ^ P1

So using this input, we calculate last 16bytes of flag. We can do this for first 9bytes of flag too. Now we give server pd2:

pd2 = 'a'*7 + '{}' + flag_2.decode('ascii')

Where flag_2 is last 16bytes of flag which we computed in previous round. inp will be:

aaaaaaaactf{XXXX XXXXXXXXXXXXXXX} XXXXXXXXXXXXXXX}

Similar to previous round, we have:

C0 = "aaaaaaaactf{XXXX"
C1 = "XXXXXXXXXXXXXXX}"
C2 = "XXXXXXXXXXXXXXX}"

Like previous round we know C1, P1, P2. Then we can calculate C0 as:

C0 = (P2 ^ C1) ^ P1

final exploit.py

Output

[+] Opening connection to crypto.2021.chall.actf.co on port 21112: Done
[*] Round 1
    | pd1: 616161616161617b7d6161616161616161616161616161616161616161616161616161616161616161
    | server response: b'd26f173c624760c6bee3613d57905e983efbcf4caaa235a4b8a9b3559bef21609f6ca0745f99ce624552a5f677f85ff09362b3706194c668416ca1f474c65dec'
    | P1: 9f6ca0745f99ce624552a5f677f85ff0
    | P2: 9362b3706194c668416ca1f474c65dec
    | C1: 61616161616161616161616161616161
[+] C0: b'more_like_ecb_c}'
[*] Round 2
    | pd1: 616161616161617b7d6d6f72655f6c696b655f6563625f637d
    | server response: b'b09cbe77feb3635924439d2a0de64ba03efbcf4caaa235a4b8a9b3559bef216032f5dc4894af3daebe82b04d9ad22142'
    | P1: 3efbcf4caaa235a4b8a9b3559bef2160
    | P2: 32f5dc4894af3daebe82b04d9ad22142
    | C1: 6d6f72655f6c696b655f6563625f637d
[+] C0: b'aaaaaaaactf{cbc_'
[+] Flag: b'actf{cbc_more_like_ecb_c}'

The Flag

actf{cbc_more_like_ecb_c}