Reverse Engineering

KITCTF intro talk

The science and art of understanding programs without source code

If a CTF challenge only gives you:


$ ls -l
total 16
-rwxr-xr-x 1 nine nine 15640 Mär 12 21:07 weirdbinary*
                    

How to make flag?

What is a binary?


$ hexdump -x weirdbinary | head
0000000    457f    464c    0102    0001    0000    0000    0000    0000
0000010    0003    003e    0001    0000    1080    0000    0000    0000
0000020    0040    0000    0000    0000    3648    0000    0000    0000
0000030    0000    0000    0040    0038    000d    0040    001e    001d
0000040    0006    0000    0004    0000    0040    0000    0000    0000
0000050    0040    0000    0000    0000    0040    0000    0000    0000
0000060    02d8    0000    0000    0000    02d8    0000    0000    0000
0000070    0008    0000    0000    0000    0003    0000    0004    0000
0000080    0318    0000    0000    0000    0318    0000    0000    0000
0000090    0318    0000    0000    0000    001c    0000    0000    0000
                    

What are we up against?

 
$ file weirdbinary 
weirdbinary: ELF 64-bit LSB pie executable,
 x86-64,
 version 1 (SYSV),
 dynamically linked,
 interpreter /lib64/ld-linux-x86-64.so.2,
 BuildID[sha1]=7d9dc9ded6659e5142be365d894a299330e20229,
 for GNU/Linux 4.4.0,
 not stripped

                    

Can file lie?

Yes

                    
$ file -d weirdbinary 2>| grep " = 1" -B 1 -A2
--
341: > 0 string,=\177ELF,"ELF"]
0 == 0 = 1
bb=[0x7f29cdaff010,15640,0], 4 [b=0x7f29cdaff010,15640,0], [o=0x4, c=1]
mget(type=1, flag=0, offset=4, o=0, nbytes=15640, il=0, nc=0)
--
345: >> 4 byte&,=2,"64-bit"]
2 == 2 = 1
bb=[0x7f29cdaff010,15640,0], 5 [b=0x7f29cdaff010,15640,0], [o=0x5, c=1]
mget(type=1, flag=0, offset=5, o=0, nbytes=15640, il=0, nc=0)
--
347: >> 5 byte&,=1,"LSB"]
1 == 1 = 1
bb=[0x7f29cdaff010,15640,0], 0 [b=0x7f29cdaff010,15640,0], [o=0, c=2]
mget(type=46, flag=0, offset=0, o=0, nbytes=15640, il=0, nc=0)
--
61: >> 16 leshort&,=3,"${x?pie executable:shared object},"]
3 == 3 = 1
bb=[0x7f29cdaff010,15640,0], 16 [b=0x7f29cdaff010,15640,0], [o=0x10, c=1]
mget(type=10, flag=0, offset=16, o=0, nbytes=15640, il=0, nc=1)
--
79: >> 18 clear&,x,""]
0 == *any* = 1
bb=[0x7f29cdaff010,15640,0], 18 [b=0x7f29cdaff010,15640,0], [o=0x12, c=1]
mget(type=10, flag=0, offset=18, o=0, nbytes=15640, il=0, nc=1)
--
167: >> 18 leshort&,=62,"x86-64,"]
62 == 62 = 1
bb=[0x7f29cdaff010,15640,0], 18 [b=0x7f29cdaff010,15640,0], [o=0x12, c=1]
mget(type=10, flag=0, offset=18, o=0, nbytes=15640, il=0, nc=1)
--
336: >> 18 default&,x,""]
0 == *any* = 1
bb=[0x7f29cdaff010,15640,0], 20 [b=0x7f29cdaff010,15640,0], [o=0x14, c=1]
mget(type=11, flag=0, offset=20, o=0, nbytes=15640, il=0, nc=1)
--
339: >> 20 lelong&,=1,"version 1"]
1 == 1 = 1
bb=[0x7f29cdaff010,15640,0], 5 [b=0x7f29cdaff010,15640,0], [o=0x5, c=1]
mget(type=1, flag=0, offset=5, o=0, nbytes=15640, il=0, nc=0)
--
351: >> 7 byte&,=0,"(SYSV)"]
0 == 0 = 1
bb=[0x7f29cdaff010,15640,0], 7 [b=0x7f29cdaff010,15640,0], [o=0x7, c=1]
mget(type=1, flag=0, offset=7, o=0, nbytes=15640, il=0, nc=0)
                    
                    
~> binwalk

Tipps and Tricks

You can write your own magic files

-m, --magic-file magicfiles

file uses seccomp and we should make a ctf challenge out of it.

Okay we know what to do

http://ref.x86asm.net/coder64.html

There is help


$ objdump -M intel -S weirdbinary
weirdbinary:     file format elf64-x86-64

Disassembly of section .init:

0000000000001000 <_init>:
    1000:	f3 0f 1e fa          	endbr64
    1004:	48 83 ec 08          	sub    rsp,0x8
    1008:	48 8b 05 c1 2f 00 00 	mov    rax,QWORD PTR [rip+0x2fc1]        # 3fd0 <__gmon_start__@Base>
    100f:	48 85 c0             	test   rax,rax
    1012:	74 02                	je     1016 <_init+0x16>
    1014:	ff d0                	call   rax
    1016:	48 83 c4 08          	add    rsp,0x8
    101a:	c3                   	ret

Disassembly of section .plt:

0000000000001020 :
    1020:	ff 35 ca 2f 00 00    	push   QWORD PTR [rip+0x2fca]        # 3ff0 <_GLOBAL_OFFSET_TABLE_+0x8>
                    

If you need an assembly refresher 🧋

OST 2 - Architecture 1001: x86-64 Assembly

Can objdump lie to you

Yes, but very rarely.

data and code can be mixed intentionally to break it

Decompilers

  • Ghidra
  • Binary Ninja
  • IDA

Decompilers lie all the time

objdump cheat sheet

the dynamic approach

gdb -ex 'set disassembly-flavor intel' weirdbinary

You probably want to use Pwndbg or GEF

Tipps and Tricks

Function Command
r args run the program
starti arg Run the program and break on first instruction
break expr Break at the given address or symbol
watch expr Break when a value is written to the given address
rwatch expr Break when a value is read from the given address
continue continue program execution
si and ni step into and step over

Examine Memory

  • x/<amount><format><size> <expr>
  • Amount is the number of things to read
  • Format means how the data should be printed most notably x, a, s for hex, addresses, and strings
  • The size means what size the data has b, h, w, g for 1, 2, 4, 8 bytes respectively
  • Expression can be (almost) any C expression

Further Reading

Processor ISA Manuals

Gdb and Pwndbg documentation

Ghidra Book

ost2.fyi

Challenges

Source Aron Hodel (flagbot)

cdn.spclr.ch/babyrev.zip

cdn.spclr.ch/icee.zip ncat --ssl icee.spclr.ch (port 31337)

cdn.spclr.ch/collateral.zip

Source foxtrot, ubuntor (PPP)

plaidctf.com/challenge/11 (Hard)