REcon movfuscation
One thing, this wasn't a ctf challenge but a challenge released as part of a talk from an REcon talk. Let's take a look at the binary:
$ file movfuscated1
movfuscated1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, stripped
$ ./movfuscated1
M/o/Vfuscator 2.0a // domas // @xoreaxeaxeax
Enter the key: 15935728
Nope.
So we can see it is a 32
bit binary, that is a crackme. Also as the name suggests, it has been obfuscated using Movfuscated (which obfuscates the code by using a lot of mov
instructions in the binary). Looking at the assembly code for this binary, we can see that it is going to be a pain:
$ objdump -D movfuscated1 -M intel | less
. . .
80482fd: a1 c0 5d 1d 08 mov eax,ds:0x81d5dc0
8048302: ba 04 00 00 00 mov edx,0x4
8048307: a3 90 5c 0d 08 mov ds:0x80d5c90,eax
804830c: 89 15 94 5c 0d 08 mov DWORD PTR ds:0x80d5c94,edx
8048312: b8 00 00 00 00 mov eax,0x0
8048317: bb 00 00 00 00 mov ebx,0x0
804831c: b9 00 00 00 00 mov ecx,0x0
8048321: ba 00 00 00 00 mov edx,0x0
8048326: c7 05 9c 5c 0d 08 00 mov DWORD PTR ds:0x80d5c9c,0x0
804832d: 00 00 00
8048330: a0 90 5c 0d 08 mov al,ds:0x80d5c90
8048335: 8a 1d 94 5c 0d 08 mov bl,BYTE PTR ds:0x80d5c94
804833b: 8a 0d 9c 5c 0d 08 mov cl,BYTE PTR ds:0x80d5c9c
8048341: 8a 94 18 d0 3b 06 08 mov dl,BYTE PTR [eax+ebx*1+0x8063bd0]
8048348: 8a b4 18 e0 3d 06 08 mov dh,BYTE PTR [eax+ebx*1+0x8063de0]
804834f: 8a 84 0a d0 3b 06 08 mov al,BYTE PTR [edx+ecx*1+0x8063bd0]
8048356: a2 98 5c 0d 08 mov ds:0x80d5c98,al
804835b: 8a 84 0a e0 3d 06 08 mov al,BYTE PTR [edx+ecx*1+0x8063de0]
8048362: a2 9c 5c 0d 08 mov ds:0x80d5c9c,al
8048367: a0 91 5c 0d 08 mov al,ds:0x80d5c91
However we don't need to reverse this binary necessarily. With a lot of different crackmes, they will essentially check the input a single character at a time. If it passes a check it will move on to the next check, and if it doesn't it just immediately exits. Thing is if we have a correct character and it goes on to the next check, that should execute more instructions than if we were to input any other incorrect character. If our assumption is correct, then we can just brute force it one character at a time, and see what character has the most instructions executed when we input it (and select that to be the correct character). Proceeding that we add it to the flag and move on to the next character until we have the flag.
For this we can use the performance analyzer perf to count the number of instructions ran (we can also count other events such as the cpu-clock or branches). Here are some examples
Count the number of instructions:
$ perf stat -e instructions ./movfuscated1
M/o/Vfuscator 2.0a // domas // @xoreaxeaxeax
Enter the key: 15935728
Nope.
Performance counter stats for './movfuscated1':
804,200 instructions
2.940768967 seconds time elapsed
We can also format the output of perf to make it easier to parse:
$ perf stat -x : -e instructions ./movfuscated1
M/o/Vfuscator 2.0a // domas // @xoreaxeaxeax
Enter the key: 15935728
Nope.
803653::instructions:857080:100.00::::
Also we can specify what privilege level we want to view the events (so count the number of instructions that run at the user level :u or the kernel level :k, or the user level k):
$ sudo perf stat -x : -e instructions:u ./movfuscated1
M/o/Vfuscator 2.0a // domas // @xoreaxeaxeax
Enter the key: 15935728
Nope.
261507::instructions:u:790421:100.00::::
We will want to use u, since the instructions we want to count are being ran with user level privileges.
So we can see that the number of instructions is the first thing it gives us with this form of output. Now with this, we can write a python program based off of the earlier mentioned writeup which will simply iterate through all printable characters for each slot, choose the character which has the most instructions ran, and move on to the next character. Also one thing I originally learned how to do this from: https://dustri.org/b/defeating-the-recons-movfuscator-crackme.html
# Import the libraries
from subprocess import *
import string
import sys
# Establish the command to count the number of instructions, pipe output of command to /dev/null
command = "perf stat -x : -e instructions:u " + sys.argv[1] + " 1>/dev/null"
# Establish the empty flag
flag = ''
while True:
# Reset the highest instruction value and corresponding character
ins_count = 0
count_chr = ''
# Iterate Through all printable characters
for i in string.printable:
# Start a new process for the new character
target = Popen(command, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=True)
# Give the program the new input to test, and grab the store the output of perf-stat in target_output
target_output, _ = target.communicate(input='%s\n'%(flag + i))
# Filter out the instruction count
instructions = int(target_output.split(':')[0])
# Check if the new character has the highest instruction count, and if so record the instruction count and corresponding character
if instructions > ins_count:
count_chr = i
ins_count = instructions
# Add the character with the highest instruction count to flag, print it, and restart
flag += count_chr
print flag
When we run it (also if you don't have the config set to run the instruction counting with perf as an unprivileged user, you will need to run this with sudo):
$ python rev.py ./movfuscated1
{
{R
{Re
{ReC
{ReCo
{ReCoN
{ReCoN2
{ReCoN20
{ReCoN201
{ReCoN2016
{ReCoN2016}
{ReCoN2016}d
{ReCoN2016}dn
$ ./movfuscated1
M/o/Vfuscator 2.0a // domas // @xoreaxeaxeax
Enter the key: {ReCoN2016}
YES!
Our script couldn't tell when the key ended, but it was obvious from the text. With that we solved the crackme!