Continuing with the exercises from Chapter 5, from Dennis book, Practical Binary Analysis, now let’s take a look at Level 3. This binary was obtained after finding the flag on Level 2.

In this post, we’ll walk through lvl3.

We start where we left of last time, and trying to get an idea of what we’ll need to do:

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 034fc4f6a536f2bf74f8d6d3816cdf88

+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+

| Level 2 completed, unlocked lvl3 |

+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+

Run oracle with -h to show a hint

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 034fc4f6a536f2bf74f8d6d3816cdf88 -h

Fix four broken things

Apparently the binary we got it’s broken, and our job is to change a few things to get the flag. Let’s start by trying to execute it:

binary@binary-VirtualBox:~/code/chapter5/level3$ ./lvl3

-bash: ./lvl3: cannot execute binary file: Exec format error

We can’t. Now let’s start looking at the file and figuring out what could have gone wrong by peeking the file with readelf:

binary@binary-VirtualBox:~/code/chapter5/level3$ readelf -h -n lvl3

ELF Header:

Magic: 7f 45 4c 46 02 01 01 0b 00 00 00 00 00 00 00 00

Class: ELF64

Data: 2's complement, little endian

Version: 1 (current)

OS/ABI: Novell - Modesto

ABI Version: 0

Type: EXEC (Executable file)

Machine: Motorola Coldfire

Version: 0x1

Entry point address: 0x4005d0

Start of program headers: 4022250974 (bytes into file)

Start of section headers: 4480 (bytes into file)

Flags: 0x0

Size of this header: 64 (bytes)

Size of program headers: 56 (bytes)

Number of program headers: 9

Size of section headers: 64 (bytes)

Number of section headers: 29

Section header string table index: 28

readelf: Error: Reading 0x1f8 bytes extends past end of file for program headers Displaying notes found at file offset 0x00000254 with length 0x00000020:

Owner Data size Description

GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)

OS: Linux, ABI: 2.6.32 Displaying notes found at file offset 0x00000274 with length 0x00000024:

Owner Data size Description

GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)

Build ID: b6c0e8d914c6433e661b2cac794108671bdcaa06

A thorough look at the file headers and we can identify a few things that seem off, in my case I started comparing the information I had with other binaries and then tried to figure out what these values meant.

Recalling from Chapter 2, or basically from knowing what you can expect within the ELF Executable Header a look at the e_ident array has the first value we need to fix.

You can peek at the elf.h file and take a look at the struct, I’ve extracted this one from /usr/include/elf.h from the VM provided for the exercises in the book:

typedef struct

{

unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */

Elf32_Half e_type; /* Object file type */

Elf32_Half e_machine; /* Architecture */

Elf32_Word e_version; /* Object file version */

Elf32_Addr e_entry; /* Entry point virtual address */

Elf32_Off e_phoff; /* Program header table file offset */

Elf32_Off e_shoff; /* Section header table file offset */

Elf32_Word e_flags; /* Processor-specific flags */

Elf32_Half e_ehsize; /* ELF header size in bytes */

Elf32_Half e_phentsize; /* Program header table entry size */

Elf32_Half e_phnum; /* Program header table entry count */

Elf32_Half e_shentsize; /* Section header table entry size */

Elf32_Half e_shnum; /* Section header table entry count */

Elf32_Half e_shstrndx; /* Section header string table index */

} Elf32_Ehdr;

First we need to understand where this flag is and then what the correct value would be:

Magic: 7f 45 4c 46 02 01 01 0b 00 00 00 00 00 00 00 00

Class: ELF64

Data: 2's complement, little endian

Version: 1 (current)

OS/ABI: Novell - Modesto

Parsing the e_ident array, we know the first 4 bytes correspond to the “magic value”, and then it’s followed others (won’t directly quote what’s on the book). The one we’re interested in is the EI_OSABI, looking into the elf.h file again we can find:

#define EI_OSABI 7 /* OS ABI identification */

#define ELFOSABI_NONE 0 /* UNIX System V ABI */

