Cobol OTP challenge
Description
To save the future you have to look at the past. Someone from the inside sent you an access code to a bank account with a lot of money. Can you handle the past and decrypt the code to save the future?
Setup
The challenges include two files otp.cob
and out
otp.cob
is a Cobol program that at a glance, encrypts a message by xoring an unknown key (50 bytes) with a message (50 bytes as well)
out
looks like an encrypted message:
$ hexdump -C out
00000000 45 6e 74 65 72 20 79 6f 75 72 20 6d 65 73 73 61 |Enter your messa| 00000010 67 65 20 74 6f 20 65 6e 63 72 79 70 74 3a 0a a6 |ge to encrypt:..| 00000020 d2 13 96 79 3b 10 64 68 75 9f dd 46 9f 5d 17 55 |...y;.dhu..F.].U| 00000030 6a 68 43 8f 8c 2d 92 31 07 54 60 68 26 9f cd 46 |jhC..-.1.T`h&..F| 00000040 87 31 2a 54 7b 04 5f a6 eb 06 a4 70 30 11 32 4a |.1*T{._....p0.2J| 00000050 0a |.|
Issue 1
Taking a look at the heart of the encryption:
call 'CBL_XOR' using ws-key(ws-ctr:1) ws-flag by value ws-xor-len end-call
Reading the documentation https://documentation.microfocus.com/help/index.jsp?topic=%2FGUID-0E0191D8-C39A-44D1-BA4C-D67107BAF784%2FHRCLRHCALL5H.html we discover that this performs a
xor between ws-key[ws-ctr]
and ws-flag
with the length of ws-xor-len
bytes. The result is stored in ws-flag
.
Looking at the types we’ve understood that ws-ctr
and ws-xor-len
are one decimal digit long only. Therefore ws-ctr
will wrap around every 10 bytes.
Thus, this limits the valid key space, since the xor of the key with the encrypted data must yield a printable character. In this case, we see that
encrypted[1] ^ key[1] = printable_char1 encrypted[11] ^ key[1] = printable_char2 encrypted[21] ^ key[1] = printable_char3 encrypted[31] ^ key[1] = printable_char4 encrypted[41] ^ key[1] = printable_char5
And, of course, this holds for the key[2]
, etc..
Issue 2
Remembering that Cobol arrays are 1 based, the value of ws-flag(ws_ctr:1)
when ws_ctr
is 0 yields nothing and the xor is effectively nullified, and this gives us 5 unencrypted chars for free:
<code>*********u*********C*********&*********_*********\n</code>
Since we had ~40 options for every character and still have 9 key character to guess we start by assuming the message looks like “flag{***************}\n
“, where the * mean unknown character.
Pluging in the right key chars to yield the above string we get:
<code>flag{***_u_c4n_***_CO2_c3***_&_s4v3***3_fUtUr***}\n</code>
Now we could try to read the message and guess the needed keys. By assuming the word save (s4v3) should have _ after it and future is a reasonable word we tried all the options to get meaningful phrases from:
<code>flag{N**_u_c4n_b**_CO2_c3r**_&_s4v3_**3_fUtUrE**}\n</code>
With a little guess work we think it reads something along the lines of: Now you can buy CO2 cer** & save the future**
Pluging in our guesses gave us the full flag:
<code>flag{N0w_u_c4n_buy_CO2_c3rts_&_s4v3_th3_fUtUrE1!}</code>