본문으로 바로가기

MMA CTF 2nd 2016 diary Write Up

category 해킹/Write Up 2017. 7. 2. 14:12

0. introduce

6월을 bob 지원에 올인했더니 문제푸는감이 다 사라진듯 싶네요.. 이 문제 푸는데만 거의 5일걸린것 같습니다. bob에 합격했으니 더 분발해야겠습니다. 그리고 제가 저번에 샌드박스를 한번 본적은 있었는데 이런식으로 익스문제에 샌드박스가 걸린문제를 처음으로 풀어본것 같습니다. 바로 풀지는 못했고 라업을 참고해서 풀었습니다. 그래서 알게된것을 정리해보려고 합니다.


PRCTL - 실행중 system call들을 제한하는것.

int prctl(int option, unsigned long arg2, unsigned long arg3,

unsigned long arg4, unsigned long arg5);

정의 되있는 option = http://elixir.free-electrons.com/linux/latest/source/include/uapi/linux/prctl.h

option 설명 = http://man7.org/linux/man-pages/man2/prctl.2.html


64bit binary에서 32bit용 shellcode가 안먹히는 이유 -> 32bit에서는 esp를 사용하는데 64bit는 rsp라서 복사할때 값이 깨짐


1.  binary

pie가 없어서 분석은 편했다. (그런데 5일이나..)


메뉴도 매우 간단하다.

1. Register

- (1970/01/01 ~ 2016/12/31)의 날짜를 입력받는다.

-  content size를 입력받는다.

- size만큼 내용을 입력받는다.


2. Show

- 날짜를 입력하면 그에 맞는 내용을 보여준다.


3. Delete

- 날짜에 맞는 chunk를 free한다.


info_chunk : [yyyy(4byte) + mm(3byte) + dd(1byte)] + [*content(8byte)] + [*fd(8byte)] + [*bk(8byte)]


하지만 custom free와 malloc을 사용하고 있었다.

free ----

이걸 분석하는데 좀 오래걸렸다. 나는 아직 free에 구조에 대해 잘 모르고있었던것 같다. 이문제를 풀어보면서 free에 unlink가 어떻게 진행되는지 더 자세히 알 수 있게 되었다.

custom free에 unlink는 다음과같다.

- 현재 chunk의 prev_inuse bit를 확인해서 0이면 이전 청크와 unlink를 진행한다.

- 다다음 chunk의 prev_inuse bit가 1인지 확인하고 1이라면 0으로 바꾸고 다음 chunk와 unlink를 진행한다.


예를 들어서 설명하자면 아래와 같다.


위 그림중 1이 free_chunk라고 가정하였다.

1 . (3)의 prev_inuse bit를 확인하여 (1)이 free가 안되있는 chunk가 맞는지 판단한다.

2. (4)의 prev_inuse bit를 확인하여 (5)가 free가 안되있는 chunk인지 판단한다.

2-1 . 여기서 (5)가 free된 chunk라면 (1)과 (5)를 unlink 한다.

3. (2)의 prev_inuse bit를 확인하여 (3)이 free가 안되있는 chunk인지 판단한다.

3-1. 여기서 (3)이 free된 chunk라면 (1)과 (3)을 unlink한다.

4. 모든 과정을 거쳐서 나온 chunk를 free_list에 추가한다.


2. Vulnerability

취약점은 heap off-by-one 이다. content를 입력받을때 데이터의 끝에 null을 입력하는데, size만큼 data를 넣으면 마지막 data값 다음에 null을 넣을 수 있다.


3. Exploit

exploit 전에 알아둬야 할것이 있다.

heap영역을 임의로 할당해줄때 권한을 7로줘서 쉘코드의 실행이 가능하도록 한다.

 

그리고 init_seccomp 함수에서 prctl로 호출가능한 syscall을 제한한다. 하지만 32bit에 int 0x80을 막진 않았으므로 32bit로 쉘코드를 작성하면된다. (문제에서 ./bash로 쉘을 띄울 수 있다고 말해줬다.)


그렇다면 총 해야하는것은 다음과 같이 3가지이다.

1) heap_add_leak

2) exit_got overwrite using unlink

3) bypass sandbox


1) heap_add_leak

이부분이 가장 까다롭다.

1. off_by_one으로 첫번째와 같은 상태로 heap을 세팅한다. 

2. 그 상태로 unlink해주면 chunk data두개가 합쳐지면서 heap_add가 남게된다.

3. 두번째 (주황색)chunk_data는 출력할 수 있으므로 출력하여 leak할 수 있다. 


말로하면 간단한데 실제 익스를 짜보면 꽤 힘들다.


2) exit_got overwrite using unlink

이부분은 쉽다. 처음에 손퍼징으로 unlink가 발생하여 원하는 위치에 값을 덮을 수 있다는것을 알았다.

다음과 같이 heap이 정돈되있다고 할 때, off_by_one으로 보라색 info의 prev_inuse bit를 0으로 만들어주면 초록색 chunk_data 가 free된것처럼 인식하여 data를 unlink해주는데 이때 data는 chunk_info가 아니므로 data값에 unlink연산을 한다. 그래서 fd, bk를 조작하여 unlink할 수 있다.


3) bypass sandbox

sandbox 우회부터는 도저히 모르겠어서 라업을 참고했다. 방법은 32bit의 execve는 막혀있지 않으므로 그걸 사용해서 쉘을 획득하는것이였다. 대회때는 특정 syscall이 제한된 bash쉘을 바이너리랑 같은 폴더에 둬서 bash를 실행하여 파일명으로 flag를 얻게 했지만 지금은 그런 bash 쉘이 없으므로 64bit bsah쉘을 두고 gdb에서 쉘이 실행되는것만 확인하였다. 쉘코드는 직접 만들었다.


Full Exploit

(https://github.com/LYoungJoo/CTF-Write-Up/blob/master/2016_mmactf%20Write%20Up/diary.py)


'해킹 > Write Up' 카테고리의 다른 글

Pwnabletw seethefile Write Up  (0) 2017.10.01
Dimigo CTF 2017 간단 Write Up  (0) 2017.07.18
HITCON 2016 babyheap Write Up  (0) 2017.06.10
RCTF 2017 aiRcraft Write Up  (0) 2017.06.04
SSG CTF 학생부 4위 Write Up  (0) 2017.06.04