Created
May 25, 2020 19:42
-
-
Save masthoon/77af72e30b9a6440f605f45594d45a16 to your computer and use it in GitHub Desktop.
Sun? On my Sat? unicorn sparc emulation
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
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