Skip to content

Instantly share code, notes, and snippets.

@masthoon
Created May 25, 2020 19:42
Show Gist options
  • Save masthoon/77af72e30b9a6440f605f45594d45a16 to your computer and use it in GitHub Desktop.
Save masthoon/77af72e30b9a6440f605f45594d45a16 to your computer and use it in GitHub Desktop.
Sun? On my Sat? unicorn sparc emulation
import sys
import lief # py2.7 v0.9.0
from unicorn import *
from unicorn.sparc_const import *
import capstone
'''
Sample output with mu.input = FLAG_EXPLOIT_CMD:
puts(Configuration Server: Running)
sleep()
read(1) = 0
..
read(1) = 0
[MAIN] ParseCommand('0b00070346c8')
[NEGOCIATE_HEADER] CRC8('46c83dde0001f9', 7)
[NEGOCIATE_HEADER] Expected CRC8 = 0x3 vs Current CRC8 0x3
[NEGOCIATE_HEADER] Expected cookie = 0x46c83dde vs Current cookie = 0x46c83dde
[NEGOCIATE_HEADER] Expected MSGID = 0x1 vs Current MSGID 0x1
[MAIN] ParseCommand('030100000000')
[GET_INFO] ClipStrIdx(0x0)
[GET_INFO] ClipStrIdx returned 0x3
puts(FLAG{No, It's not in the firmware, that would have been too easy. But in the patched version it will be located here, so good that should help...?})
puts(ACK)
'''
def disasm(address, opcode):
md = capstone.Cs(capstone.CS_ARCH_SPARC, capstone.CS_MODE_BIG_ENDIAN)
for instruction in md.disasm(opcode, address):
return "%s\t%s" %(instruction.mnemonic, instruction.op_str)
START = 0x4000145c # Init function address
STACK = 0x4001d150
BASE = 0x40000000
page_align = lambda s: (s + 0x1000) & ~(0x1000-1)
mu = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN)
mu.hooks = {}
mu.breakpoints = {}
mu.removed_bp = []
# Map memory for this emulation.
elf = lief.parse('test.elf') # FROM https://quals.2020.hackasat.com/scoreboard/challenge/22
for seg in elf.segments:
mu.mem_map(seg.virtual_address, page_align(seg.virtual_size))
content = bytes(bytearray(seg.content[:]))
mu.mem_write(seg.virtual_address, content)
# Input
FLAG_EXPLOIT_CMD = '0f0b00070346c83dde0001f9030100' # read write-up :D sun.satellitesabove.me:5043
GET_FAKE_FLAG_CMD = '0e0a00069546c83dde0001030300'
GET_INFO_CMD = '0e0a00069546c83dde0001030101'
# No input will use dynamic input (stdin)
# mu.input = list(FLAG_EXPLOIT_CMD)
mu.input = list('')
# Helpers
def read_string(mu, at):
s = ''
while 1:
b = mu.mem_read(at, 1)
at += 1
if b == '\0':
return s
s += b
return s
def breakpoint(mu, at, handler):
old = mu.mem_read(at, 4)
mu.breakpoints[at] = (handler, old)
mu.mem_write(at, b'\x91\xD0\x20\x01') # ta 1
def hook_and_ret(mu, at, handler):
mu.mem_write(at, b'\x91\xD0\x20\x00') # ta 0
mu.hooks[at] = handler
def print_ctx(mu):
pc = mu.reg_read(UC_SPARC_REG_PC)
print("IP {:08x} :: {}".format(pc, disasm(pc, mu.mem_read(pc, 20))))
def hook_exception(mu, intno, user_data):
pc = mu.reg_read(UC_SPARC_REG_PC)
# Re-enable removed breakpoint
for reset_bp in mu.removed_bp:
mu.mem_write(reset_bp, b'\x91\xD0\x20\x01') # ta 1
if intno == 128: # hook_and_ret
if pc in mu.hooks:
mu.hooks[pc](mu)
mu.reg_write(UC_SPARC_REG_PC, 0x4000325C) # gadget ret;nop
elif intno == 129: # breakpoint
if pc in mu.breakpoints:
(handler, old) = mu.breakpoints[pc]
handler(mu)
mu.mem_write(pc, bytes(old))
mu.removed_bp.append(pc)
else: # unhandled exception
print_ctx(mu)
return 0
# libc hooks
def puts_hook(mu):
arg0 = mu.reg_read(UC_SPARC_REG_O0)
print('puts({})'.format(read_string(mu, arg0)))
def sleep_hook(mu):
print('sleep()')
def printf_hook(mu):
arg0 = mu.reg_read(UC_SPARC_REG_O0)
s = read_string(mu, arg0)
if '%s' in s:
arg1 = mu.reg_read(UC_SPARC_REG_O1)
s = str(s) % str(read_string(mu, arg1))
s = s.replace('\n', '\\n')
print('printf({})'.format(s))
def read_hook(mu):
address = mu.reg_read(UC_SPARC_REG_O1)
count = mu.reg_read(UC_SPARC_REG_O2)
if not mu.input:
read = raw_input('read({}) = '.format(count))
else:
read = ''
for _ in range(count):
read += mu.input.pop(0)
print('read({}) = {}'.format(count, read))
mu.mem_write(address, read)
mu.reg_write(UC_SPARC_REG_O0, count)
def exit_hook(mu):
print('exit')
exit()
# Setup libc hooks
hook_and_ret(mu, 0x40011EBC, puts_hook)
hook_and_ret(mu, 0x40011FDC, sleep_hook)
hook_and_ret(mu, 0x40011D84, printf_hook)
hook_and_ret(mu, 0x40003C9C, read_hook)
hook_and_ret(mu, 0x40011860, exit_hook)
# Breakpoints
# NEGOCIATE_HEADER cookie, crc8 amd msgid checks
def header_compare_cookie(mu):
g1 = mu.reg_read(UC_SPARC_REG_G1)
g2 = mu.reg_read(UC_SPARC_REG_G2)
print('[NEGOCIATE_HEADER] Expected cookie = 0x{:x} vs Current cookie = 0x{:x}'.format(g1, g2))
breakpoint(mu, 0x40001280, header_compare_cookie)
def header_crc8_entry(mu):
buffer = mu.reg_read(UC_SPARC_REG_O0)
count = mu.reg_read(UC_SPARC_REG_O1)
print("[NEGOCIATE_HEADER] CRC8('{}', {})".format(str(mu.mem_read(buffer, count)).encode('hex'), count))
breakpoint(mu, 0x400015EC, header_crc8_entry)
def header_crc8_end(mu):
expected = mu.reg_read(UC_SPARC_REG_O0)
current = mu.reg_read(UC_SPARC_REG_L6)
print("[NEGOCIATE_HEADER] Expected CRC8 = 0x{:x} vs Current CRC8 0x{:x}".format(expected, current))
breakpoint(mu, 0x400016A8, header_crc8_end)
def header_compare_message_id(mu):
msgid = mu.reg_read(UC_SPARC_REG_O0)
g1 = mu.reg_read(UC_SPARC_REG_G1)
print("[NEGOCIATE_HEADER] Expected MSGID = 0x{:x} vs Current MSGID 0x{:x}".format(g1, msgid))
breakpoint(mu, 0x400012A8, header_compare_message_id)
# MAIN new command debugging
def main_on_new_subcommand(mu):
msg = mu.reg_read(UC_SPARC_REG_I0)
print("[MAIN] ParseCommand('{}')".format(str(mu.mem_read(msg, 6)).encode('hex')))
breakpoint(mu, 0x400013B8, main_on_new_subcommand)
# GET_INFO Vulnerable function clipStrIdx input+output
def get_info_clip_idx_entry(mu):
info_id = mu.reg_read(UC_SPARC_REG_I0)
print("[GET_INFO] ClipStrIdx(0x{:x})".format(info_id))
breakpoint(mu, 0x400016C4, get_info_clip_idx_entry)
def get_info_clip_idx_end(mu):
info_id = mu.reg_read(UC_SPARC_REG_O0)
print("[GET_INFO] ClipStrIdx returned 0x{:x} ".format(info_id))
# mu.reg_write(UC_SPARC_REG_O0, 3) for flag
breakpoint(mu, 0x400012F0, get_info_clip_idx_end)
# Setup registers and intr hook
mu.reg_write(UC_SPARC_REG_SP, STACK)
mu.reg_write(UC_SPARC_REG_PC, START)
mu.hook_add(UC_HOOK_INTR, hook_exception)
# GO GO GADGET
# single step doesn't work on SPARC unicorn/issues/631 ...
mu.emu_start(START, 2000)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment