본문 바로가기
pwnable.kr/[Toddler's Bottle]

[pwnable.kr Toddler's Bottle] bof - write up

by 안다니. 2020. 3. 26.
반응형

bof(버퍼오버플로우)

  버퍼오버플로우에 대한 문제인 것 같다. 하지만 이전과의 문제와는 다르게, bof파일과 bof.c파일이 따로있고 Running at이 있는 걸로 보아 소스코드와 실행파일로 문제를 해결하고 그 값을 저기 nc로 던져주면 문제가 해결 될 것으로 보인다. 그럼 소스코드를 먼저 보도록 하자.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
	char overflowme[32];
	printf("overflow me : ");
	gets(overflowme);	// smash me!
	if(key == 0xcafebabe){
		system("/bin/sh");
	}
	else{
		printf("Nah..\n");
	}
}
int main(int argc, char* argv[]){
	func(0xdeadbeef);
	return 0;
}

  main함수를 먼저 보자면, func함수를 바로 호출하고 인자값을 보낸다. 

  func함수에선, gets(overflowme) 즉 gets함수를 호출하고, char형으로 선언된 32비트인 크기의 변수 overflowme가 들어간다. 그리고 if문은, main함수에서 넘겨준 인자 값과 0xcafebab 값과 같은지 비교하고 같으면 /bin/sh를 실행한다. 

 

  그냥 소스코드만 본다면, 해결 할 수 없다고 생각 할 수 있겠지만, 문제 이름이 bof이며 gets함수는 취약점이 있다. gets는 입력하는 사람이 엔터를 입력하는 동안에 입력을 받습니다. 그러니, char overflowme[32] 라고 되어있지만, 이 이상의 값을 입력을 받을 수 있고, 만약 입력을 받게 된다면 버퍼 오버플로우가 발생하게 됩니다. (친절하게 주석에 smah me ! 라고 되어있습니다)

 

  그럼 문제를 해결하러 가보겠습니다.

 

gets 함수의 사용 위험을 알려준다

  먼저 gcc로 컴파일 했을 때 친절하게 gets 사용은 위험하다고 알려주고 있습니다. 그럼 gdb로 까보도록 하겠습니다.

eax 레지스트리 값

  먼저 break 포인트를 잡고 run 했다. break포인트는 입력을 받고 나서(gets함수) 걸었으며 입력은 AAAAAAAA을 했다.

위의 사진을 보면 입력한 값은 eax 레지스트리에 전달이 된다.

gdb disas func

반응형

 

  먼저 <+50> 부분을 보면, ebp-0x2c의 위치의 값에 입력한 값이 들어가고, 우리가 비교할 0xcafebabe는 ebp+0x8에 위치하고 있다고 해석이 된다. 

  0x2c 의 값은 10진수로 44이다.  그러면 ebp-44 에서 ebp+8로 가기 위해서는 총 52라는 값이 필요하다. 이 52비트가 입력 받은 값과, 비교해야 되는 값의 거리라고 판단된다. 그러면 gets함수는 사용자가 엔터를 치기 전까지 값을 입력 받는다고 했다. 바로 실습을 해보자!

 

bof 문제 해결

 

  먼저 python의 print 함수를 사용해 값을 전달했다. 괄호 안에 마지막 cat을 넣은 이유는, 값을 전달하고 나서 stdin을 유지하기 위해서 넣었다. 그리고 위에 소스코드에 0xcafebabe 값과 비교한다고 했는데, 전달 한 인자값을 보자면 순서가 반대로 되어있다. 이는 리틀엔디언과 빅엔디언이 있는데 잠깐 설명을 넣자면,

빅엔디언 리틀엔디언

  빅 엔디언은 소프트웨어의 디버그를 편하게 해 주는 경향이 있다. 사람이 숫자를 읽고 쓰는 방법과 같기 때문에 디버깅 과정에서 메모리의 값을 보기 편한데, 예를 들어 0x59654148은 빅 엔디언으로 59 65 41 48로 표현된다.

  반대로 리틀 엔디언은 메모리에 저장된 값의 하위 바이트들만 사용할 때 별도의 계산이 필요 없다는 장점이 있다.

 예를 들어, 32비트 숫자인 0x2A는 리틀 엔디언으로 표현하면 2A 00 00 00이 되는데, 이 표현에서 앞의 두 바이트 또는 한 바이트만 떼어 내면 하위 16비트 또는 8비트를 바로 얻을 수 있다.

 반면 32비트 빅 엔디언 환경에서는 하위 16비트나 8비트 값을 얻기 위해서는 변수 주소에 2바이트 또는 3바이트를 더해야 한다. 보통 변수의 첫 바이트를 그 변수의 주소로 삼기 때문에 이런 성질은 종종 프로그래밍을 편하게 하는 반면, 리틀 엔디언 환경의 프로그래머가 빅 엔디언 환경에서 종종 실수를 일으키는 한 이유이기도 하다.   -출처 위키백과

  

elf파일인 경우 readelf -h 파일명으로 파악이 가능하다.

설명사진

반응형

댓글