0%

Binary-Analysis-CTF

Report for Practical Binary Analysis CTF

The game now is given.

However, we have some problems at first. The virtual machine given on the website is really old. It even couldn’t support the newest visual studio code ssh-server. So there is a method to have a nice editing experience to use command:

1
ssh -p 2222 binary@127.0.0.1 

After finish ssh connection, I really recommend everyone who want to play this game to use hexedit but not vim. Vim really takes me big trouble in the game.

And now, we could begin the game.

Level 2

First of all, there is a key given 84b34c124b2ba5ca224af8e33b077e9e.

1
2
3
4
5
binary@binary-VirtualBox:~/code/chapter5$ ./oracle 84b34c124b2ba5ca224af8e33b077e9e 
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Level 1 completed, unlocked lvl2 |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
Run oracle with -h to show a hint

It gives us a hint, it’s important.

1
2
binary@binary-VirtualBox:~/code/chapter5$ ./oracle 84b34c124b2ba5ca224af8e33b077e9e -h
Combine the parts

It seems we have no point to solve the problem. So we can first justify what it is.

1
2
binary@binary-VirtualBox:~/code/chapter5$ file ./lvl2
./lvl2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=457d7940f6a73d6505db1f022071ee7368b67ce9, stripped

OK, it’s a executable. We can run a few times to observe it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
74
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
a5
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
a5
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
74
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
f2
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
f2
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
4f
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
4f
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
f2
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
36
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
36
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
6c
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
6c
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
d6
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
df
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
df
binary@binary-VirtualBox:~/code/chapter5$ ./lvl2
d6

It puts some words repeatedly. We guess the key is an arrangement of these words.