#define ELFOSABI_SYSV 0 /* Alias. */

#define ELFOSABI_HPUX 1 /* HP-UX */

#define ELFOSABI_NETBSD 2 /* NetBSD. */

#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */

#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */

#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */

#define ELFOSABI_AIX 7 /* IBM AIX. */

#define ELFOSABI_IRIX 8 /* SGI Irix. */

#define ELFOSABI_FREEBSD 9 /* FreeBSD. */

#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */

#define ELFOSABI_MODESTO 11 /* Novell Modesto. */

#define ELFOSABI_OPENBSD 12 /* OpenBSD. */

#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */

#define ELFOSABI_ARM 97 /* ARM */

#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */

Basically, the first thing we need to do is replace 0xb (11 Novell Modesto) with 0x0 (UNIX System V).

One found, three to go.

The second value is for the Machine, this matches with the e_machine field, that basically specifies the architecture.

Value for Motorola Coldfire is 0x34, and we want this executable to be ran on an AMD X86–64 architecture (0x3e):

#define EM_NONE 0 /* No machine */

#define EM_M32 1 /* AT&T WE 32100 */

#define EM_SPARC 2 /* SUN SPARC */

#define EM_386 3 /* Intel 80386 */

#define EM_68K 4 /* Motorola m68k family */

#define EM_88K 5 /* Motorola m88k family */

#define EM_860 7 /* Intel 80860 */

#define EM_MIPS 8 /* MIPS R3000 big-endian */

#define EM_S370 9 /* IBM System/370 */

#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ #define EM_PARISC 15 /* HPPA */

#define EM_VPP500 17 /* Fujitsu VPP500 */

#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */

#define EM_960 19 /* Intel 80960 */

#define EM_PPC 20 /* PowerPC */

#define EM_PPC64 21 /* PowerPC 64-bit */

#define EM_S390 22 /* IBM S390 */ #define EM_V800 36 /* NEC V800 series */

#define EM_FR20 37 /* Fujitsu FR20 */

#define EM_RH32 38 /* TRW RH-32 */

#define EM_RCE 39 /* Motorola RCE */

#define EM_ARM 40 /* ARM */

#define EM_FAKE_ALPHA 41 /* Digital Alpha */

#define EM_SH 42 /* Hitachi SH */

#define EM_SPARCV9 43 /* SPARC v9 64-bit */

#define EM_TRICORE 44 /* Siemens Tricore */

#define EM_ARC 45 /* Argonaut RISC Core */

#define EM_H8_300 46 /* Hitachi H8/300 */

#define EM_H8_300H 47 /* Hitachi H8/300H */

#define EM_H8S 48 /* Hitachi H8S */

#define EM_H8_500 49 /* Hitachi H8/500 */

#define EM_IA_64 50 /* Intel Merced */

#define EM_MIPS_X 51 /* Stanford MIPS-X */

#define EM_COLDFIRE 52 /* Motorola Coldfire */

#define EM_68HC12 53 /* Motorola M68HC12 */

#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/

#define EM_PCP 55 /* Siemens PCP */

#define EM_NCPU 56 /* Sony nCPU embeeded RISC */

#define EM_NDR1 57 /* Denso NDR1 microprocessor */

#define EM_STARCORE 58 /* Motorola Start*Core processor */

#define EM_ME16 59 /* Toyota ME16 processor */

#define EM_ST100 60 /* STMicroelectronic ST100 processor */

#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/

#define EM_X86_64 62 /* AMD x86-64 architecture */

#define EM_PDSP 63 /* Sony DSP Processor */

Two found, two to go.

The third value we’ve identified it’s the program headers table address. We know that we need to look into the value for e_shoff, which is currently 4022250974:

binary@binary-VirtualBox:~/code/chapter5/level3$ xxd -l 80 lvl3

00000000: 7f45 4c46 0201 010b 0000 0000 0000 0000 .ELF............

