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:
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: foobar Ciphertext is: 683a5a5cb2ff42e2e8a2a8f22b0500c2 ECB or CBC? ECB
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
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
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.
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], 0x9d mov dword [rbp-0x4], 0xa46d7 cmp dword [rbp-0x4], 0x18e9b8 jne 0x800272
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:
but whatever ¯\_(ツ)_/¯. Passing that to the server netted us our flag.
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)