본문으로 바로가기

해커스쿨 LOB giant 풀이, write up

category Wargame/LOB 2018. 9. 16. 15:58

giant --- one step closer


문제 풀기 전에 아래의 명령어를 반드시 쳐야한다.


$export SHELL=/bin/bash2

$/bin/bash2


우선 giant의 디렉터리를 확인한다.






assassin이라는 의심스러운 파일이 존재한다. assassin은 assassin의 권한을 가지고 있고, setuid가 걸려있는 파일이다.



assassin.c를 가지고 assassin elf 파일을 만들었을 가능성이 있기 때문에, assassin.c 파일을 본 뒤 어떤 취약점을 가지고 있는지 확인해보자.





지역변수를 선언한다.



1
char buffer[40];                                                                    
cs



인자의 개수가 없으면 "argv error\n"를 출력하고 종료한다.



1
2
3
4
5
if(argc < 2)
{
    printf("argv error\n");                                                            
    exit(0);
}
cs



첫 번째 인자의 48번 째 문자가 '\xbf'이면 "stack retbayed you!\n"를 출력하고 종료한다.



1
2
3
4
5
if(argv[1][47== '\xbf')
{
    printf("stack retbayed you!\n");                                                
    exit(0);
}
cs




첫 번째 인자의 48번 째 문자가 '\x40'이면 "library retbayed you, too!!"를 출력하고 종료한다.



1
2
3
4
5
if(argv[1][47== '\x40')
{
    printf("library retbayed you, too!!\n");                                                
    exit(0);
}
cs



첫 번째 인자를 buffer에 저장하고 출력한다.



1
2
strcpy(buffer, argv[1]);                                                            
printf("%s\n", buffer);
cs



buffer와 SFP를 0으로 padding 한다.



1
2
// buffer+sfp hunter
memset(buffer, 044);                                                                
cs




assassin의 메모리 구조를 예상해보면 아래와 같이 그릴 수 있다.





실제 변수가 할당될 때, gcc가 최적화를 위해 자동적으로 dummy를 생성할 수 있다.


gdb를 이용하여, assassin을 분석해보자. assassin을 gdb로 실행하는 도중 permission denied가 발생하면 cp 명령을 이용해 파일을 복사하면 된다. 현재 user의 권한으로 똑같은 파일을 가질 수 있기 때문이다.





위와 같이 assassincp가 giant의 권한으로 복사된 것을 확인할 수 있다.


dummy가 생성되었는지 gdb를 통해 확인해보자.






dummy가 추가적으로 생성되지 않고 정확히 40(0x28) byte가 생성된것을 확인할 수 있다.


assassin.c를 보면 no stack, no RTL 이라는 힌트를 주었다.


소스에도 마찬가지로 스택 영역과, library 영역을 사용하지 못하도록 하였다.

또한 memset을 이용해 buffer와 SFP를 0으로 모두 초기화 하기 때문에 buffer의 공간도 사용할 수 없다.


우리가 집중해야하는 곳은 바로 함수의 에필로그 부분이다.


함수의 에필로그는 두 개의 어셈블리 명령어로 이루어져 있다.


leave 

ret


leave와 ret은 각각 두개의 어셈블리 명령을 수행하는 명령어이다.


leave 


mov esp, ebp

pop ebp


ret


pop eip

jmp eip



ret 명령어는 현재 스택의 값을 pop을 하여 eip에 저장하고 eip로 점프한다.

만일 ret, ret 처럼 연속적으로 두 번 사용하면 아래와 같이 명령어가 나열될 것이다.


pop eip

jmp eip


pop eip

jmp eip


첫 번째 ret은 두 번째 ret에 의해 의미 없게 된다.

pop 할 때 esp가 자동적으로 +4 가 된다는 것을 당연히 알고 있는 사실이다.


44byte의 dummy + &ret의 주소 + 쉘코드의 주소


이번에는 쉘코드의 주소를 argv[2]를 이용해 쉘코드를 전달할 것이다.



스택에 ebp가 push되고 나서 이후의 스택값을 들여다보면


EIP, argc, argv[]가 순서대로 스택에 있다.


argv[]의 주소는 0xbffffb44이기 때문에 배열에 인자를 보면 쉘코드가 적재될 위치를 확인할 수 있다.





payload를 작성해보면 아래와 같이 작성할 수 있다.


44byte의 dummy + &ret의 주소 + 쉘코드의 주소


성공률을 높이기 위해 쉘코드의 주소 앞에 NOP padding을 진행했다.




위와 같은 식으로 공격을 진행한다.