6

I have been doing a number of CTF activities when I came across something I’m unfamiliar with. As I am somewhat new to exploitation and reverse engineering, this was unfamiliar to me.

I have 7 pieces of an ELF binary file. I’ve been using readelf to get a better idea of where to put each piece. But I’m still unable to glean the order except for piece 4 contains the header and piece 6 contains the section names.

I’ve been using ‘cat’ to glue the pieces together then run them through readelf and gdb. Is there an easier way that I’m just missing?

For reference the particular example can be found on 0x0539.net -> Fangorn Forest -> Fragmented. I don’t want the answer. What I’m looking for is someone to point me in the right direction.

  • what about creating a small python script that will try the combinations of the remaining parts and checking if the can be interpreted as a valid ELF file? Probably some of the combinations will result in an error and remaining ones could be inspected manually. – Paweł Łukasik Dec 23 '19 at 21:22
2

Here is the flag:

FLAG{1ts_l1k3_4_m0r3_fun_puzz1e_m0m}

Notes:

  • readelf -SW was used to look at 2 small baseline binaries: a simple "hello world"-style ELF binary and the cat utility. This provided a frame of reference for how GCC organizes information in a binary.
  • Hex dumps of the fragments were then compared to a hex dump of these 2 programs.
  • Areas of particular importance were certain sections and their boundaries, such as between .plt, .text, and .rodata. Sections containing strings provided helpful reference points, certain sections contained table-like structures with repeating patters, and object code could also be identified just from looking at the dump. The offsets in the hex dumps could be cross referenced with the information output by readelf -SW to match locations in the dumps with the different sections.
  • After the first and last piece had been identified, they were analyzed further to determine which sections they contained. As you have pointed out, frag_4 contains the ELF header and frag_6 contains the section header string table. However, these 2 pieces actually contain much more than just this information.
  • frag_3 contains the procedure linkage table and the beginning of the .text section
  • pieces 2, 3, 5 and 7 consist of object code
  • frag_5 looked like it contained the end of the .text section.
  • at this point, the order looked like this: 4, 1, {2, 3, 7 - order could not be deduced from looking at hex dumps}, 5, 6
  • pieces 2, 3, and 7 were disassembled using Capstone using this script:
#!/usr/bin/python3

from capstone import *
import sys


md = Cs(CS_ARCH_X86, CS_MODE_64)

def disassemble(buf):
    for i in md.disasm(buf, 0x4004e0):
        print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))


def main():
    file_buf = []

    for arg in sys.argv:
        file_buf.append(arg)

    buf = []
    for file in file_buf[1:]:
        with open(file, 'rb') as f:
            buf = f.read()
            print(f.name + '\n')
            disassemble(buf)
            print('\n\n')

if __name__ == '__main__':
    main()
  • After viewing the disassembly, an educated guess was made: 3, 2, 7
  • Then the file was rebuilt:
#!/usr/bin/python3

file_names = ['frag_4', 'frag_1', 'frag_3', 'frag_2', 'frag_7', 'frag_5', 'frag_6']

bytes = b''
for file in file_names:
    with open(file, 'rb') as f:
        buf = f.read()
        bytes += buf

with open('reassembled.elf', 'wb') as f:
    f.write(bytes)

Resulting file:

