Writeup: OpenToAll 2015 - Switchy

Informations

  • Category: reverse
  • Points: 150

Description

An ELF 32, i386, not-stripped executable. You could find all infos rabin2 -g [FILENAME]
Example output:

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
rabin2 -g 7fcdb7907692cbd6ea87600ab11377b3

[Sections]
...

32 sections
[Entrypoints]
vaddr=0x08048370 paddr=0x00000370 baddr=0x08048000 laddr=0x00000000

1 entrypoints
[Main]
vaddr=0x080486d0 paddr=0x000006d0
[Imports]
ordinal=001 plt=0x08048330 bind=GLOBAL type=FUNC name=printf
ordinal=002 plt=0x08048340 bind=GLOBAL type=FUNC name=fflush
ordinal=003 plt=0x08048350 bind=UNKNOWN type=NOTYPE name=__gmon_start__
ordinal=004 plt=0x08048360 bind=GLOBAL type=FUNC name=__libc_start_main

4 imports
[Symbols]
vaddr=0x080486d0 ... type=FUNC name=main
vaddr=0x08048330 ... type=FUNC name=imp.printf
vaddr=0x08048340 ... type=FUNC name=imp.fflush
vaddr=0x08048350 ... type=NOTYPE name=imp.__gmon_start__
vaddr=0x08048360 ... type=FUNC name=imp.__libc_start_main

5 symbols
...
file test
type EXEC (Executable file)
canary false
nx true
crypto false
va true
class ELF32
lang c
arch x86
bits 32
machine Intel 80386
os linux
subsys linux
endian little
strip false
static false
...
[Header fields]
...

12 fields
[Linked libraries]
libc.so.6

1 libraries
[Relocations]
vaddr=0x0804affc paddr=0x00002ffc type=SET_32 __gmon_start__
vaddr=0x0804b160 paddr=0x00003160 type=ADD_64
vaddr=0x0804b00c paddr=0x0000300c type=SET_32 printf
vaddr=0x0804b010 paddr=0x00003010 type=SET_32 fflush
vaddr=0x0804b014 paddr=0x00003014 type=SET_32 __gmon_start__
vaddr=0x0804b018 paddr=0x00003018 type=SET_32 __libc_start_main

6 relocations
10554

When run, it outputs a few non-printable chars on stdout, breaking the console.

Hexdump of the program’s stdout:

1
2
3
4
5
./7fcdb7907692cbd6ea87600ab11377b3 | hexdump -C
00000000 63 db 7d db 97 ea 4c c9 26 0e 07 b7 0d 69 ae 1f |c.}...L.&....i..|
00000010 b7 1f fc db fc b7 1f fc db fc b7 a6 fc 69 51 0e |.............iQ.|
00000020 c9 46 0a |.F.|
00000023

Writeup

We suspect that the program pre-processes the flag and outputs the result.
It’s time for a radare session!

Analysis (radare)

1
$ radare2 -d ./7fcdb7907692cbd6ea87600ab11377b3

Once inside radare, we prepare for the analysis:

1
2
3
4
V
:db main
:dc
:af

Then, in visual mode cycle with p to reach the disassembled view:

Main

./main.png

We suspect that function[1] is actually doing the pre-processing responsible of the weird outputs by printf.

function[1] structure

The function[1] has a jmp ecx trampoline which lands in a xor between two bytes, followed by a ret.

./f1.png

main - function[1] interactions

Each time the main calls function[1] with a small index as argument.
Moving in radare is really fast! Follow the calls with [] and back to caller by pressing “u”.

re-constructing the data sequence

Now we can follow the flow, and before every

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
0x8048583 f
0x8048605 -> 0x804b040 -> 0x6c (l) 0xb7 (dirty char)
0x8048535 a
0x80484cd -> 0x804b028 -> 0x67 (g) 0xbc (dirty char)
0x8048687 {
0x8048639 -> 0x804b044 -> 0x73 (s) 0x99 (dirty char)
0x804866d w
0x804859d -> 0x804b038 -> 0x69 (i) 0xa0 (dirty char)
0x8048653 t
0x8048501 -> 0x804b02c -> 0x63 (c) 0x6d (dirty char)
0x80485b7 3a 68 6f h
0x80484b3 26 20 97 (space)
0x80485d1 3c 6a 67 j
0x804851b 2e 75 1c u
0x80485eb 3e 6d c3 m
0x80484e7 2a 70 6f p
0x80484b3 26 20 97 (space)
0x80484e7 2a 70 6f p
0x8048499 24 6f 93 o
0x80484cd g
0x8048499 o
0x80484b3 (space)
0x80484e7 p
0x8048499 o
0x80484cd g
0x8048499 o
0x80484b3 (space)
0x804854f 32 62 c4 b
0x8048499 o
0x804851b u
0x804861f 42 6e 3f n
0x80485d1 j <-- it's a c, but we found a j at the first try
0x8048569 34 65 ac e
0x80486a1 }

gdb-peda

./peda.png

automation!

There are tons of way to to so:

GDB with python

First of all check configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(gdb) show configuration
This GDB was configured as follows:
configure --host=x86_64-unknown-linux-gnu --target=x86_64-unknown-linux-gnu
--with-auto-load-dir=$debugdir:$datadir/auto-load
--with-auto-load-safe-path=$debugdir:$datadir/auto-load
--with-expat
--with-gdb-datadir=/usr/local/share/gdb (relocatable)
--with-jit-reader-dir=/usr/local/lib/gdb (relocatable)
--without-libunwind-ia64
--with-lzma
--with-python=/usr
--with-separate-debug-dir=/usr/local/lib/debug (relocatable)
--with-zlib
--without-babeltrace

("Relocatable" means the directory can be moved with the GDB installation
tree, and GDB will still find it.)

Note --with-python
Now set python print-stack full, first tries verbose is useful.
Damn is a long run…. something else?

GDB commands

1
2
3
4
5
6
break *0x8048497
commands
si 2
printf "%c",$eax
c
end

And at the end the best method ever….PATCHING!

1
2
3
4
5
6
7
#!/usr/bin/python
addr = [0x4a7,0x4c1,0x4db,0x4f5,0x50f,0x529,0x543,0x55d,0x577,0x591,0x5ab,0x5c5,0x5df,0x5f9,0x613,0x62d,0x647,0x661,0x67b,0x695,0x6af]
with file('switchy','r+b') as f:
for i in addr:
f.seek(i)
f.write(chr(0x90)+chr(0x90))
f.closed

Source ^

Flag

flag{switch jump pogo pogo bounce}