1
2
3
4
5
6
7
8
binary@binary-VirtualBox:~/code/chapter5$ ltrace ./lvl2
__libc_start_main(0x400500, 1, 0x7fff185b57b8, 0x400640 <unfinished ...>
time(0) = 1764236346
srand(0x69281c3a, 0x7fff185b57b8, 0x7fff185b57c8, 0) = 0
rand(0x7f1246c6d620, 0x7fff185b569c, 0x7f1246c6d0a4, 0x7f1246c6d11c) = 0x23f62ce4
puts("a5"a5
) = 3
+++ exited (status 0) +++

In ltrace, we can see that the program seems like to get a rand number and get a words to puts. And when we use objdump, it seems we are close to the answer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0000000000400500 <.text>:
400500: 48 83 ec 08 sub $0x8,%rsp
400504: 31 ff xor %edi,%edi
400506: e8 c5 ff ff ff callq 4004d0 <time@plt>
40050b: 89 c7 mov %eax,%edi
40050d: e8 ae ff ff ff callq 4004c0 <srand@plt>
400512: e8 c9 ff ff ff callq 4004e0 <rand@plt>
400517: 99 cltd
400518: c1 ea 1c shr $0x1c,%edx
40051b: 01 d0 add %edx,%eax
40051d: 83 e0 0f and $0xf,%eax
400520: 29 d0 sub %edx,%eax
400522: 48 98 cltq
400524: 48 8b 3c c5 60 10 60 mov 0x601060(,%rax,8),%rdi
40052b: 00

We can see from 0x400517 to 0x400522, eax is expanded in edx. And then edx is shifted right by 28 bits. Then add edx and eax and only the last four digits are retained. The whole progress is to take the remainder of eax divided by 16. And in 0x400524, get a words from 0x601060 + 8 * rax to puts.

So we could just use gdb to get the key.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(gdb) b *0x400524
Breakpoint 1 at 0x400524
(gdb) x/16x 0x601060
0x601060: 0x004006c4 0x00000000 0x004006c7 0x00000000
0x601070: 0x004006ca 0x00000000 0x004006cd 0x00000000
0x601080: 0x004006d0 0x00000000 0x004006d3 0x00000000
0x601090: 0x004006d6 0x00000000 0x004006d9 0x00000000
(gdb) x/16s 0x4006c4
0x4006c4: "03"
0x4006c7: "4f"
0x4006ca: "c4"
0x4006cd: "f6"
0x4006d0: "a5"
0x4006d3: "36"
0x4006d6: "f2"
0x4006d9: "bf"
0x4006dc: "74"
0x4006df: "f8"
0x4006e2: "d6"
0x4006e5: "d3"
0x4006e8: "81"
0x4006eb: "6c"
0x4006ee: "df"
0x4006f1: "88"

Now we get the key 034fc4f6a536f2bf74f8d6d3816cdf88.

1
2
3
4
5
binary@binary-VirtualBox:~/code/chapter5$ ./oracle 034fc4f6a536f2bf74f8d6d3816cdf88   
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Level 2 completed, unlocked lvl3 |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
Run oracle with -h to show a hint

Level 3

1
```

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 034fc4f6a536f2bf74f8d6d3816cdf88 -h
Fix four broken things

1
The hint of level 3 is clear. Let's first see what lvl3 is.

binary@binary-VirtualBox:~/code/chapter5$ file lvl3
lvl3: ERROR: ELF 64-bit LSB executable, Motorola Coldfire, version 1 (Novell Modesto) error reading (Invalid argument)

1
It is obvious something wrong. First check its elf headers.

binary@binary-VirtualBox:~/code/chapter5$ readelf -h 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

1
2
3
4
5
First of all, the OS/ABI is obviously wrong, default linux should be Unix/System V. So "0b" should be corrected to "00". And also, the Machine is ridiculous. It should be X86-64, so we need to correct "34" to "3E".
And in the Hexedit view, there is a hint "deadbeef". Start of program headers should be 64, so we need to use "4000 0000" to replace it.
![屏幕截图_20251127_180303.png](https://boring-picture1.oss-cn-hangzhou.aliyuncs.com/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE_20251127_180303.png)

After we fix the 3 problems, it seems everything is right. But in fact, the hint remind us there are four problems. So let's take a shot on every section.

[14] .text NOBITS 0000000000400550 00000550
00000000000001f2 0000000000000000 AX 0 0 16

1
The problem is showed. ``.text`` is NOBITS. It should be PROGBITS. So we correct "8d00 0000 0800 0000" to "8d00 0000 0100 0000". And the position is 4480 + 64 * 13 = 5312. The hex mode is 0x14c0, so find "8d00 0000 0800 0000" around it and correct it. Then we could get the key:

binary@binary-VirtualBox:/code/chapter5$ ./lvl3
3a5c381e40d2fffd95ba4452a0fb4a40 ./lvl3
binary@binary-VirtualBox:
/code/chapter5$ ./oracle 3a5c381e40d2fffd95ba4452a0fb4a40
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Level 3 completed, unlocked lvl4 |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
Run oracle with -h to show a hint

1
2
3
4
By the way, **Don't use vim when you want to change a binary file**. Otherwise, it will take you 3 hours to find what mistakes you have made, and finally you'll find that it's vim who change your code without your permission.

## Level 4
As we see, there is a hint for level 4:

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 3a5c381e40d2fffd95ba4452a0fb4a40 -h
Watch closely while I run

1
2
It reminds us we should observe its running time.
So let's first run it:

binary@binary-VirtualBox:/code/chapter5$ ./lvl4
binary@binary-VirtualBox:
/code/chapter5$

1
We found nothing in it. So maybe we could use ltrace to see what libc function it called.

binary@binary-VirtualBox:~/code/chapter5$ ltrace ./lvl4
__libc_start_main(0x4004a0, 1, 0x7ffceec34fd8, 0x400650 <unfinished …>
setenv(“FLAG”, “656cf8aecb76113a4dece1688c61d0e7”…, 1) = 0
+++ exited (status 0) +++

1
Lucky! The answer is just in it.

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 656cf8aecb76113a4dece1688c61d0e7
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Level 4 completed, unlocked lvl5 |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
Run oracle with -h to show a hint

1
2
3

## Level 5
Now let's focus on level 5.

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 656cf8aecb76113a4dece1688c61d0e7 -h
Secrets hidden in code unused
The method of redirection is the key
Static rather than dynamically

1
OK, the hint reminds us that we should find the code never used and do a redirection to get the key. So we can use objdump first.

0000000000400500 <.text>:
400500: 48 83 ec 08 sub $0x8,%rsp
400504: bf 97 07 40 00 mov $0x400797,%edi
400509: e8 a2 ff ff ff callq 4004b0 <puts@plt>
40050e: b8 01 00 00 00 mov $0x1,%eax
400513: 48 83 c4 08 add $0x8,%rsp
400517: c3 retq
400518: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40051f: 00
400520: 31 ed xor %ebp,%ebp
400522: 49 89 d1 mov %rdx,%r9
400525: 5e pop %rsi
400526: 48 89 e2 mov %rsp,%rdx
400529: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
40052d: 50 push %rax
40052e: 54 push %rsp
40052f: 49 c7 c0 60 07 40 00 mov $0x400760,%r8
400536: 48 c7 c1 f0 06 40 00 mov $0x4006f0,%rcx
40053d: 48 c7 c7 00 05 40 00 mov $0x400500,%rdi
400544: e8 87 ff ff ff callq 4004d0 <__libc_start_main@plt>
400549: f4 hlt
40054a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
400550: b8 4f 10 60 00 mov $0x60104f,%eax
400555: 55 push %rbp
400556: 48 2d 48 10 60 00 sub $0x601048,%rax
40055c: 48 83 f8 0e cmp $0xe,%rax
400560: 48 89 e5 mov %rsp,%rbp
400563: 76 1b jbe 400580 <__printf_chk@plt+0xa0>
400565: b8 00 00 00 00 mov $0x0,%eax
40056a: 48 85 c0 test %rax,%rax
40056d: 74 11 je 400580 <__printf_chk@plt+0xa0>
40056f: 5d pop %rbp
400570: bf 48 10 60 00 mov $0x601048,%edi
400575: ff e0 jmpq *%rax
400577: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
40057e: 00 00
400580: 5d pop %rbp
400581: c3 retq
400582: 0f 1f 40 00 nopl 0x0(%rax)
400586: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40058d: 00 00 00
400590: be 48 10 60 00 mov $0x601048,%esi
400595: 55 push %rbp
400596: 48 81 ee 48 10 60 00 sub $0x601048,%rsi
40059d: 48 c1 fe 03 sar $0x3,%rsi
4005a1: 48 89 e5 mov %rsp,%rbp
4005a4: 48 89 f0 mov %rsi,%rax
4005a7: 48 c1 e8 3f shr $0x3f,%rax
4005ab: 48 01 c6 add %rax,%rsi
4005ae: 48 d1 fe sar %rsi
4005b1: 74 15 je 4005c8 <__printf_chk@plt+0xe8>
4005b3: b8 00 00 00 00 mov $0x0,%eax
4005b8: 48 85 c0 test %rax,%rax
4005bb: 74 0b je 4005c8 <__printf_chk@plt+0xe8>
4005bd: 5d pop %rbp
4005be: bf 48 10 60 00 mov $0x601048,%edi
4005c3: ff e0 jmpq *%rax
4005c5: 0f 1f 00 nopl (%rax)
4005c8: 5d pop %rbp
4005c9: c3 retq
4005ca: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
4005d0: 80 3d 71 0a 20 00 00 cmpb $0x0,0x200a71(%rip) # 601048 <__printf_chk@plt+0x200b68>
4005d7: 75 11 jne 4005ea <__printf_chk@plt+0x10a>
4005d9: 55 push %rbp
4005da: 48 89 e5 mov %rsp,%rbp
4005dd: e8 6e ff ff ff callq 400550 <__printf_chk@plt+0x70>
4005e2: 5d pop %rbp
4005e3: c6 05 5e 0a 20 00 01 movb $0x1,0x200a5e(%rip) # 601048 <__printf_chk@plt+0x200b68>
4005ea: f3 c3 repz retq
4005ec: 0f 1f 40 00 nopl 0x0(%rax)
4005f0: bf 20 0e 60 00 mov $0x600e20,%edi
4005f5: 48 83 3f 00 cmpq $0x0,(%rdi)
4005f9: 75 05 jne 400600 <__printf_chk@plt+0x120>
4005fb: eb 93 jmp 400590 <__printf_chk@plt+0xb0>
4005fd: 0f 1f 00 nopl (%rax)
400600: b8 00 00 00 00 mov $0x0,%eax
400605: 48 85 c0 test %rax,%rax
400608: 74 f1 je 4005fb <__printf_chk@plt+0x11b>
40060a: 55 push %rbp
40060b: 48 89 e5 mov %rsp,%rbp
40060e: ff d0 callq *%rax
400610: 5d pop %rbp
400611: e9 7a ff ff ff jmpq 400590 <__printf_chk@plt+0xb0>
400616: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40061d: 00 00 00
400620: 53 push %rbx
400621: be 74 07 40 00 mov $0x400774,%esi
400626: bf 01 00 00 00 mov $0x1,%edi
40062b: 48 83 ec 30 sub $0x30,%rsp
40062f: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400636: 00 00
400638: 48 89 44 24 28 mov %rax,0x28(%rsp)
40063d: 31 c0 xor %eax,%eax
40063f: 48 b8 10 60 21 33 15 movabs $0x6223331533216010,%rax
400646: 33 23 62
400649: c6 44 24 20 00 movb $0x0,0x20(%rsp)
40064e: 48 89 04 24 mov %rax,(%rsp)
400652: 48 b8 45 65 76 34 41 movabs $0x6675364134766545,%rax
400659: 36 75 66
40065c: 48 89 44 24 08 mov %rax,0x8(%rsp)
400661: 48 b8 17 67 75 64 10 movabs $0x6570331064756717,%rax
400668: 33 70 65
40066b: 48 89 44 24 10 mov %rax,0x10(%rsp)
400670: 48 b8 18 35 76 62 11 movabs $0x6671671162763518,%rax
400677: 67 71 66
40067a: 48 89 44 24 18 mov %rax,0x18(%rsp)
40067f: 8b 1c 25 40 05 40 00 mov 0x400540,%ebx
400686: 31 c0 xor %eax,%eax
400688: 89 da mov %ebx,%edx
40068a: e8 51 fe ff ff callq 4004e0 <__printf_chk@plt>
40068f: 48 8d 54 24 20 lea 0x20(%rsp),%rdx
400694: 48 89 e0 mov %rsp,%rax
400697: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
40069e: 00 00
4006a0: 31 18 xor %ebx,(%rax)
4006a2: 48 83 c0 04 add $0x4,%rax
4006a6: 48 39 d0 cmp %rdx,%rax
4006a9: 75 f5 jne 4006a0 <__printf_chk@plt+0x1c0>
4006ab: 31 c0 xor %eax,%eax
4006ad: 48 89 e2 mov %rsp,%rdx
4006b0: be 82 07 40 00 mov $0x400782,%esi
4006b5: bf 01 00 00 00 mov $0x1,%edi
4006ba: e8 21 fe ff ff callq 4004e0 <__printf_chk@plt>
4006bf: 31 c0 xor %eax,%eax
4006c1: 48 8b 4c 24 28 mov 0x28(%rsp),%rcx
4006c6: 64 48 33 0c 25 28 00 xor %fs:0x28,%rcx
4006cd: 00 00
4006cf: 75 06 jne 4006d7 <__printf_chk@plt+0x1f7>
4006d1: 48 83 c4 30 add $0x30,%rsp
4006d5: 5b pop %rbx
4006d6: c3 retq
4006d7: e8 e4 fd ff ff callq 4004c0 <__stack_chk_fail@plt>
4006dc: 0f 1f 40 00 nopl 0x0(%rax)
4006e0: bf 97 07 40 00 mov $0x400797,%edi
4006e5: e9 c6 fd ff ff jmpq 4004b0 <puts@plt>
4006ea: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
4006f0: 41 57 push %r15
4006f2: 41 56 push %r14
4006f4: 41 89 ff mov %edi,%r15d
4006f7: 41 55 push %r13
4006f9: 41 54 push %r12
4006fb: 4c 8d 25 0e 07 20 00 lea 0x20070e(%rip),%r12 # 600e10 <__printf_chk@plt+0x200930>
400702: 55 push %rbp
400703: 48 8d 2d 0e 07 20 00 lea 0x20070e(%rip),%rbp # 600e18 <__printf_chk@plt+0x200938>
40070a: 53 push %rbx
40070b: 49 89 f6 mov %rsi,%r14
40070e: 49 89 d5 mov %rdx,%r13
400711: 4c 29 e5 sub %r12,%rbp
400714: 48 83 ec 08 sub $0x8,%rsp
400718: 48 c1 fd 03 sar $0x3,%rbp
40071c: e8 5f fd ff ff callq 400480 <puts@plt-0x30>
400721: 48 85 ed test %rbp,%rbp
400724: 74 20 je 400746 <__printf_chk@plt+0x266>
400726: 31 db xor %ebx,%ebx
400728: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40072f: 00
400730: 4c 89 ea mov %r13,%rdx
400733: 4c 89 f6 mov %r14,%rsi
400736: 44 89 ff mov %r15d,%edi
400739: 41 ff 14 dc callq *(%r12,%rbx,8)
40073d: 48 83 c3 01 add $0x1,%rbx
400741: 48 39 eb cmp %rbp,%rbx
400744: 75 ea jne 400730 <__printf_chk@plt+0x250>
400746: 48 83 c4 08 add $0x8,%rsp
40074a: 5b pop %rbx
40074b: 5d pop %rbp
40074c: 41 5c pop %r12
40074e: 41 5d pop %r13
400750: 41 5e pop %r14
400752: 41 5f pop %r15
400754: c3 retq
400755: 90 nop
400756: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40075d: 00 00 00
400760: f3 c3 repz retq

1
OK, it's a very long text, and obviously it's difficult for human to understand it. So maybe we can use something smartly. We can add breakpoint at everywhere it seems like a return or finish for a function, and we could run it in gdb. The place it would not be run is the thing we are finding.

(gdb) b *0x400520
Breakpoint 1 at 0x400520
(gdb) b *0x4005c9
Breakpoint 2 at 0x4005c9
(gdb) b *0x40061d
Breakpoint 3 at 0x40061d
(gdb) b *0x4006c6
Breakpoint 4 at 0x4006c6
(gdb) b *0x400728
Breakpoint 5 at 0x400728
(gdb) run
Starting program: /home/binary/code/chapter5/lvl5

Breakpoint 1, 0x0000000000400520 in ?? ()
(gdb) c
Continuing.

Breakpoint 5, 0x0000000000400728 in ?? ()
(gdb) c
Continuing.

Breakpoint 2, 0x00000000004005c9 in ?? ()
(gdb) c
Continuing.
nothing to see here
[Inferior 1 (process 1936) exited with code 01]
(gdb)

1
As we predicted, there are some codes never used. Let's focus on it.

400620: 53 push %rbx
400621: be 74 07 40 00 mov $0x400774,%esi
400626: bf 01 00 00 00 mov $0x1,%edi
40062b: 48 83 ec 30 sub $0x30,%rsp
40062f: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400636: 00 00
400638: 48 89 44 24 28 mov %rax,0x28(%rsp)
40063d: 31 c0 xor %eax,%eax
40063f: 48 b8 10 60 21 33 15 movabs $0x6223331533216010,%rax
400646: 33 23 62
400649: c6 44 24 20 00 movb $0x0,0x20(%rsp)
40064e: 48 89 04 24 mov %rax,(%rsp)
400652: 48 b8 45 65 76 34 41 movabs $0x6675364134766545,%rax
400659: 36 75 66
40065c: 48 89 44 24 08 mov %rax,0x8(%rsp)
400661: 48 b8 17 67 75 64 10 movabs $0x6570331064756717,%rax
400668: 33 70 65
40066b: 48 89 44 24 10 mov %rax,0x10(%rsp)
400670: 48 b8 18 35 76 62 11 movabs $0x6671671162763518,%rax
400677: 67 71 66
40067a: 48 89 44 24 18 mov %rax,0x18(%rsp)
40067f: 8b 1c 25 40 05 40 00 mov 0x400540,%ebx
400686: 31 c0 xor %eax,%eax
400688: 89 da mov %ebx,%edx
40068a: e8 51 fe ff ff callq 4004e0 <__printf_chk@plt>
40068f: 48 8d 54 24 20 lea 0x20(%rsp),%rdx
400694: 48 89 e0 mov %rsp,%rax
400697: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
40069e: 00 00
4006a0: 31 18 xor %ebx,(%rax)
4006a2: 48 83 c0 04 add $0x4,%rax
4006a6: 48 39 d0 cmp %rdx,%rax
4006a9: 75 f5 jne 4006a0 <__printf_chk@plt+0x1c0>
4006ab: 31 c0 xor %eax,%eax
4006ad: 48 89 e2 mov %rsp,%rdx
4006b0: be 82 07 40 00 mov $0x400782,%esi
4006b5: bf 01 00 00 00 mov $0x1,%edi
4006ba: e8 21 fe ff ff callq 4004e0 <__printf_chk@plt>
4006bf: 31 c0 xor %eax,%eax
4006c1: 48 8b 4c 24 28 mov 0x28(%rsp),%rcx
4006c6: 64 48 33 0c 25 28 00 xor %fs:0x28,%rcx

1
2
As the hint says, we could do a redirection to here to get something.
The rest thing we need to do is put 0x400620 as main to run. In fact, we need only change this:

400500: 48 83 ec 08 sub $0x8,%rsp
400504: bf 97 07 40 00 mov $0x400797,%edi
400509: e8 a2 ff ff ff callq 4004b0 <puts@plt>
40050e: b8 01 00 00 00 mov $0x1,%eax
400513: 48 83 c4 08 add $0x8,%rsp
400517: c3 retq
400518: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40051f: 00
400520: 31 ed xor %ebp,%ebp
400522: 49 89 d1 mov %rdx,%r9
400525: 5e pop %rsi
400526: 48 89 e2 mov %rsp,%rdx
400529: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
40052d: 50 push %rax
40052e: 54 push %rsp
40052f: 49 c7 c0 60 07 40 00 mov $0x400760,%r8
400536: 48 c7 c1 f0 06 40 00 mov $0x4006f0,%rcx
40053d: 48 c7 c7 00 05 40 00 mov $0x400500,%rdi -> 40053d 48 c7 c7 20 06 40 00 mov $0x400520, %rdi
400544: e8 87 ff ff ff callq 4004d0 <__libc_start_main@plt>

1
Now we can get the key.

binary@binary-VirtualBox:/code/chapter5$ ./lvl5
key = 0x00400620
decrypted flag = 0fa355cbec64a05f7a5d050e836b1a1f
binary@binary-VirtualBox:
/code/chapter5$ ./oracle 0fa355cbec64a05f7a5d050e836b1a1f
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Level 5 completed, unlocked lvl6 |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
Run oracle with -h to show a hint

1
2
3

## Level 6
We get some hint of level 6.

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 0fa355cbec64a05f7a5d050e836b1a1f -h
Find out what I expect, then trace me for a hint

1
So let's just run it.

binary@binary-VirtualBox:/code/chapter5$ ./lvl6
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
binary@binary-VirtualBox:
/code/chapter5$ ./lvl6 101
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
binary@binary-VirtualBox:/code/chapter5$ ./lvl6 2
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
binary@binary-VirtualBox:
/code/chapter5$ ./lvl6 97
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
binary@binary-VirtualBox:~/code/chapter5$ ltrace ./lvl6
__libc_start_main(0x4005f0, 1, 0x7fff758d71b8, 0x400890 <unfinished …>
__printf_chk(1, 0x400947, 2, 100) = 2
__printf_chk(1, 0x400947, 3, 0x7ffffffe) = 2
__printf_chk(1, 0x400947, 5, 0x7ffffffe) = 2
__printf_chk(1, 0x400947, 7, 0x7ffffffe) = 2
__printf_chk(1, 0x400947, 11, 0x7ffffffe) = 3
__printf_chk(1, 0x400947, 13, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 17, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 19, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 23, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 29, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 31, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 37, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 41, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 43, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 47, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 53, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 59, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 61, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 67, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 71, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 73, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 79, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 83, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 89, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 97, 0x7ffffffd) = 3
putchar(10, 3, 0, 0x7ffffffd2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
) = 10
+++ exited (status 0) +++

1
We get nothing, and it seems we need to find something it expected. So we maybe could use strings to try if we can get anything interesting.

binary@binary-VirtualBox:~/code/chapter5$ strings ./lvl6
/lib64/ld-linux-x86-64.so.2
libc.so.6
__printf_chk
__stack_chk_fail
putchar
__sprintf_chk
strcmp
_libc_start_main
setenv
gmon_start
GLIBC_2.3.4
GLIBC_2.4
GLIBC_2.2.5
UH-`
AWAVA
AUATL
[]A\A]A^A