00000010: 0200 3400 0100 0000 d005 4000 0000 0000 ..4.......@.....

00000020: dead beef 0000 0000 8011 0000 0000 0000 ................

00000030: 0000 0000 4000 3800 0900 4000 1d00 1c00 ....@.8...@.....

00000040: 0600 0000 0500 0000 4000 0000 0000 0000 ........@.......

Looking at the hexdump of the file with xxd we can see this matches with deadbeef, funny no?

A reminder of the ELF File Format we know that the Program Headers Table comes after the ELF Headers. And we got the size for one, so we can know the address for the second.

Three found, one to go.

Up to this point I started to go into a rabbit hole, and as part of that I did “patched” the challenge with the values I already knew. For doing did I followed the following steps:

Use xxd to get an hex dump of the file Patch the values I’ve discovered Used xxd -r for patching the binary

Here is a dump of the before and after:

binary@binary-VirtualBox:~/code/chapter5/level3$ xxd -l 60 lvl3

00000000: 7f45 4c46 0201 010b 0000 0000 0000 0000 .ELF............

00000010: 0200 3400 0100 0000 d005 4000 0000 0000 ..4.......@.....

00000020: dead beef 0000 0000 8011 0000 0000 0000 ................

00000030: 0000 0000 4000 3800 0900 4000 ....@.8...@.

binary@binary-VirtualBox:~/code/chapter5/level3$ xxd -l 60 patch

00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............

00000010: 0200 3e00 0100 0000 d005 4000 0000 0000 ..>.......@.....

00000020: 4000 0000 0000 0000 8011 0000 0000 0000 @...............

00000030: 0000 0000 4000 3800 0900 4000 ....@.8...@.

Once I got this, I tried to see if the binary would run:

binary@binary-VirtualBox:~/code/chapter5/level3$ ./patch

0e2ada7381d04d4d2ed31be82b121aa3 ./patch

binary@binary-VirtualBox:~/code/chapter5/level3$ ltrace -i ./patch

[0x4005f9] __libc_start_main(0x400550, 1, 0x7fffa3429638, 0x4006d0 <unfinished ...>

[0x40058c] __strcat_chk(0x7fffa3429130, 0x400754, 1024, 0) = 0x7fffa3429130

[0x4005a2] __strncat_chk(0x7fffa3429130, 0x7fffa342b675, 1016, 1024) = 0x7fffa3429130

[0x4005aa] system("md5sum ./patch"0e2ada7381d04d4d2ed31be82b121aa3 ./patch

<no return ...>

[0x7f87b2c39730] --- SIGCHLD (Child exited) ---

[0x4005aa] <... system resumed> ) = 0

[0xffffffffffffffff] +++ exited (status 0) +++

After running my patched challenged I did got a flag, but it did not work:

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 0e2ada7381d04d4d2ed31be82b121aa3

Invalid flag: 0e2ada7381d04d4d2ed31be82b121aa3

This makes sense as I know I’ve changed three out of four, but now the binary runs and I know it’s calculating it’s own hash so there is something I’m missing here.

After some looking around and trying to disassemble the binary I’ve noticed that something was missing:

binary@binary-VirtualBox:~/code/chapter5/level3$ objdump -M intel -d patch | grep "Disassembly of section"

Disassembly of section .init:

Disassembly of section .plt:

Disassembly of section .plt.got:

Disassembly of section .fini:

Section .text was missing, so tried to figure out why this was happening:

binary@binary-VirtualBox:~/code/chapter5/level3$ readelf -S -W patch

There are 29 section headers, starting at offset 0x1180: 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 0000a8 18 A 6 1 8

[ 6] .dynstr STRTAB 0000000000400360 000360 000081 00 A 0 0 1

[ 7] .gnu.version VERSYM 00000000004003e2 0003e2 00000e 02 A 5 0 2

[ 8] .gnu.version_r VERNEED 00000000004003f0 0003f0 000040 00 A 6 1 8

[ 9] .rela.dyn RELA 0000000000400430 000430 000018 18 A 5 0 8

[10] .rela.plt RELA 0000000000400448 000448 000078 18 AI 5 24 8

[11] .init PROGBITS 00000000004004c0 0004c0 00001a 00 AX 0 0 4

[12] .plt PROGBITS 00000000004004e0 0004e0 000060 10 AX 0 0 16

[13] .plt.got PROGBITS 0000000000400540 000540 000008 00 AX 0 0 8

[14] .text NOBITS 0000000000400550 000550 0001f2 00 AX 0 0 16

[15] .fini PROGBITS 0000000000400744 000744 000009 00 AX 0 0 4

[16] .rodata PROGBITS 0000000000400750 000750 00000c 00 A 0 0 4

[17] .eh_frame_hdr PROGBITS 000000000040075c 00075c 000034 00 A 0 0 4

[18] .eh_frame PROGBITS 0000000000400790 000790 000104 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 000040 08 WA 0 0 8

[25] .data PROGBITS 0000000000601040 001040 000010 00 WA 0 0 8

[26] .bss NOBITS 0000000000601050 001050 000008 00 WA 0 0 1

[27] .comment PROGBITS 0000000000000000 001050 000034 01 MS 0 0 1

[28] .shstrtab STRTAB 0000000000000000 001084 0000fc 00 0 0 1

Key to Flags:

W (write), A (alloc), X (execute), M (merge), S (strings), l (large)

I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)

