DefconCTF Quals - RE (ELF Crumble)

As is usual, I took way longer on this than I should have. Unfortunately, the challenges are down so I can’t provide a screenshot of the challenge. It said something along the lines of:

We made you a welcome banner but we dropped it and it shattered to pieces.

Good thing none of the instructions are lost! You can probably put it back together with some glue.

You’re given a file named “pieces”.

I download it, run $ file pieces and see that’s it’s a gzip file. I run $ gzip -d pieces to unzip it. Now it’s a tar file, so I run $ tar xvf broken, and we finally get to the files.

I get a folder called pieces out, with a file called broken and 8 files consecutively names fragment_#.dat.

Let’s run file on broken.

root@kali:~/Desktop# file broken
broken: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=4cd47d8a237a3139d1884b3ef52f6ed387c75772, not stripped

so it’s a 32-bit binary. I try running it only to get a no file or folder exists, assuming it’s because it’s broken. Let’s look at what the fragments have in them.

root@kali:~/Desktop# cat fragment_1.dat 
[]�U��������E��#�U�����‹E��
                               �U��Љʈ�E��}�~א��U��S��

Yup, can’t make sense of that. Let’s go look at broken in Ida.

DefconCTF Quals IDA View

Well, it literally opens right up to a bunch of pop eaxs, which are equivalent to 58 in hex and “x” in ascii. This is the case for all the relevant functions (namely, f1, f2, f3, recover_flag, and main). Since the prompt said the instructions are still around, they must be somewhere else.

At first, I thought they were hidden within the binary itself. I looked through the comments or any function that doesn’t look like it didn’t belong. Then it hit me. I had assumed the fragments were the flag that needed to be decoded by the broken program.

No, they were the instructions.

I did a $ hexdump -v -e ‘/1 “%02X”’ fragment_1.dat » out of all the fragment files into an output file, formatting them with -e so hexdump didn’t change their format and stop any repeating hex from being omitted with -v. This part is important if you’re using hexdump because hexdump makes the hex into little endian byte-length chunks, which flips their places for example, 5b 5d becomes 5d5b leading you to have the wrong code. I took the resulting hex to the handy dandy Online x86 Diassembler at https://defuse.ca/online-x86-assembler.htm.

64: 5b                      pop    ebx
65: 5d                      pop    ebp
66: c3                      ret
67: 55                      push   ebp
68: 89 e5                   mov    ebp,esp
6a: 83 ec 10                sub    esp,0x10
6d: e8 e0 01 00 00          call   0x252
72: 05 dc 18 00 00          add    eax,0x18dc
77: c7 45 fc 00 00 00 00    mov    DWORD PTR [ebp-0x4],0x0
....

looks like real instructions!

So from here, I waste my time because I have no respect for myself. I didn’t think I could possibly figure out how to manually find their positions in the code with 8 fragments having 4k permutations, so I wrote a brute forcer. The code is as follows:

import binascii
import itertools
import os

with open('broken', 'rb') as f:
	hexdata = binascii.hexlify(f.read())

with open('fragment_1.dat', 'rb') as f:
	f1 = binascii.hexlify(f.read())

with open('fragment_2.dat', 'rb') as f:
	f2 = binascii.hexlify(f.read())

with open('fragment_3.dat', 'rb') as f:
	f3 = binascii.hexlify(f.read())

with open('fragment_4.dat', 'rb') as f:
	f4 = binascii.hexlify(f.read())

with open('fragment_5.dat', 'rb') as f:
	f5 = binascii.hexlify(f.read())

with open('fragment_6.dat', 'rb') as f:
	f6 = binascii.hexlify(f.read())

with open('fragment_7.dat', 'rb') as f:
	f7 = binascii.hexlify(f.read())

with open('fragment_8.dat', 'rb') as f:
	f8 = binascii.hexlify(f.read())

files = [f1,f2,f3,f4,f5,f6,f7,f8]

# cut out the x's
# put the new combinations in the middle
# combine the beginning, new middle, and end, then save to file
# run the file
# loop for that each combination

# the beginning hex for the pop eax section
str1 = "5858"
# the ending hex for the pop eax section
str2 = "8b04"

# get the positions
strindex1 = hexdata.index(str1)
strindex2 = hexdata.index(str2)

# separate the beginning and end
beginning = hexdata[0:strindex1]
end = hexdata[strindex2:]

# iterate through permutations
for subset in itertools.permutations(files):
	with open("broken", "wb") as fout:
		fout.write(binascii.unhexlify(beginning) + binascii.unhexlify(''.join(subset)) + binascii.unhexlify(end))
	os.system("./broken" + " >> out")
	os.system("rm broken")

I run this, cat the output file, and nothing. NOTHING. Okay, you know what, time to go to sleep. The next day, I decided to cat out all the fragments to try to get a clue.

root@kali:~/Desktop# cat fragment_3.dat 
�ȋ@�E�e��E�1��E�Did �E�You �E�FinD�E� the�E� GlUf�E�e?�E��E���
                                                                 �E�P�T������E����E����E����E��E��ЉU��E����u��E����E����E�����
                                               QW�u��u��u�VRP������P

Is this really the fucking flag? I try submit “Did You FinD the GlUe?” in a couple of ways. False flag.

At this point, I started talking to a friend over Slack who was also doing the challenge, and he said “Well the 8th one starts with what main should start with, so that’s obviously the start for main,” (that was actually incorrect) which made me think, maybe I can do this manually.

The way I figured this out was just logical reasoning. I looked at the lengths, order, and generally how the function should look like in assembly to decide how to piece them together. The order of the functions were f1, f2, f3, recover_flag, and main, so instructions for each would also be in this order, and the beginning for one function could be in the middle the other’s fragment.

I copied and replaced the 58s with the hex from the three fragments on HxD. Make sure you right click and copy from the hex side of the screen, and then right click and “paste write” on the hex side, or else you can end up pasting the hex as ascii.

DefconCTF Quals HxD View

Then I checked it on Ida to see if it looks right. The easiest way to check if I had gotten the right combination was checking to see if the sp-analysis failed went away on Ida.

DefconCTF Quals Ida View Before

I followed this reasoning and some guessing and checking until I got the fragment order of 8, 7, 1, 5, 6, 2, 3, 4.

I saved the final file, and tried to run it. Still no file or folder exists, but this time I’m sure it’s supposed to run. Oh it’s a 32-bit file and I’m on a 64-bit Kali. I install the architecture for x32 ($ dpkg –add-architecture i386 $ apt-get update if you were wondering how), and run it.

root@kali:~/Desktop# ./broken 
welcOOOme

SUCCESS. Wait.. so why didn’t my brute forcer work?

Oh yeah, I hadn’t installed the 32-bit architecture. I run the brute forcer and cat the output file

root@kali:~/Desktop# python exploit.py
root@kali:~/Desktop# cat out 
welcOOOme

I’m not crying, you are.