Skip to content

Instantly share code, notes, and snippets.

@vient
Last active May 13, 2017 23:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vient/0d5e0f0a6cc9b2b0b7bcb87be2b599b9 to your computer and use it in GitHub Desktop.
Save vient/0d5e0f0a6cc9b2b0b7bcb87be2b599b9 to your computer and use it in GitHub Desktop.
Solution for Enlightenment task from DEF CON CTF Qualifier 2017
The idea is that in each task the key was checked character by character in the same way.
So we can make a pattern from assembly code and then extract all information with simple re.search()
The first task, Magic, was solved with angr though mainly because I didn't think about regexes in the first way.
There are two details that differ Enlightment from pevoius tasks (subtasks here):
1. All binaries were compiled with another options so all my regexes broke as well as angr solution.
Regexes are easily adjustable, but you can't use these solutions to solve previous tasks anymore.
2. Only in this task keys are sometime reversed. I decided not to find in the binary whether it reverses key or not,
instead I just tried to feed the key to the binary. If return code is not 0 then we need to reverse the key.
Additional notes
Occult solution is overcomplicated with handlers but they help with debugging and make cleaner code.
Witchcraft code is obscure since I mixed all regexes for different operations in one.
Check for 'swift' in subprocess output is there because Swift do not work on my machine and I decided not to spend time on this.
from pwn import *
import re
import angr
import struct
import base64
import subprocess
def MagicSolution(filename):
elf = angr.Project('./' + filename)
state = elf.factory.blank_state(addr=0x40066D)
i = state.se.BVS('i', 80*8)
state.memory.store(0x1000, i)
state.regs.rbx = 0x1000
pg = elf.factory.path_group(state)
pg.explore(find=0x400675)
state = pg.found[0].state
key = state.se.any_str(i).rstrip("\x00")
return key
def SorcerySolution(filename):
with open(filename, 'rb') as f:
q = f.read()
mask1 = r'\x8A\x0F\x80\xF9([\x10-\x7f])'
mask2 = r'\x8A(\x47.\x3C|\x4F.\x80\xF9)([\x10-\x7f])'
key = ''
found = re.search(mask1, q)
key += found.group(1)
prev = 0
while True:
prev += found.start() + 1
found = re.search(mask2, q[prev + 1:], re.DOTALL)
if not found:
break
key += found.group(2)
return key
def AlchemySolution(filename):
with open(filename, 'rb') as f:
q = f.read()
mask2 = r'\x0F\xB6.{1,2}\x48\x83[\xF8\xF9]([\x10-\x7f])'
key = ''
prev = 0
while True:
found = re.search(mask2, q[prev + 1:], re.DOTALL)
if not found:
break
prev += found.start() + 1
key += found.group(1)
return key
def WitchcraftSolution(filename):
with open(filename, 'rb') as f:
q = f.read()
mask = r'\x48(\x81([\xC7\xEF\xFF])(.{4})|\x83([\xC7\xEF\xFF])(.)|\x85\xFF)[\x70-\x80\x0F]'
key = ''
prev = 0
acc = 0
while True:
found = re.search(mask, q[prev + 1:], re.DOTALL)
if not found:
break
prev += found.start() + 1
op = found.group(2) or found.group(4)
if op:
num = ord(found.group(5)) if found.group(5) else struct.unpack('<I', found.group(3))[0]
else:
op = '\xFF'
num = 0
if op == '\xC7':
acc += num
elif op == '\xEF':
acc -= num
else:
if acc:
key += chr((num - acc) & 0xFF)
acc = 0
return key
def OccultSolution(filename):
with open(filename, 'rb') as f:
q = f.read()
mask_arith = r'\x48\x8D\x78\x08\x48\x8D[\x70-\x78](.)\x40'
mask_res = r'\x48\x89\x44\x24\x38\x48(\x81\x78\x08(.{4})|\x83\x78\x08(.))'
mask_index = r'\x48[\x8B\x0B].{4}\0\xB9(.\0\0\0)'
def ArithHandler(found):
num = struct.unpack('<b', found.group(1))[0]
return num
def ResHandler(found):
if found.group(2):
return struct.unpack('<i', found.group(2))[0]
else:
return ord(found.group(3))
def IndexHandler(found):
return struct.unpack('<i', found.group(1))[0]
key = {}
prev = 0
it_arith = re.finditer(mask_arith, q, re.DOTALL)
it_res = re.finditer(mask_res, q, re.DOTALL)
it_index = re.finditer(mask_index, q, re.DOTALL)
found_arith = next(it_arith)
try:
while True:
acc = 0
found_res = next(it_res)
while found_arith and found_arith.start() < found_res.start():
acc += ArithHandler(found_arith)
try:
found_arith = next(it_arith)
except StopIteration:
break
res = (ResHandler(found_res) - acc) & 0xFF
found_index = next(it_index)
key[IndexHandler(found_index)] = chr(res // 2)
except StopIteration:
pass
key = ''.join(x[1] for x in sorted((k, key[k]) for k in key))
return key
def main():
p = remote('cm2k-enlightenment_4ee3a7c97ce496cde9bdf905843cf0f1.quals.shallweplayaga.me', 12999)
_ = p.readline()
try:
while True:
x = p.readline()
print x
x = x.strip()
keys = []
try:
with open(x, 'rb') as f:
q = f.read()
if len(q) < 1024*15:
keys.append(MagicSolution(x))
except Exception:
pass
try:
keys.append(SorcerySolution(x))
except Exception:
pass
try:
keys.append(AlchemySolution(x))
except Exception:
raise
try:
keys.append(WitchcraftSolution(x))
except Exception:
pass
try:
keys.append(OccultSolution(x))
except Exception:
pass
key = sorted([(len(y), y) for y in keys if y is not None])[-1][1]
proc = subprocess.Popen(['./' + x], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate(key + '\n')
if proc.returncode != 0 and 'swift' not in err:
key = key[::-1]
print key
p.sendline(base64.b64encode(key))
except Exception:
raise
p.interactive()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment