Continuing with the challenges in Chapter 5, Practical Binary Analysis from Dennis Andriesse. Goal is to find a flag that would let us to open the next challenge while techniques being explained so far into the book are applied.

All levels start with a file, and in this case it’s another executable file that we have for level file. Execute the challenge:

binary@binary-VirtualBox:~/code/chapter5/level5$ ./lvl5

nothing to see here

That was not that useful no? Using previous level flag and getting some hints this is what we see:

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 656cf8aecb76113a4dece1688c61d0e7 -h

Secrets hidden in code unused

The method of redirection is the key

Static rather than dynamically

Now we know that most of the binary analysis we’ll have to make is static, and that some how we’ll have to change program execution to give us the flag. Let’s see what else can we find.

binary@binary-VirtualBox:~/code/chapter5/level5$ strings lvl5 | more

/lib64/ld-linux-x86-64.so.2

libc.so.6

__printf_chk

puts

__stack_chk_fail

__libc_start_main

__gmon_start__

GLIBC_2.3.4

GLIBC_2.4

GLIBC_2.2.5

UH-H

D$(1

Eev4A6ufH

3peH

gqfH

T$ H

L$(dH3

AWAVA

AUATL

[]A\A]A^A_

key = 0x%08x

decrypted flag = %s

nothing to see here

;*3$"

...

Running strings on the program shows that there are two of them that we might be interested and refereed to the key, and the flag. Now we’ll have to figure out how to get to them.

We can trace program execution as is, and figure out if we have any helpful information:

binary@binary-VirtualBox:~/code/chapter5/level5$ ltrace -i ./lvl5

[0x400549] __libc_start_main(0x400500, 1, 0x7fff14981e98, 0x4006f0 <unfinished ...>

[0x40050e] puts("nothing to see here"nothing to see here

) = 20

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

binary@binary-VirtualBox:~/code/chapter5/level5$

After using objdump to get the full dump of the binary we can take a look, what’s being executed it’s is:

0000000000400500 <.text>:

400500: 48 83 ec 08 sub rsp,0x8

400504: bf 97 07 40 00 mov edi,0x400797

400509: e8 a2 ff ff ff call 4004b0 <puts@plt>

40050e: b8 01 00 00 00 mov eax,0x1

400513: 48 83 c4 08 add rsp,0x8

400517: c3 ret

400518: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0]

40051f: 00

400520: 31 ed xor ebp,ebp

400522: 49 89 d1 mov r9,rdx

400525: 5e pop rsi

400526: 48 89 e2 mov rdx,rsp

400529: 48 83 e4 f0 and rsp,0xfffffffffffffff0

40052d: 50 push rax

40052e: 54 push rsp

40052f: 49 c7 c0 60 07 40 00 mov r8,0x400760

400536: 48 c7 c1 f0 06 40 00 mov rcx,0x4006f0

40053d: 48 c7 c7 00 05 40 00 mov rdi,0x400500

400544: e8 87 ff ff ff call 4004d0 <__libc_start_main@plt>

400549: f4 hlt

The block of code that starts at 0x4500 it’s pretty simple, prints out the string we’re seeing on the string. There are big pieces of unused code in .text, so we’ll have to read a bit through it.

We know that the challenge will use the strings we got before when printing, and maybe looking into any functions it might be using for printing that content would be useful for us. Let’s take a look at what other information we can collect.

binary@binary-VirtualBox:~/code/chapter5/level5$ nm -D lvl5

w __gmon_start__

U __libc_start_main

U __printf_chk

U puts

U __stack_chk_fail

One way that I did proceed on this exercise was looking at the strings being used, and the ones we found earlier. Firing up the binary with gdb, and looking stepping to the functions there is one thing I tried to figure out.

binary@binary-VirtualBox:~/code/chapter5/level5$ gdb lvl5

GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1

Copyright (C) 2016 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later ...

(gdb) set disassembly-flavor intel

(gdb) b *0x400544

Breakpoint 1 at 0x400544

(gdb) b *0x400500

Breakpoint 2 at 0x400500

(gdb) r

Starting program: /home/binary/code/chapter5/level5/lvl5

Breakpoint 1, 0x0000000000400544 in ?? ()

(gdb) c

Continuing.

Breakpoint 2, 0x0000000000400500 in ?? ()

(gdb) ni

0x0000000000400504 in ?? ()

(gdb) ni

(gdb) x/i $rip

=> 0x400504: mov edi,0x400797

(gdb) x/s $edi

0x400797: "nothing to see here"

(gdb) x/10s 0x400797 - 0x25

0x400772: "\002"

0x400774: "key = 0x%08x

"

0x400782: "decrypted flag = %s

"

0x400797: "nothing to see here"

0x4007ab: ""

0x4007ac: "\001\033\003;@"

0x4007b2: ""

0x4007b3: ""

0x4007b4: "\a"

0x4007b6: ""

At 0x400504 we see that the memory address to the string `nothing to see here` is being referenced, so looking a little backwards into that memory address we might be able to see the rest of them. Now we this in mind, we can look into the disassembly of the binary if the other two (0x400774 and 0x400782) are being referenced. Here is where I found them:

400620: 53 push rbx

400621: be 74 07 40 00 mov esi,0x400774

400626: bf 01 00 00 00 mov edi,0x1

40062b: 48 83 ec 30 sub rsp,0x30

40062f: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28

400636: 00 00

400638: 48 89 44 24 28 mov QWORD PTR [rsp+0x28],rax

40063d: 31 c0 xor eax,eax

40063f: 48 b8 10 60 21 33 15 movabs rax,0x6223331533216010

400646: 33 23 62

400649: c6 44 24 20 00 mov BYTE PTR [rsp+0x20],0x0

40064e: 48 89 04 24 mov QWORD PTR [rsp],rax

400652: 48 b8 45 65 76 34 41 movabs rax,0x6675364134766545

400659: 36 75 66

40065c: 48 89 44 24 08 mov QWORD PTR [rsp+0x8],rax

400661: 48 b8 17 67 75 64 10 movabs rax,0x6570331064756717

400668: 33 70 65

40066b: 48 89 44 24 10 mov QWORD PTR [rsp+0x10],rax

400670: 48 b8 18 35 76 62 11 movabs rax,0x6671671162763518

400677: 67 71 66

40067a: 48 89 44 24 18 mov QWORD PTR [rsp+0x18],rax

40067f: 8b 1c 25 40 05 40 00 mov ebx,DWORD PTR ds:0x400540

400686: 31 c0 xor eax,eax

400688: 89 da mov edx,ebx

40068a: e8 51 fe ff ff call 4004e0 <__printf_chk@plt>

40068f: 48 8d 54 24 20 lea rdx,[rsp+0x20]

400694: 48 89 e0 mov rax,rsp

400697: 66 0f 1f 84 00 00 00 nop WORD PTR [rax+rax*1+0x0]

40069e: 00 00

4006a0: 31 18 xor DWORD PTR [rax],ebx

4006a2: 48 83 c0 04 add rax,0x4

4006a6: 48 39 d0 cmp rax,rdx

4006a9: 75 f5 jne 4006a0 <__printf_chk@plt+0x1c0>

4006ab: 31 c0 xor eax,eax

4006ad: 48 89 e2 mov rdx,rsp

4006b0: be 82 07 40 00 mov esi,0x400782

4006b5: bf 01 00 00 00 mov edi,0x1

4006ba: e8 21 fe ff ff call 4004e0 <__printf_chk@plt>

4006bf: 31 c0 xor eax,eax

4006c1: 48 8b 4c 24 28 mov rcx,QWORD PTR [rsp+0x28]

4006c6: 64 48 33 0c 25 28 00 xor rcx,QWORD PTR fs:0x28

4006cd: 00 00

If we set any breakpoint in this section of code it would never be executed, so we need to find a way to redirect the execution flow here. At 0x40053d we see that the start of the function is being passed to __libc_start_main, so we can replace that with the address we want:

(gdb) r

Starting program: /home/binary/code/chapter5/level5/lvl5 Breakpoint 1, 0x0000000000400544 in ?? ()

(gdb) x/10i $rip - 0x15

0x40052f: mov r8,0x400760

0x400536: mov rcx,0x4006f0

0x40053d: mov rdi,0x400500

=> 0x400544: call 0x4004d0 <__libc_start_main@plt>

0x400549: hlt

0x40054a: nop WORD PTR [rax+rax*1+0x0]

0x400550: mov eax,0x60104f

0x400555: push rbp

0x400556: sub rax,0x601048

0x40055c: cmp rax,0xe

(gdb) set $rdi = 0x400620

(gdb) x/10i $rip

=> 0x400620: push rbx

0x400621: mov esi,0x400774

0x400626: mov edi,0x1

0x40062b: sub rsp,0x30

0x40062f: mov rax,QWORD PTR fs:0x28

0x400638: mov QWORD PTR [rsp+0x28],rax

0x40063d: xor eax,eax

0x40063f: movabs rax,0x6223331533216010

0x400649: mov BYTE PTR [rsp+0x20],0x0

0x40064e: mov QWORD PTR [rsp],rax

(gdb) c

Continuing.

key = 0x00400500

decrypted flag = ea36cbE`64A35fb5d60e06bb1f

[Inferior 1 (process 29664) exited normally]

Now it does seems we’re getting somewhere, but still the flag does not seems valid, that could mean we have an incorrect key. A deeper look into the function we’re not executing could int us where the flag seems to be decoded and how they key is actually used.

400694: 48 89 e0 mov rax,rsp

400697: 66 0f 1f 84 00 00 00 nop WORD PTR [rax+rax*1+0x0]

40069e: 00 00

4006a0: 31 18 xor DWORD PTR [rax],ebx

4006a2: 48 83 c0 04 add rax,0x4

4006a6: 48 39 d0 cmp rax,rdx

4006a9: 75 f5 jne 4006a0 <__printf_chk@plt+0x1c0>

At 0x400694 the encoded flag pointed by rax and then there is a loop xoring each byte with the content of ebx. The value that’s being stored in ebx is 0x48, which is loaded at 0x40067f. It points to 0x00400500

One of the hints is that The method of redirection is the key , so if we wanted to point the execution of the binary to the address that would print the key, we could try to rewrite that value and patch the binary as we’ve done before.

We can use xxd to dump the challenge contents and then look for the sequence of bytes we need to replace 48 c7 c7 00 05 40 00 :

00000500: 4883 ec08 bf97 0740 00e8 a2ff ffff b801 H......@........

00000510: 0000 0048 83c4 08c3 0f1f 8400 0000 0000 ...H............

00000520: 31ed 4989 d15e 4889 e248 83e4 f050 5449 1.I..^H..H...PTI

00000530: c7c0 6007 4000 48c7 c1f0 0640 0048 c7c7 ..`.@.H....@.H..

00000540: 0005 4000 e887 ffff fff4 660f 1f44 0000 ..@.......f..D..

00000550: b84f 1060 0055 482d 4810 6000 4883 f80e .O.`.UH-H.`.H...

00000560: 4889 e576 1bb8 0000 0000 4885 c074 115d H..v......H..t.]

We correct the address, patch the binary and now we can validate our patch with object dump, it should looks similar

40052f: 49 c7 c0 60 07 40 00 mov r8,0x400760

400536: 48 c7 c1 f0 06 40 00 mov rcx,0x4006f0

40053d: 48 c7 c7 20 06 40 00 mov rdi,0x400620

400544: e8 87 ff ff ff call 4004d0 <__libc_start_main@plt>

For confirming this value we can use gdb again:

binary@binary-VirtualBox:~/code/chapter5/level5$ gdb fixed



(gdb) set disassembly-flavor intel

(gdb) b *0x4006a0

Breakpoint 1 at 0x4006a0

(gdb) r

Starting program: /home/binary/code/chapter5/level5/fixed

key = 0x00400620 Breakpoint 1, 0x00000000004006a0 in ?? ()

(gdb) x/10i $rip

=> 0x4006a0: xor DWORD PTR [rax],ebx

0x4006a2: add rax,0x4

0x4006a6: cmp rax,rdx

0x4006a9: jne 0x4006a0

0x4006ab: xor eax,eax

0x4006ad: mov rdx,rsp

0x4006b0: mov esi,0x400782

0x4006b5: mov edi,0x1

0x4006ba: call 0x4004e0 <__printf_chk@plt>

0x4006bf: xor eax,eax

(gdb) x/x $ebx

0x400620: 0x53

(gdb) del breakpoints 1

(gdb) c

Continuing.

decrypted flag = 0fa355cbec64a05f7a5d050e836b1a1f

[Inferior 1 (process 29928) exited normally]

By looking at this we can see that the xor value is the byte located at 0x400620: 400620: 53 push rbx . Now we pass the flag to unlock the next level:

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

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

| Level 5 completed, unlocked lvl6 |

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

Run oracle with -h to show a hint

And now we can move along to the next level.