CSAW Quals 2020

These are some quick writeups of some of the challenges that I solved for CSAW Quals 2020. Overall, the CTF was not that great, lots of guesswork, but we did qualify for the first time in my school’s history which was pretty sweet :)

We scored 15th for the US-Canada Undergraduate bracket and the top 15 qualified for finals this year.

Here was the final scoreboard:

cucknightsec would’ve been a better name

Anyway, let’s get into the challenges. Although I am more of pwn/rev guy, I tried just solving anything because we only had very few people from our team working on the CTF.


modus_operandi - 100pt

Gave you a server to netcat to. It would prompt you to enter some plaintext and then ask you if it was encrypted with ECB or CBC.

> nc crypto.chal.csaw.io 5001
Hello! For each plaintext you enter, find out if the block cipher used is ECB or CBC. Enter "ECB" or "CBC" to get the flag!
Enter plaintext: 
Ciphertext is:  683a5a5cb2ff42e2e8a2a8f22b0500c2
ECB or CBC? 

ECB is bad – it “encrypts identical plaintext blocks into identical ciphertext blocks, it does not hide data patterns well. ECB is not recommended for use in cryptographic protocols.”


So, if we wanted to determine if the block cipher used was ECB, we just give it the same plaintext for two blocks, and if we get two of the same blocks in the ciphertext, then it is ECB. Otherwise, it was encrypted with CBC. I scripted this and after about two hundred iterations, it terminated and I didn’t receive any flag. I guessed that the flag was binary encoded with ECB being 0 and CBC being 1, which gave me the flag.

authy - 150pt

Files: - handout.py

Textbook SHA1 length extension attack. I won’t bother trying to explain why it works, but here is a LiveOverflow video on it. Basically we have to set entrynum equal to 7, and admin and access_sensitive equal to True. The code given has a weird parsing bug where you can set parameters inside the name, because you could just do “foo&admin=True…”. I tried for a few hours trying to cheese it that way, but I couldn’t get the entrynum set to 7, but I could get the admin and access_sensitive equal to True.

I ended up using a tool called hashpumpy to do the length extension attack, bruteforcing the unknown key length. I spent a long time dealing with some weird Unicode bug which was not very fun.

adversarial - 200pt

Some AES CTR magic. It reused the key and IV for different plaintexts, basically making it a two-time pad (n-time pad?).

The formula is: C1 ^ C2 = P1 ^ P2, C2 ^ C3 = P2 ^ P3, etc.

So having two plaintexts XOR’d with each other is not readable but we are able to leak information. Here is a good StackOverflow post that explains the attack. I used some script I found online because I was being lazy and guessed Matrix quotes for the plaintext because the challenge seemed to be Matrix themed. I found some hex code in the pseudo-plaintext and submitted that to the netcat server to get the flag.


baby_mult - 50pt

Files: - program.txt

Decimal encoded x86 instructions. I just opened it in Binja, declared a function and could see the flag. 55 48 89 e5 is the classic x86 function prologue and easy to spot.

gamer moment

ezbreezy - 100pt

Files: - app

main just doesn’t even do anything, but then you find this weird section called .aj1ishudgqis with one function. Even though this function isn’t even called, let’s investigate. The function does a bunch of these weird checks repeated a bunch, like:

mov     byte [rbp+0x1 {var_8+0x1}], 0x9d
mov     dword [rbp-0x4], 0xa46d7
cmp     dword [rbp-0x4], 0x18e9b8
jne     0x800272  {0x1}


Each of these calls are going to jump 100% of the time, but okay, let’s inspect it some more. I patched the binary to jump to this function, and examined the stack after.

gef> x/10g $rsp-0x20
0x7fffffffdf38:    0x7fffffffdf70    0x555555555080
0x7fffffffdf48:    0x50b9500000000    0x875b9e5c90879da3    <-------
0x7fffffffdf58:    0x5b9a5b9e588b5b9a   <------ 0x7fa55b95878c
0x7fffffffdf68:    0x100000000    0x555555555200
0x7fffffffdf78:    0x7ffff7df9152    0x7fffffffe068

This definitely looked like some weird hex, but how could we get a flag from it. It’s simple: guess.

I just randomly checked the first byte of the ‘encrypted’ bytes subtracted by the first byte of the known first character of the flag ‘f’. Hmm, difference of 40, let’s see what the difference is for the second bytes. 40…

Luckily I guessed this pretty quickly, others were not so lucky.

not_malware - 150pt

Files: - not_malware

This one just a credit card number checker, could’ve probably done it really easily with angr, but I just did it by hand with me debugging and my teammate guyinatuxedo taking some notes and keeping track of the final credit card ‘number’. Ours was kinda weird compared to other’s I have seen: softbank:000:11111111111111111111:endyzABCDEFGHIJKLMNOPQ:end

but whatever ¯\_(ツ)_/¯. Passing that to the server netted us our flag.

In Review

My team and I were very close to solving take-it-easy, we guessed the tool (steg86) at the last hour, but could not guess the XOR key of XOR!. Kind of sad, but makes me laugh now, because that is the stupidest reversing challenge I have ever seen. I was bindiff-ing for hours, looking for anything, maybe some new function, because this was supposed to be reversing. Turned out to be stego.

I didn’t think this CSAW lived up to the standards of years prior.

At least I am going to finals :v)