본문으로 바로가기

9447CTF 2015 serach engine Write Up

category 해킹/Write Up 2017. 2. 19. 01:55

0. Introduce

how2heap문서에서 fastbin_dup_into_stack부분에 예시 ctf문제로 나와있던 문제입니다.

라업을 참고해도 머리가 아플정도로 어려웠지만 재미있었던 문제였습니다.

라업도 다른문제보다 적고 문제푸는데만 16시간정도 잡고있었는데 결국 풀어서 정말 좋습니다.

(영어로된 Write Up밖에없고 번역기가 번역을 제대로 못해서 직접 읽어봤는데 은근 해석이 잘되서 기분이 좋았습니다)


1. Binary

Canary가 걸려있고 NX가 걸려있다. 실제로 익스할때 NX가 많이 걸려서 꼭 NX인걸 생각하고 익스를 해야한다.


바이너리는 생각보다 간단하다. 세가지의 메뉴로 구성되어있으며 단어 검색, 문장 추가, 나가기가 있다.

문장을 추가할때는 문장의 크기를 입력받고 그만큼 입력받는다. 검색기능도 마찬가지로 단어의 크기를 입력받고 그만큼 입력 후 검색한다.

검색후에는 그 문장을 지울것인지 지우지 않을것인지를 묻는 문장이나오며(y/n) y를 선택하게되면 문장이 지워진다.

이것은 단어이기때문에 여러개가 검색되거나 지울 수 있다.


2.  Vulnerability & Exploit

취약점은 Fastbin DFB를 이용해야한다. 그러긴 위해서 다음과같은 과정을 거쳐야한다.

1. return_add leak

2. system_add leak & /bin/sh add leak

3. use the double free to overwrite return_add to a call system("/bin/sh")


1) return_add leak

처음에 메뉴를 입력받는 부분의 함수이다. (sub_400a40은 자기자신) 이 함수에서 처음 입력을 하고 재귀호출을 이용해 자신을 호출할때 메모리를 확인해보면 second_buf+48 위치에 이전 버퍼의 주소가 남아있는것을 볼 수 있다.

따라서 두번째 입력에서 dummy(48)을 넣어주게 된다면 처음에 입력을 받을때 사용했던 버퍼의 주소를 알 수있다.

처음 버퍼를 확인해보면 first_buf+82 위치에 return_add가 있는것을 확인할 수 있다.


2) system_add leak & /bin/sh add leak

결국 libc_base leak이 필요한건데 재미있게 leak을 할 수 있다.

바로 small bin을 이용하는것이다. fastbin이외에 다른 bin들은 free가 되고나면 원래 data가 위치하던 영역에 fd와 bk가 남게되는데 bin에 fd나 bk가 다른 chunk와 연결되어있지 않은 경우에 libc의 특정부분 주소가 <확인해보면 (main_area+86) 이라고 나옴> 남게된다. 그것을 leak해서 libc_base를 구하면 된다.

다음 함수를 보면 문자열을 비교할 때 단어별로 memcmp로 비교하는데 이점을 이용해야 한다.

sentence_A (size : 512) = [AAAA~~ B]

이 A문장을 넣고 B를 검색해서 지워준뒤에 '\x00'을 검색한다면 삭제된 문장을 검색하는것이기 때문에 다시 전체문장을 읽을 수 있다.

( 문장이 free된 이후에 fd, bk가 존재하므로 이것을 data라고 착각해서 단어를 검색할 수 있게함 )

이렇게해서 libc_base를 구하고 각각의 offset을 더해서 system과 /bin/sh의 주소를 구한다.


3.  use the double free to overwrite return_add to a call system("/bin/sh")

double free는 삭제된 문장을 검색해서 두번 문장을 지울 때 발생한다.

index a sentence로 문장을 추가했을 때 heap구조를 확인해보면 다음과 같다.

prev_size(8) size(8) data(?) size(8) word1_add(8) word1_size(8) data_add(8)  data_size(8) ...

이런식의 구조로 하나의 문장 chunk와 그 문장에서의 단어들의 정보를 나타내는 chunk로 나뉜다. 그리고 모든 단어들의 포인터는 0x6020b8에 있는 전역변수로 관리된다. 그래서 검색할때는 문장chunk는 쓰이지않고 단어들의 chunk만 사용된다.

여기서 Double Free를 할 때 주의점은 fastbin은 fd 밖에 존재하지않고 Head -> A -> B -> NULL 이런식의 구조라는것을 알아야한다.

그래서 small bin과는 다르게 double free를 하려면 3개를 동적할당해서 두번씩 지워줘야 한다.

sentence_A (size : 56) = [AAAA~~ D]

sentence_B (size : 56) = [AAAA~~ D]

sentence_C (size : 56) = [AAAA~~ D]

크기를 56으로 선정한 이유는 fastbin에 들어가있어야 하면서 문장을 추가할때 입력받는 문장이나 단어들의 동적할당시 bin의 위치가 겹치지 않게 하기 위해서이다. 위와같이 3개의 문장을 만들고 D를 지워서 fastbin을 Head -> A -> B -> C -> NULL형태로 만들어준다. 그리고 다시 "\x00"을 검색해서 처음 검색된것만 지워주면 Head -> B -> A -> B -> ...형태로 fastbin을 만들 수 있다.

그렇다면 fd를 ret으로 조작해서 ret에 원하는 내용을 쓰는것이 가능해진다. ret에 덮어 씌워질 내용은 다음과같다.

pop_rdi_add + '/bin/sh'_add +  system_add

이렇게 쉘을 획득할 수 있다.


Full Exploit Code

https://github.com/LYoungJoo/CTF-Write-Up/blob/master/2015_9447ctf%20Write%20Up/search%20engine.py