$ readelf -SW reassembled.elf 
There are 31 section headers, starting at offset 0x1ac8:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        0000000000400238 000238 00001c 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            0000000000400254 000254 000020 00   A  0   0  4
  [ 3] .note.gnu.build-id NOTE            0000000000400274 000274 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        0000000000400298 000298 00001c 00   A  5   0  8
  [ 5] .dynsym           DYNSYM          00000000004002b8 0002b8 000090 18   A  6   1  8
  [ 6] .dynstr           STRTAB          0000000000400348 000348 00005f 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          00000000004003a8 0003a8 00000c 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         00000000004003b8 0003b8 000030 00   A  6   1  8
  [ 9] .rela.dyn         RELA            00000000004003e8 0003e8 000018 18   A  5   0  8
  [10] .rela.plt         RELA            0000000000400400 000400 000060 18  AI  5  24  8
  [11] .init             PROGBITS        0000000000400460 000460 00001a 00  AX  0   0  4
  [12] .plt              PROGBITS        0000000000400480 000480 000050 10  AX  0   0 16
  [13] .plt.got          PROGBITS        00000000004004d0 0004d0 000008 00  AX  0   0  8
  [14] .text             PROGBITS        00000000004004e0 0004e0 0003c2 00  AX  0   0 16
  [15] .fini             PROGBITS        00000000004008a4 0008a4 000009 00  AX  0   0  4
  [16] .rodata           PROGBITS        00000000004008b0 0008b0 00000f 00   A  0   0  4
  [17] .eh_frame_hdr     PROGBITS        00000000004008c0 0008c0 00004c 00   A  0   0  4
  [18] .eh_frame         PROGBITS        0000000000400910 000910 000154 00   A  0   0  8
  [19] .init_array       INIT_ARRAY      0000000000600e10 000e10 000008 00  WA  0   0  8
  [20] .fini_array       FINI_ARRAY      0000000000600e18 000e18 000008 00  WA  0   0  8
  [21] .jcr              PROGBITS        0000000000600e20 000e20 000008 00  WA  0   0  8
  [22] .dynamic          DYNAMIC         0000000000600e28 000e28 0001d0 10  WA  6   0  8
  [23] .got              PROGBITS        0000000000600ff8 000ff8 000008 08  WA  0   0  8
  [24] .got.plt          PROGBITS        0000000000601000 001000 000038 08  WA  0   0  8
  [25] .data             PROGBITS        0000000000601038 001038 000010 00  WA  0   0  8
  [26] .bss              NOBITS          0000000000601048 001048 000008 00  WA  0   0  1
  [27] .comment          PROGBITS        0000000000000000 001048 000035 01  MS  0   0  1
  [28] .shstrtab         STRTAB          0000000000000000 0019b8 00010c 00      0   0  1
  [29] .symtab           SYMTAB          0000000000000000 001080 0006c0 18     30  47  8
  [30] .strtab           STRTAB          0000000000000000 001740 000278 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

Since the symbol table and section header table were intact, objdump -dj .text reassembled.elf was used to compare disassembly of the code in the file to that of cat to check for consistency. There were some differences, but it turned out not to matter, as the flag was recoverable even though the program segfaults:

$ ./reassembled.elf 
��Ϛ^FLAG{1ts_l1k3_4_m0r3_fun_puzz1e_m0m}
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)
| improve this answer | |
  • I see where I went wrong, thanks! – t3nt0nbr1ck Dec 30 '19 at 1:52
  • @t3nt0nbr1ck you are welcome – julian Dec 30 '19 at 1:54
  • I wouldn't publish the flag :) It's kinda cheating, if they want the flag they should do your step themselves IMO. – macro_controller Dec 30 '19 at 8:40
  • @macr0controller I agree. – t3nt0nbr1ck Jan 2 at 17:08
1

It may be beneficial to study what the flags in the header actually mean. This will tell you useful information such as, where "things" are located and the size of said "things". Plus you would gain a better understanding of the ELF file format!

After that, you could probably automate the process with a python script, looking for key flags and using them to piece it together.

| improve this answer | |
  • So, I tried both methods, and got literally no where. I was able to sort the resulting files into only ones with the elf header (720), then I wrote a shell script to attempt to run each file, which failed because I keep getting access denied. tried file, which did confirm that almost all of them read as valid executables. when I try an objdump I just run into errors on every file, stating that there are references that go beyond the file size. I'm sort of still lost. Any recommendations? – t3nt0nbr1ck Dec 29 '19 at 21:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.