DEBUG: argv[1] = %s
get_data_addr
0x%jx
DATA_ADDR
;*3$”

1
2
GOOD! There are really obvious hint for us. ``argv[1] = %s get_data_addr''
So we could just try to run it with the parameter.

binary@binary-VirtualBox:/code/chapter5$ ./lvl6 get_data_addr
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
binary@binary-VirtualBox:
/code/chapter5$ ltrace ./lvl6 get_data_addr
__libc_start_main(0x4005f0, 2, 0x7ffd05739688, 0x400890 <unfinished …>
strcmp(“get_data_addr”, “get_data_addr”) = 0
__sprintf_chk(0x7ffd05739180, 1, 1024, 0x400937) = 8
setenv(“DATA_ADDR”, “0x4006c1”, 1) = 0
__printf_chk(1, 0x400947, 2, 100) = 2
__printf_chk(1, 0x400947, 3, 0x7ffffffe) = 2
__printf_chk(1, 0x400947, 5, 0x7ffffffe) = 2
__printf_chk(1, 0x400947, 7, 0x7ffffffe) = 2
__printf_chk(1, 0x400947, 11, 0x7ffffffe) = 3
__printf_chk(1, 0x400947, 13, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 17, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 19, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 23, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 29, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 31, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 37, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 41, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 43, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 47, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 53, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 59, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 61, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 67, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 71, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 73, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 79, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 83, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 89, 0x7ffffffd) = 3
__printf_chk(1, 0x400947, 97, 0x7ffffffd) = 3
putchar(10, 3, 0, 0x7ffffffd2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
) = 10
+++ exited (status 0) +++

1
There is nothing special in directly running, but something interesting in ltrace. If input get_data_addr, set DATA_ADDR as 0x4006c1. Let's have a look what it is.

(gdb) x/16x 0x4006c1
0x4006c1: 0x2e 0x29 0xc6 0x4a 0x0f 0x03 0xa6 0xee
0x4006c9: 0x2a 0x30 0x7f 0xec 0xc8 0xc3 0xff 0x42

1
We get the key ``2e29c64a0f03a6ee2a307fecc8c3ff42``.

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 2e29c64a0f03a6ee2a307fecc8c3ff42
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Level 6 completed, unlocked lvl7 |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
Run oracle with -h to show a hint

1
2
3

## Level 7
OK, Now we stand on level 7.

binary@binary-VirtualBox:~/code/chapter5$ ./oracle 2e29c64a0f03a6ee2a307fecc8c3ff42 -h
First observe my runtime state,
then watch closely while I replicate

1
Let's first see runtime state.

binary@binary-VirtualBox:~/code/chapter5$ ./lvl7
-bash: ./lvl7: cannot execute binary file: Exec format error

1
Strange! Let's have a look what lvl7 is.

binary@binary-VirtualBox:~/code/chapter5$ file ./lvl7
./lvl7: gzip compressed data, last modified: Sat Dec 1 17:30:15 2018, from Unix

1
OK, it's a gzip. First unzip it.

binary@binary-VirtualBox:~/code/chapter5$ tar -xvzf ./lvl7
stage1
stage2.zip

1
OK, two file is here.

binary@binary-VirtualBox:/code/chapter5$ file stage1
stage1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=ac71081a0951af729a4064c1dafbc5713b1537e3, stripped
binary@binary-VirtualBox:
/code/chapter5$ unzip stage2.zip
Archive: stage2.zip
[stage2.zip] tmp password:

1
As we can see, stage1 is a elf file, and stage2.zip need a password. It seems that we need to get the password through stage1.

binary@binary-VirtualBox:/code/chapter5$ ./stage1
binary@binary-VirtualBox:
/code/chapter5$ ltrace ./stage1
__libc_start_main(0x4003e0, 1, 0x7fff690515d8, 0x400520 <no return …>
+++ exited (status 0) +++

1
There is nothing interesting in runtime state. So maybe the password would stay in strings.

binary@binary-VirtualBox:~/code/chapter5$ strings stage1
/lib64/ld-linux-x86-64.so.2
libc.so.6
_libc_start_main
gmon_start
GLIBC_2.2.5
dump ecx
UH-0
AWAVA
AUATL
[]A\A]A^A

S)TA
E2 KE
;*3$”