O (extra OS processing required) o (OS specific), p (processor specific)

Looks like section .text is marked as NOBITS, once again, looking at the section header information, now we know why. This value is incorrect and now we need to patch it.

Finding the offset for this:

Section Headers offset: 0x1880 (4480 bytes)

Section .text number: 14

Offset to sh_type: 4 bytes

4480 + 14 * 64 + 4 = 5380

We know that at offset 0x1500 (if the numbers are right) we’ll find the .text section header, and looking at the struct in /user/include/elf.h we found:

typedef struct

{

Elf32_Word sh_name; /* Section name (string tbl index) */

Elf32_Word sh_type; /* Section type */

Elf32_Word sh_flags; /* Section flags */

Elf32_Addr sh_addr; /* Section virtual addr at execution */

Elf32_Off sh_offset; /* Section file offset */

Elf32_Word sh_size; /* Section size in bytes */

Elf32_Word sh_link; /* Link to another section */

Elf32_Word sh_info; /* Additional section information */

Elf32_Word sh_addralign; /* Section alignment */

Elf32_Word sh_entsize; /* Entry size if section holds table */

} Elf32_Shdr;

Now what we need to do is replace the sh_type value that it’s set to SH_NOBITS (0x8) to SHT_PROGBITS (0x1). Again, given the tools and tricks explained in the book, I’ve relied on xxd to patch the binary:

binary@binary-VirtualBox:~/code/chapter5/level3$ xxd -s 0x1500 -l 40 patch

00001500: 8d00 0000 0800 0000 0600 0000 0000 0000 ................

00001510: 5005 4000 0000 0000 5005 0000 0000 0000 P.@.....P.......

00001520: f201 0000 0000 0000 ........

binary@binary-VirtualBox:~/code/chapter5/level3$ xxd -s 0x1500 -l 40 patch2

00001500: 8d00 0000 0100 0000 0600 0000 0000 0000 ................

00001510: 5005 4000 0000 0000 5005 0000 0000 0000 P.@.....P.......

00001520: f201 0000 0000 0000 ........

With this last change, now we have four out of four and we can generate the flag, let’s try this time to see if it works:

binary@binary-VirtualBox:~/code/chapter5/level3$ ./patch2

3a5c381e40d2fffd95ba4452a0fb4a40 ./patch2

binary@binary-VirtualBox:~/code/chapter5/level3$ cd ..

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 3a5c381e40d2fffd95ba4452a0fb4a40

+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+

| Level 3 completed, unlocked lvl4 |

+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+

Run oracle with -h to show a hint

And now we’re off to level 5.