Dec 13: muffin_asm
As M. said, kind of a different architecture!
Challenge
ohai \o/
How about some custom asm to obsfucate the codez?
Solution
It is a python program, running a simple asm-like program:
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
#!/usr/bin/env python
import sys, struct
ip, r, f = 0x00, [0x00]*4, [False]
def _add(r1, r2): r[r1] = ((r[r1] + r[r2]) & 0xFF)
def _addv(r1, v): r[r1] = ((r[r1] + v) & 0xFF)
def _sub(r1, r2): r[r1] = ((r[r1] - r[r2]) & 0xFF)
def _subv(r1, v): r[r1] = ((r[r1] - v) & 0xFF)
def _xor(r1, r2): r[r1] = (r[r1] ^ r[r2])
def _xorv(r1, v): r[r1] = (r[r1] ^ v)
def _cmp(r1, r2): f[0] = (r[r1] == r[r2])
def _cmpv(r1, v): f[0] = (r[r1] == v)
def _je(o): global ip; ip = (o if f[0] else ip)
def _jne(o): global ip; ip = (o if not f[0] else ip)
def _wchr(r1): sys.stdout.write(chr(r[r1]))
def _rchr(r1): r[r1] = ord(sys.stdin.read(1))
ins = [_add, _addv, _sub, _subv, _xor, _xorv, _cmp, _cmpv, _je, _jne, _wchr, _rchr]
def run(codez):
global ip
while ip < len(codez):
c_ins = ins[ord(codez[ip])]
if c_ins in [_je, _jne]:
old_ip = ip
c_ins(struct.unpack('<I', codez[(ip+1):(ip+5)])[0])
if old_ip == ip: ip += 5
continue
num_of_args = c_ins.func_code.co_argcount
if num_of_args == 0: c_ins()
elif num_of_args == 1: c_ins(ord(codez[ip+1]))
else: c_ins(ord(codez[ip+1]), ord(codez[ip+2]))
ip += (1 + num_of_args)
print '[ muffin asm ]'
print 'muffinx: Did you ever codez asm?'
run('\x04\x00\x00\x04\x01\x01\x04\x02\x02\x04\x03\x03\x05\x02\xbd\x00\x02\x00\x00 [..] \x01\x03\x00\x01')
Interesting, ok, lets run it.. We are asked for a flag and are told “nope” when we enter it incorrectly:
1
2
3
4
5
6
$ python muffin_asm.py
[ muffin asm ]
muffinx: Did you ever codez asm?
<< flag_getter v1.0 >>
ohai, gimmeh flag: tardis
[-] nope!
The program likely checks the string one character at a time with the cmp
function, so we simply check what it is checking against by changing one line:
1
2
# def _cmp(r1, r2): f[0] = (r[r1] == r[r2]) # old
def _cmp(r1, r2): sys.stdout.write(chr(r[r2])); f[0] = (r[r2] == r[r2]);
Now we run the program, and give it a random string at least as long as the flag, and it just prints out the flag for us :D
1
2
3
4
5
6
$ python muffin_asm.py
[ muffin asm ]
muffinx: Did you ever codez asm?
<< flag_getter v1.0 >>
ohai, gimmeh flag: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
HV17-mUff!n-4sm-!s-cr4zY[+] valid! by muffinx :D if you liked the challenge, troll me @ twitter.com/muffiniks =D
Whoo! we have our flag HV17-mUff!n-4sm-!s-cr4zY
NOTE: below is my initial, overly complex solution:
We could try to reverse the program, but if the program checks the flag one character at a time and exits upon the first mismatch, we might be able to determine the flag by brute-forcing each subsequent character and determine the correct one from the number of cycles it takes before failure.
We adjust the program a bit to count the number of instructions and build the flag one character at a time:
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
#!/usr/bin/env python
import sys, struct, string
ip, r, f = 0x00, [0x00]*4, [False]
def _add(r1, r2): r[r1] = ((r[r1] + r[r2]) & 0xFF)
def _addv(r1, v): r[r1] = ((r[r1] + v) & 0xFF)
def _sub(r1, r2): r[r1] = ((r[r1] - r[r2]) & 0xFF)
def _subv(r1, v): r[r1] = ((r[r1] - v) & 0xFF)
def _xor(r1, r2): r[r1] = (r[r1] ^ r[r2])
def _xorv(r1, v): r[r1] = (r[r1] ^ v)
def _cmp(r1, r2): f[0] = (r[r1] == r[r2])
def _cmpv(r1, v): f[0] = (r[r1] == v)
def _je(o): global ip; ip = (o if f[0] else ip)
def _jne(o): global ip; ip = (o if not f[0] else ip)
def _wchr(r1): pass #sys.stdout.write(chr(r[r1]))
def _rchr(r1):
#r[r1] = ord(sys.stdin.read(1))
global nextchar
try:
r[r1] = ord(myinput[nextchar])
except:
pass
nextchar += 1
ins = [_add, _addv, _sub, _subv, _xor, _xorv, _cmp, _cmpv, _je, _jne, _wchr, _rchr]
def run(codez):
global ip
instrcount = 0
while ip < len(codez):
instrcount += 1
c_ins = ins[ord(codez[ip])]
if c_ins in [_je, _jne]:
old_ip = ip
c_ins(struct.unpack('<I', codez[(ip+1):(ip+5)])[0])
if old_ip == ip: ip += 5
continue
num_of_args = c_ins.func_code.co_argcount
if num_of_args == 0: c_ins()
elif num_of_args == 1:
c_ins(ord(codez[ip+1])),
else:
c_ins(ord(codez[ip+1]), ord(codez[ip+2]))
ip += (1 + num_of_args)
return instrcount
print '[ muffin asm ]'
print 'muffinx: Did you ever codez asm?'
program = '\x04\x00\x00\x04\x01\x01\x04\x02\x02\x04\x03\x03\x05\x02\xbd\x00\x02\x00\x00 [..] \x01\x03\x00\x01'
# find the flag by looking at which subsequent letter increases the total instruction count
# of the program
print 'Looking for the flag..'
maxinstrcount = 1520 #number of instructions when no correct characters
flag = ''
for i in range(0,29):
for c in string.letters+string.digits+string.punctuation:
myinput = flag + c
ip = 0
nextchar = 0
numinstr = run(program)
if numinstr > maxinstrcount:
flag += c
maxinstrcount = numinstr
break
print flag
This outputs:
1
2
3
4
5
$ python muffin_asm_bf.py
[ muffin asm ]
muffinx: Did you ever codez asm?
Looking for the flag..
HV17-mUff!n-4sm-!s-cr4zY
Flag
HV17-mUff!n-4sm-!s-cr4zY