1
We in fact find something interesting inthe last 3 lines. There are something be like STAE2 KE. Now it's time to use GDB!

00000000004003e0 <.text>:
4003e0: ba a4 05 40 00 mov $0x4005a4,%edx
4003e5: 0f 1f 00 nopl (%rax)
4003e8: 0f be 02 movsbl (%rdx),%eax
4003eb: 83 f8 30 cmp $0x30,%eax
4003ee: 7c 12 jl 400402 <__libc_start_main@plt+0x42>
4003f0: 83 f8 5a cmp $0x5a,%eax
4003f3: 7f 0d jg 400402 <__libc_start_main@plt+0x42>
4003f5: eb 09 jmp 400400 <__libc_start_main@plt+0x40>
4003f7: 64 75 6d fs jne 400467 <__libc_start_main@plt+0xa7>
4003fa: 70 20 jo 40041c <__libc_start_main@plt+0x5c>
4003fc: 65 63 78 00 movslq %gs:0x0(%rax),%edi
400400: 89 c1 mov %eax,%ecx
400402: 48 83 c2 01 add $0x1,%rdx
400406: 48 81 fa b5 05 40 00 cmp $0x4005b5,%rdx
40040d: 75 d9 jne 4003e8 <__libc_start_main@plt+0x28>
40040f: 31 c0 xor %eax,%eax
400411: c3 retq
(gdb) b *0x4003e0
Breakpoint 1 at 0x4003e0
(gdb) run
Starting program: /home/binary/code/chapter5/stage1

