CSAW Quals 2020 baby-mult & ezbreezy

Published:

I didn’t play CSAW Quals with RPISEC but thought I’d take a look at the reversing challenges afterwards to practice using Binary Ninja. I have a commercial license… so I should use it, right?

Here’s how I did the first two reversing challenges: baby-mult and ezbreezy.

baby-mult

This challenged gave a text file named “program.txt” with the following:

85, 72, 137, 229, 72, 131, 236, 24, 72, 199, 69, 248, 79, 0, 0, 0, 72, 184, 21, 79, 231, 75, 1, 0, 0, 0, 72, 137, 69, 240, 72, 199, 69, 232, 4, 0, 0, 0, 72, 199, 69, 224, 3, 0, 0, 0, 72, 199, 69, 216, 19, 0, 0, 0, 72, 199, 69, 208, 21, 1, 0, 0, 72, 184, 97, 91, 100, 75, 207, 119, 0, 0, 72, 137, 69, 200, 72, 199, 69, 192, 2, 0, 0, 0, 72, 199, 69, 184, 17, 0, 0, 0, 72, 199, 69, 176, 193, 33, 0, 0, 72, 199, 69, 168, 233, 101, 34, 24, 72, 199, 69, 160, 51, 8, 0, 0, 72, 199, 69, 152, 171, 10, 0, 0, 72, 199, 69, 144, 173, 170, 141, 0, 72, 139, 69, 248, 72, 15, 175, 69, 240, 72, 137, 69, 136, 72, 139, 69, 232, 72, 15, 175, 69, 224, 72, 15, 175, 69, 216, 72, 15, 175, 69, 208, 72, 15, 175, 69, 200, 72, 137, 69, 128, 72, 139, 69, 192, 72, 15, 175, 69, 184, 72, 15, 175, 69, 176, 72, 15, 175, 69, 168, 72, 137, 133, 120, 255, 255, 255, 72, 139, 69, 160, 72, 15, 175, 69, 152, 72, 15, 175, 69, 144, 72, 137, 133, 112, 255, 255, 255, 184, 0, 0, 0, 0, 201

I assumed that this was a list of bytes in decimal form because each value seemed to be between 0 and 255 (0x00 and 0xFF).

So, I changed this list of decimal values to a list of bytes in Python:

l = [85, 72, 137, 229, 72, 131, 236, 24, 72, 199, 69, 248, 79, 0, 0, 0, 72, 184, 
    21, 79, 231, 75, 1, 0, 0, 0, 72, 137, 69, 240, 72, 199, 69, 232, 4, 0, 0, 0, 
    72, 199, 69, 224, 3, 0, 0, 0, 72, 199, 69, 216, 19, 0, 0, 0, 72, 199, 69, 208, 
    21, 1, 0, 0, 72, 184, 97, 91, 100, 75, 207, 119, 0, 0, 72, 137, 69, 200, 72, 
    199, 69, 192, 2, 0, 0, 0, 72, 199, 69, 184, 17, 0, 0, 0, 72, 199, 69, 176, 193, 
    33, 0, 0, 72, 199, 69, 168, 233, 101, 34, 24, 72, 199, 69, 160, 51, 8, 0, 0, 72, 
    199, 69, 152, 171, 10, 0, 0, 72, 199, 69, 144, 173, 170, 141, 0, 72, 139, 69, 
    248, 72, 15, 175, 69, 240, 72, 137, 69, 136, 72, 139, 69, 232, 72, 15, 175, 69,
    224, 72, 15, 175, 69, 216, 72, 15, 175, 69, 208, 72, 15, 175, 69, 200, 72, 137, 
    69, 128, 72, 139, 69, 192, 72, 15, 175, 69, 184, 72, 15, 175, 69, 176, 72, 15, 
    175, 69, 168, 72, 137, 133, 120, 255, 255, 255, 72, 139, 69, 160, 72, 15, 175, 
    69, 152, 72, 15, 175, 69, 144, 72, 137, 133, 112, 255, 255, 255, 184, 0, 0, 0, 
    0, 201]
res =  ''.join('%02x'%c for c in l)
print(res)

res is the string representation of the hex bytes. Using Binary Ninja, you can create a new file and write these in… or just change the original python script above to write the literal bytes to a file by adding this snippet:

with open("a.bin", "wb") as f:
  f.write(bytes.fromhex(res))

Then open a.bin with Binary Ninja. p creates a function at the address. I experimented with a lot of the options, but found that x86-64 mac gave the best results (maybe because I’m running this on a Mac?). Other options, like x86 y or z linux stopped the function at byte 44 instead of e2.

Anyways, creating the function give the following:

babymult1

Displaying the hex strings as character constants (r) gives us (what I assmume) parts of the flag.

babymult2

Resuling in flag: flag{sup3r_v4l1d_pr0gr4m}

Interestingly, I tried doing this in Ghidra as well. It’s possible, but might require some extra fanangling. The decompiler show “bad opcodes”. This might be because there’s no c3 or ret call. Additionally, it’s not as simple to display the hex strings as character constants :P.

ezbreezy

This challenge gave a binary called app.

I started by running strings on it.

...
not_even_real.txt
Just another innocent application, nothing to see here!
;*3$"
GCC: (Arch Linux 9.3.0-1) 9.3.0
GCC: (GNU) 10.1.0
.shstrtab
.interp
.note.gnu.build-id
.init
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.got
.init_array
.fini_array
.dynamic
.got.plt
.data
.comment
.bss
.aj1ishudgqis

I found two things interesting.

  • not_even_real.txt
    • A file name. Maybe this is supposed to exist? Do we have to write the flag in here and have it checked by app?
  • .aj1ishudgqis
    • A weird (probably because after all the .xs) section.

not_even_real.txt

I checked the main function of app in Binary Ninja. The main function does nothing but call sub_1179().

I checked sub_1179() and saw that nothing really happens with not_even_real.txt except it reading it if it exists.

So, I went and checked out the odd section.

.aj1ishudgqis

I checked out the .aj1ishudgqis section. It turned out that this section is unused and contains a function. I looked at the function and it looked like it creates a string. So, I wrote down (by hand) each byte that waas being written.

0x8e,
0x94,
0x89,
0x8f,
0xa3,
0x9d,
0x87,
0x90,
0x5c,
0x9e,
0x5b,
0x87,
0x9a,
0x5b,
0x8b,
0x58,
0x9e,
0x5b,
0x9a,
0x5b,
0x8c,
0x87,
0x95,
0x5b,
0xa5

Cool, but when converted to ASCII, this is gibberish.

I tried a lot of different things until I got it. And I don’t know if took forever because I’m bad or because the solution is a little guessy.

We know that the flag is of the form flag{xxx}. This means that the first five bytes of the flag will be 66 6c 61 67 7b. However, we have 8e 94 89 8f a3. Apparently, flag{ with element-wise addition of 0x28 gives us what we have in the program. This means that we need to subtract 0x28 from our list, and we’ll get the correct bytes.

Doing so gives us:

66 6c 61 67 7b 75 5f 68 34 76 33 5f 72 33 63 30 76 33 72 33 64 5f 6d 33 7d


flag{u_h4v3_r3c0v3r3d_m3}

Cool.