# XOR HexFlare-On 2017 Challenge 3 Solution

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106``` ```#!/usr/bin/env python2 ''' Author: @XOR_Hex #flareon4 Challenge 3 ' Three parts ' 0) Dump the ASM from IDA, Start @ 0x40107C and the length is 0x79 (see box @ loc_401029) ' bytes = idaapi.get_many_bytes(0x40107C, 0x79) ' with open('/home/firefly/buffer.asm', 'wb') as f: ' f.write(bytes) ' 1) Figure out what byte the code needs to be xor'ed with. Last word must match 0xfb5eL (loop at 401039, comparison check: 40105E) ' Code to be xor'ed and add'ed is located at 40107C and the length is 0x79 (see box @ loc_401029) ' To determine the input byte the modified (xord + add) bytes have to be passed through sub_4011E6 before the comparison check can be done ' 2) After the check passes the state memory from angr needs to be interpreted as code, so use capstone ' The op_str from capstone contains what looks like ASCII characters... ' 3) Take the capstone output and interpret the ASCII codes to get the flag ''' import angr import sys p = angr.Project('greek_to_me.exe', load_options={'auto_load_libs': False}) f2 = None # Interate through all of the possible byte values to find the correct "user" input to de-mask the flag for buf in xrange(0x100): print("Trying buf = {0}".format(buf)) # Variable to store the bits written to disk using IDA asm = None # Store the output from the first de-obfuscation routine b2 = [] # Read in bytes written to file from IDA with open('greek_to_me_buffer.asm', 'rb') as f: asm = f.read() # Re-implement loc_401039 dl = buf for byte in asm: bl = ord(byte) bl = bl ^ dl bl = bl & 0xff bl = bl + 0x22 bl = bl & 0xff b2.append(bl) # Set up angr to "run" sub_4011E6 s = p.factory.blank_state(addr=0x4011E6) s.mem[s.regs.esp+4:].dword = 1 # Angr memory location to hold the xor'ed and add'ed bytes s.mem[s.regs.esp+8:].dword = 0x79 # Length of ASM # Copy bytes output from loc_401039 into address 0x1 so Angr can run it asm = ''.join(map(lambda x: chr(x), b2)) s.memory.store(1, s.se.BVV(int(asm.encode('hex'), 16), 0x79 * 8 )) # Create a simulation manager... #import pdb; pdb.set_trace() simgr = p.factory.simulation_manager(s) # Tell Angr where to go, though there is only one way through this function, # we just need to stop after ax is set simgr.explore(find=0x401268) # Once ax is set, check to see if the value in ax matches the comparison value for found in simgr.found: #import pdb; pdb.set_trace() print(' ax = %s' % hex(found.state.solver.eval(found.state.regs.ax))) # Comparison check if hex(found.state.solver.eval(found.state.regs.ax)) == '0xfb5eL': # Upon success, dump the asm code = ("%x" % found.state.solver.eval_upto(found.state.memory.load(1, 0x79), 1)).decode('hex') print('\n Winner is: {0}\n\n'.format(buf)) print(' %s' % code) bl = None dl = None flag = [] # Using capstone, interpret the ASM from capstone import * md = Cs(CS_ARCH_X86, CS_MODE_32) for i in md.disasm(code, 0x1000): flag_char = None # The if statements do the work of interpreting the ASCII codes to their value counterpart if i.op_str.split(',').startswith("byte ptr"): flag_char = chr(long(i.op_str.split(','), 16)) if i.op_str.split(',').startswith('bl'): bl = chr(long(i.op_str.split(','), 16)) if i.op_str.split(',').startswith('dl'): dl = chr(long(i.op_str.split(','), 16)) if i.op_str.split(',').strip() == 'dl': flag_char = dl if i.op_str.split(',').strip() == 'bl': flag_char = bl if (flag_char): flag.append(flag_char.strip()) print(" 0x%x\t%s\t%s\t%s" %(i.address, i.mnemonic, i.op_str, flag_char)) print('\n\n') print(''.join(flag)) sys.exit(0) ```