Breakpoint 1, 0x00000000004003e0 in ?? ()
(gdb) display/i $pc
1: x/i $pc
=> 0x4003e0: mov $0x4005a4,%edx
(gdb) x/16s 0x4005a4
0x4005a4: “ S)TA\021G\372\336\377E2 KE\212Y”

1
Lucky! We meet it at the first line. We guess the password is ``STAGE2KEY``.

binary@binary-VirtualBox:~/code/chapter5/lv7$ ./stage2
#include <stdio.h>
#include <string.h>
#include
#include

int main()
{
std::vector hex;
char q[] = “#include <stdio.h>\n#include <string.h>\n#include \n#include \n\nint main()\n{\nstd::vector hex;\nchar q[] = "%s";\nint i, _0F;\nchar c, qc[4096];\n\nfor(i = 0; i < 32; i++) for(c = ‘0’; c <= ‘9’; c++) hex.push_back(c);\nfor(i = 0; i < 32; i++) for(c = ‘A’; c <= ‘F’; c++) hex.push_back(c);\nstd::srand(55);\nstd::random_shuffle(hex.begin(), hex.end());\n\n_0F = 0;\nfor(i = 0; i < strlen(q); i++)\n{\nif(q[i] == 0xa)\n{\nqc[_0F++] = 0x5c;\nqc[_0F] = ‘n’;\n}\nelse if(q[i] == 0x22)\n{\nqc[_0F++] = 0x5c;\nqc[0F] = 0x22;\n}\nelse if(!strncmp(&q[i], "0F", 2) && (q[i-1] == ‘‘ || i == 545))\n{\nchar buf[3];\nbuf[0] = q[i];\nbuf[1] = q[i+1];\nbuf[2] = 0;\nunsigned j = strtoul(buf, NULL, 16);\nqc[_0F++] = q[i++] = hex[j];\nqc[_0F] = q[i] = hex[j+1];\n}\nelse qc[_0F] = q[i];\n_0F++;\n}\nqc[_0F] = 0;\n\nprintf(q, qc);\n\nreturn 0;\n}\n”;
int i, _0F;
char c, qc[4096];

for(i = 0; i < 32; i++) for(c = ‘0’; c <= ‘9’; c++) hex.push_back(c);
for(i = 0; i < 32; i++) for(c = ‘A’; c <= ‘F’; c++) hex.push_back(c);
std::srand(55);
std::random_shuffle(hex.begin(), hex.end());

_0F = 0;
for(i = 0; i < strlen(q); i++)
{
if(q[i] == 0xa)
{
qc[_0F++] = 0x5c;
qc[_0F] = ‘n’;
}
else if(q[i] == 0x22)
{
qc[_0F++] = 0x5c;
qc[0F] = 0x22;
}
else if(!strncmp(&q[i], “0F”, 2) && (q[i-1] == ‘
‘ || i == 545))
{
char buf[3];
buf[0] = q[i];
buf[1] = q[i+1];
buf[2] = 0;
unsigned j = strtoul(buf, NULL, 16);
qc[_0F++] = q[i++] = hex[j];
qc[_0F] = q[i] = hex[j+1];
}
else qc[_0F] = q[i];
_0F++;
}
qc[_0F] = 0;

printf(q, qc);

return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
Run stage2. We discover it puts a cpp file, and generate 2 Bytes keys. So we could iterated this process 16 times to get the entire key.
``` bash
#!/bin/bash

for i in $(seq 1 16); do
j=$((i+1))
./test$i > test$j.cpp
echo -n $(./test$i | egrep ', "' | cut -d '"' -f 2) >> flag
g++ test$j.cpp -o test$j
done

cat flag
echo

OK, now we get the full key.

1
2
3
4
5
6
7
binary@binary-VirtualBox:~/code/chapter5/lv7$ ./sol.sh
0F25E512A7763EEFB7696B3AEDA1F964
binary@binary-VirtualBox:~/code/chapter5$ ./oracle 0F25E512A7763EEFB7696B3AEDA1F964
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Level 7 completed, unlocked lvl8 |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
Run oracle with -h to show a hint

Level 8

We finally come in level 8. The hint is very strange.

1
2
binary@binary-VirtualBox:~/code/chapter5$ ./oracle 0F25E512A7763EEFB7696B3AEDA1F964 -h
Sometimes a byte is a bit

OK, we take it aside and see what lvl8 is first.

1
2
binary@binary-VirtualBox:~/code/chapter5$ file ./lvl8
./lvl8: ASCII text, with very long lines

Strange! ASCII text, and very long. Open it.
屏幕截图_20251128_140438.png
Obviously, it’s not any language we could understand, but we could still find something really interesting in it.
If you observe it carefully, you’ll find there are many repeated words, and also, the capacity is very strange. So it’s easy to think about that maybe the words is meaningless, but the uppercase and lowercase of the letters are very important. Let’s make a program to translate uppercase letters into 1 and lowercase letters into 0.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/bash
set -e

python3 - << 'PY'
import string
from pathlib import Path

data = Path("lvl8").read_text()

bits = []
for ch in data:
if ch in string.ascii_lowercase:
bits.append('0')
elif ch in string.ascii_uppercase:
bits.append('1')

bits_str = "".join(bits)
n_bits = len(bits_str) // 8 * 8
bits_str = bits_str[:n_bits]

if not bits_str:
raise SystemExit("没有从 lvl8 里提取到任何大小写字母")

value = int(bits_str, 2)
n_bytes = n_bits // 8
b = value.to_bytes(n_bytes, "big")

with open("level8.bin", "wb") as f:
f.write(b)
PY

file level8.bin

And we could get such a file level8.bin

1
2
binary@binary-VirtualBox:~/code/chapter5/lv8$ file level8.bin
level8.bin: PC bitmap, Windows 3.x format, 300 x 300 x 24

Now we have a bit map which is 300 * 300 * 24. And open the picture, it’s like this:
level8.bmp

I’m stuck here for a bit while. Because I don’t know what the picture is. So I decided to open it with hexedit first.
屏幕截图_20251128_141456.png

Strange! Do you remember what the picture is? 300 * 300 * 24. However, that means, white should be FF and black should be 00. But as you see in picture, there are many 01 and FE, that means there are some information in the lowest bit. Let’s extract it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
FILE *fileptr;
unsigned char *buffer;
unsigned char *p;
long filelen;

fileptr = fopen("lvl8_pic", "rb");
if (!fileptr)
{
perror("fopen lvl8_pic");
return 1;
}

fseek(fileptr, 0, SEEK_END);
filelen = ftell(fileptr);
rewind(fileptr);

buffer = malloc(filelen);
if (!buffer)
{
perror("malloc");
fclose(fileptr);
}

fread(buffer, filelen, 1, fileptr);
fclose(fileptr);

FILE *out = fopen("lvl8_elf", "wb");
if (!out)
{
perror("fopen lvl8_elf");
free(buffer);
return 1;
}

unsigned char decodeByte = 0;
long bufferCounter = 0;
int bitIndex = 0;

p = buffer;

while (1)
{
unsigned char thisByte = *p;
p++;
bufferCounter++;

if ((thisByte & 1) == 1)
{
decodeByte |= (1 << (7 - bitIndex));
}

bitIndex++;

if (bitIndex == 8)
{
bitIndex = 0;
fwrite(&decodeByte, 1, 1, out);
decodeByte = 0;
}

if (bufferCounter >= filelen)
{
break;
}

}
fclose(out);
free(buffer);
return 0;
}

Now we get another elf file, which seems to have ability to help us get the key.

1
2
3
4
binary@binary-VirtualBox:~/code/chapter5/lv8$ gcc ./deal.c -o deal
binary@binary-VirtualBox:~/code/chapter5/lv8$ ./deal
binary@binary-VirtualBox:~/code/chapter5/lv8$ file ./lvl8_elf
./lvl8_elf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=3f43c1bc1bc2d1dccc12d2fbb1cb83347e8cb3b4, not stripped

Let’s first run it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
binary@binary-VirtualBox:~/code/chapter5/lv8$ ./lvl8_elf
binary@binary-VirtualBox:~/code/chapter5/lv8$ ltrace ./lvl8
"./lvl8" is not an ELF file
binary@binary-VirtualBox:~/code/chapter5/lv8$ ltrace ./lvl8_elf
__libc_start_main(0x4005d6, 1, 0x7ffe9d5e1e98, 0x4006b0 <unfinished ...>
memalign(4096, 4096, 0x7ffe9d5e1ea8, 0) = 0x17f1000
mprotect(0x17f1000, 4096, 3, 0x7fb7fa68bb20) = 0
memcpy(0x17f1000, "\234\235\236\232\233t\315\314\314\314s\315\314\314\314\204A\371\324\314\314\314\204G\331\376\314\314\314\303\311\223"..., 83) = 0x17f1000
mprotect(0x17f1000, 4096, 4, 82) = 0
+++ exited (status 0) +++
binary@binary-VirtualBox:~/code/chapter5/lv8$ ./lvl8_elf
binary@binary-VirtualBox:~/code/chapter5/lv8$ ltrace ./lvl8_elf
__libc_start_main(0x4005d6, 1, 0x7ffca7aba4e8, 0x4006b0 <unfinished ...>
memalign(4096, 4096, 0x7ffca7aba4f8, 0) = 0x17f9000
mprotect(0x17f9000, 4096, 3, 0x7f4f4f00db20) = 0
memcpy(0x17f9000, "\234\235\236\232\233t\315\314\314\314s\315\314\314\314\204A\371\324\314\314\314\204G\331\376\314\314\314\303\311\223"..., 83) = 0x17f9000
mprotect(0x17f9000, 4096, 4, 82) = 0
+++ exited (status 0) +++

Really disgusting. It don’t want give us the key directly. OK, as we usually do, have a look at ltrace. There are something interesting need we focus. First it unlock a memory, and then write something on it, and lock it. Maybe we can find something it. GDB!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
00000000004005d6 <main>:
4005d6: 55 push %rbp
4005d7: 48 89 e5 mov %rsp,%rbp
4005da: 48 83 ec 30 sub $0x30,%rsp
4005de: 89 7d dc mov %edi,-0x24(%rbp)
4005e1: 48 89 75 d0 mov %rsi,-0x30(%rbp)
4005e5: be 00 10 00 00 mov $0x1000,%esi
4005ea: bf 00 10 00 00 mov $0x1000,%edi
4005ef: e8 bc fe ff ff callq 4004b0 <memalign@plt>
4005f4: 48 89 45 f0 mov %rax,-0x10(%rbp)
4005f8: 48 8b 45 f0 mov -0x10(%rbp),%rax
4005fc: ba 03 00 00 00 mov $0x3,%edx
400601: be 00 10 00 00 mov $0x1000,%esi
400606: 48 89 c7 mov %rax,%rdi
400609: e8 b2 fe ff ff callq 4004c0 <mprotect@plt>
40060e: 8b 05 a0 0a 20 00 mov 0x200aa0(%rip),%eax # 6010b4 <flag_bin_len>
400614: 89 c2 mov %eax,%edx
400616: 48 8b 45 f0 mov -0x10(%rbp),%rax
40061a: be 60 10 60 00 mov $0x601060,%esi
40061f: 48 89 c7 mov %rax,%rdi
400622: e8 79 fe ff ff callq 4004a0 <memcpy@plt>
400627: 8b 05 87 0a 20 00 mov 0x200a87(%rip),%eax # 6010b4 <flag_bin_len>
40062d: 89 c2 mov %eax,%edx
40062f: 48 8b 45 f0 mov -0x10(%rbp),%rax
400633: 48 01 d0 add %rdx,%rax
400636: c6 00 c3 movb $0xc3,(%rax)
400639: c7 45 ec 00 00 00 00 movl $0x0,-0x14(%rbp)
400640: eb 26 jmp 400668 <main+0x92>
400642: 8b 45 ec mov -0x14(%rbp),%eax
400645: 48 63 d0 movslq %eax,%rdx
400648: 48 8b 45 f0 mov -0x10(%rbp),%rax
40064c: 48 01 d0 add %rdx,%rax
40064f: 8b 55 ec mov -0x14(%rbp),%edx
400652: 48 63 ca movslq %edx,%rcx
400655: 48 8b 55 f0 mov -0x10(%rbp),%rdx
400659: 48 01 ca add %rcx,%rdx
40065c: 0f b6 12 movzbl (%rdx),%edx
40065f: 83 f2 cc xor $0xffffffcc,%edx
400662: 88 10 mov %dl,(%rax)
400664: 83 45 ec 01 addl $0x1,-0x14(%rbp)
400668: 8b 55 ec mov -0x14(%rbp),%edx
40066b: 8b 05 43 0a 20 00 mov 0x200a43(%rip),%eax # 6010b4 <flag_bin_len>
400671: 39 c2 cmp %eax,%edx
400673: 72 cd jb 400642 <main+0x6c>
400675: 48 8b 45 f0 mov -0x10(%rbp),%rax
400679: ba 04 00 00 00 mov $0x4,%edx
40067e: be 00 10 00 00 mov $0x1000,%esi
400683: 48 89 c7 mov %rax,%rdi
400686: e8 35 fe ff ff callq 4004c0 <mprotect@plt>
40068b: 48 8b 45 f0 mov -0x10(%rbp),%rax
40068f: 8b 15 1f 0a 20 00 mov 0x200a1f(%rip),%edx # 6010b4 <flag_bin_len>
400695: 89 d2 mov %edx,%edx
400697: 48 01 d0 add %rdx,%rax
40069a: 48 89 45 f8 mov %rax,-0x8(%rbp)
40069e: 48 8b 55 f8 mov -0x8(%rbp),%rdx
4006a2: b8 00 00 00 00 mov $0x0,%eax
4006a7: ff d2 callq *%rdx
4006a9: b8 00 00 00 00 mov $0x0,%eax
4006ae: c9 leaveq
4006af: c3 retq

We can see that, after operating the memory block, it gets in a circle. And obviously, we don’t want to see what it exactly do. So we just use gdb and try to see what happen to the block.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
(gdb) b *0x400606
Breakpoint 1 at 0x400606
(gdb) b *0x400673
Breakpoint 2 at 0x400673
(gdb) b *0x400675
Breakpoint 3 at 0x400675
(gdb) run
Starting program: /home/binary/code/chapter5/lv8/lvl8_elf

Breakpoint 1, 0x0000000000400606 in main ()
(gdb) x/16x $rdi
0xffffffff: Cannot access memory at address 0xffffffff
(gdb) ni
0x0000000000400609 in main ()
(gdb) x/16x $rdi
0x603000: 0x00000000 0x00000000 0x00000000 0x00000000
0x603010: 0x00000000 0x00000000 0x00000000 0x00000000
0x603020: 0x00000000 0x00000000 0x00000000 0x00000000
0x603030: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) c
Continuing.

Breakpoint 2, 0x0000000000400673 in main ()
(gdb) display/16bx 0x603000
1: x/16xb 0x603000
0x603000: 0x9c 0x9d 0x9e 0x9a 0x9b 0x74 0xcd 0xcc
0x603008: 0xcc 0xcc 0x73 0xcd 0xcc 0xcc 0xcc 0x84
(gdb) c
Continuing.

Breakpoint 2, 0x0000000000400673 in main ()
1: x/16xb 0x603000
0x603000: 0x50 0x9d 0x9e 0x9a 0x9b 0x74 0xcd 0xcc
0x603008: 0xcc 0xcc 0x73 0xcd 0xcc 0xcc 0xcc 0x84
(gdb) c
Continuing.

Breakpoint 2, 0x0000000000400673 in main ()
1: x/16xb 0x603000
0x603000: 0x50 0x51 0x9e 0x9a 0x9b 0x74 0xcd 0xcc
0x603008: 0xcc 0xcc 0x73 0xcd 0xcc 0xcc 0xcc 0x84
(gdb) c
Continuing.

Breakpoint 2, 0x0000000000400673 in main ()
1: x/16xb 0x603000
0x603000: 0x50 0x51 0x52 0x9a 0x9b 0x74 0xcd 0xcc
0x603008: 0xcc 0xcc 0x73 0xcd 0xcc 0xcc 0xcc 0x84
(gdb) c
Continuing.

Breakpoint 2, 0x0000000000400673 in main ()
1: x/16xb 0x603000
0x603000: 0x50 0x51 0x52 0x56 0x9b 0x74 0xcd 0xcc
0x603008: 0xcc 0xcc 0x73 0xcd 0xcc 0xcc 0xcc 0x84
(gdb) c
Continuing.

Breakpoint 2, 0x0000000000400673 in main ()
1: x/16xb 0x603000
0x603000: 0x50 0x51 0x52 0x56 0x57 0x74 0xcd 0xcc
0x603008: 0xcc 0xcc 0x73 0xcd 0xcc 0xcc 0xcc 0x84
(gdb) delete 2
(gdb) c
Continuing.

Breakpoint 3, 0x0000000000400675 in main ()
1: x/16xb 0x603000
0x603000: 0x50 0x51 0x52 0x56 0x57 0xb8 0x01 0x00
0x603008: 0x00 0x00 0xbf 0x01 0x00 0x00 0x00 0x48

We can see that, the circle do one thing: change 0x603000 into 50 51 52 56 57. So we have enough reason to think that maybe there are something interesting in 0x53-0x55.

1
(gdb) dump binary memory dump.bin 0x603000 0x603055

屏幕截图_20251128_144924.png
Got it!

1
2
3
4
5
6
7
binary@binary-VirtualBox:~/code/chapter5$ ./oracle 2235a6b2123404469f4abce71b1dd29f
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Level 8 completed, unlocked reward.tar.gz |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
Run oracle with -h to show a hint
binary@binary-VirtualBox:~/code/chapter5$ ./oracle 2235a6b2123404469f4abce71b1dd29f -h
Congratulations, you've completed all levels!

And there is a rewards for us!
wall_1024x768.jpg

This game is really meaningful and interesting. I think everyone who wants to expose joy of binary analysis should finish this game.

The whole file directory could be found on https://github.com/BoringZheng/binary-analysis.git