warmheap

해당 바이너리는 64비트며 보호기법은 nx만 설정되어 있다.


바이너리를 실행하면

두번의 입력을 받고 종료된다.


심볼이 없어서 gdb로 함수를 열어볼 수 없다.


따라서 ida로 봐야했다.


main()함수의 헥스레이다.


v3에 16바이트 크기를 malloc으로 동적할당을 해준다.

그리고 *v3에 1을 넣는다.

그 다음 *(QWORD(v3) + 1)에 또다시 8바이트를 동적할당한다. v3가 가리키는 곳에서 한칸(8바이트) 추가된 위치에 할당을 해주는 것이다.

그리고 v4에 16바이트를 할당한 후 위와 비슷하게 *v4에 2를 넣는다.  그 다음 *(QWORT(v4) + 1)위치에 8바이트를 동적할당한다.

그리고 s라는 변수에 4096바이트를 fgets()를 통해 입력받고 strcpy()로 (v3+1)에 복사한다.

다음에도 마찬가지로 s에 4096바이트를 입력받고 strcpy()로 *(v4+1)에 그 값을 넣는다.

그리고 exit()함수로 바이너리를 종료한다.


우선 메인만 봤을 때는 main에서 다른함수로 jump하는 것은 없어서 이게 전부의 기능인듯싶으나 ida로 살펴보면 여러 함수가 많이 있다.

그 중 문제풀이와 관련될 것 같이 생긴 함수가 있어서 분석했다.

sub_400826()함수인데 보면 flag라는 파일을 읽어서 출력해주는 역할을 한다.

메인에서 이 함수로 넘어올 수 있는 방법은 eip를 조작하는 방법 말고는 없어보인다.


처음엔 어떻게 해야할지 막막해서 이전에 풀었던 문제들 중 힙에 대해 찾아보다가 알고보니 protostar excercise heap2와 너무 유사한 문제였다.


바이너리를 차근차근 짚어보면 쉽다.

우선 메인함수의 주소는 0x4008a8다.

x/52i를 하면 main의 어셈코드를 전부 출력할 수 있다.

여기서 bp를 적절히 주었다.

첫번째 fgets에 bp를 걸고 A 8바이트를 줬다.



일단 $rbp-0x1020 = v3 의 스택 상태를 확인해보자.

보면 $rbp-0x1020 에는 malloc(16)한 주소가 *v3에 있다. 그리고 $rbp-0x1018에는 *v4에 동적할당한 주소가 있다.

그리고 이제 strcpy(*((char **)v3 + 1), &s); 를 실행시킨 후 스택과 힙을 봐야한다.


*(v3 + 1)에 stdin이 복사되므로 *v4는 0x602010이므로 *(v3+1)은 8바이트가 더해진 0x602018이다. 즉 0x602018에 있는 주소에 stdin을 복사한다.

예상했던대로 0x602030에 아까 입력한 A 8바이트가 복사되었다.


그리고 그 다음 fgets에서 B를 8바이트 입력해봤다.

stdin에 B가 8바이트가 들어간 것을 확인할 수 있다.


이제 strcpy(*((char **)v4 + 1), &s); 를 확인해보자.

먼저 v4+1의 위치를 보면 

0x602070이다.

0x00602010에서부터 메모리를 확인해보면 

아까 확인했듯이 *v4는 0x602050이다.

즉 *(v4 + 1)은 0x602058인데 그 위치에 0x602070이 있다. 이 주소에 stdin을 넣는데.. 0x602030부터 overflow로 0x602058까지 덮어서 원하는 주소를 입력하면 eip를 조작할 수 있다.


eip를 조작해서 sub_400826()함수로 jump하면 된다.


다행히 이 바이너리의 마지막은 exit()로 끝나기 때문에 exit@got에 sub_400826()함수의 주소인 0x400826를 덮어버리면 바이너리 마지막 단계에서 sub_400826()함수를 실행하면서 flag를 읽어낼 수 있다.

0x602030부터 0x602054까지 40바이트가 차이가 나니 첫번째 fgets에서 dummy40바이트에 나머지 8바이트를 exit@got의 주소를 넣고 두번째 fgets에서 0x400826를 넣으면 익스가 된다.


exploit code

from pwn import *

exit_got = 0x601068
exploit_addr = 0x400826

r = process("./warmheap")

payload = "A"*40
payload += p64(exit_got)
r.sendline(payload)

r.sendline(p64(exploit_addr))
print r.recv()


'CTF' 카테고리의 다른 글

RCTF 2015 welpwn  (0) 2018.08.01
EasyCTF2017 Simple ROP  (0) 2018.08.01
CodeGate2016 watermelon  (0) 2018.07.26
CodeGate2014 nuclear  (0) 2018.07.22
CodeGate 2014 angry_doraemon  (0) 2018.07.20

+ Recent posts