Labyrenth/mobile_2 :

We are given a MIPS binary, which is probably a ransomware ( hint + filename ).

-> file routerlocker
routerlocker: ELF 32-bit MSB executable, MIPS, MIPS64 version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 2.6.26, BuildID[sha1]=b9720b983cafb2a111bbac302b4ead891019e600, not stripped

Decompile it :

As I didn’t knew anything in MIPS asm, and quite franckly, didn’t have the time to learn a new arch, I used retdec.com to “decompile” the binary and started looking under the hood :

Once cleaned a bit, we start to get a clear picture of the execution flow :

    stat_loc = 0 # bp-104

	# forking himself to make debugging "harder"
    if fork() != 0:
        wait(&stat_loc)
        return 0

    # start of the forked process

    .... "obf MIPS asm" part 1 (construct the filename string)
    .... (not catched by the "decompiler")
    ....

    ptrace(0) # gdb will not like it

    .... "obf MIPS asm" second part
    .... (not catched by the "decompiler")
    ....

    file_path = ???????

    file = fopen(&file_path, "r") # 0x400a1c

    if file == NULL:
        fwrite("License file not found.\n", 1, 24, stream)
        fwrite("Lock it up, and lock it out.\n", 1, 29, stream)
        return 1

Just before the program fork, there is a huge bloc of asm opcodes that create the filename string :

create filename string

We could have reversed the mips asm, but as I’m a lazy guy and had kept a debian MIPS qemu image, I decided to do it dynamically :

Trace it :

To obtain the filename easily, we can simply strace the process, but as the process is forking itself, we need to use the -ff switch ( to follow the child execution ).

IMPORTANT : If we decide later to use gdb, we will have to use “set follow-fork-mode child” to be able to break or debug into the forked process.

[email protected]:~# strace -ff -q -e open ./routerlocker
open("/etc/ld.so.cache", O_RDONLY)      = 4
open("/lib/mips-linux-gnu/libc.so.6", O_RDONLY) = 4
[pid  2448] open("/tmp/router.lck", O_RDONLY) = -1 ENOENT (No such file or directory)
License file not found.
Lock it up, and lock it out.
--- SIGCHLD (Child exited) @ 0 (0) ---

Ok, now that we have the filename, let’s continue reading the decompiled code :

    if fread(&file_buf, 1, 29, v5) >= 29:
        # 0x400d44
        fclose(v5)

It read 29 bytes in the license file, which clearly indicates us the size of the license. Let’s create a ‘/tmp/router.lck’ file with 29 bytes in it and see what happen in strace :

[pid  2463] open("/tmp/router.lck", O_RDONLY) = 4
[pid  2463] fstat64(4, {st_mode=S_IFREG|0644, st_size=30, ...}) = 0
[pid  2463] old_mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x77a7a000
[pid  2463] read(4, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 4096) = 29
[pid  2463] close(4)                    = 0
[pid  2463] munmap(0x77a7a000, 65536)   = 0
[pid  2463] write(2, "Serial is invalid.\n", 19Serial is invalid.) = 19
[pid  2463] write(2, "Ambrocious, unlock this door!\n", 30Ambrocious, unlock this door!) = 30

Good. Let’s continue :

	.... big blob of "obf MIPS asm" to construct a value in v0?
	....
	....

        v6 = strlen(&file_buf) # 0x400d1850
        v7 = (4 * v8 & 252 | v8 % 4) + &g13
        v9 = 0 # 0x400d484753
        # branch -> 0x400d44

        while True:
            # 0x400d44
            if v9 < v6:
                v10 = v9 + &v3 # 0x400c58
                if v10[44] != v10[76] ^ *v7:
                    # 0x400c84
                    fwrite("Serial is invalid.\n", 1, 19, g17)
                    fwrite("Ambrocious, unlock this door!\n", 1, 30, g17)

We see a kind of “weird-crc-magickey-xor-sum” algo, in addition to this, looking at the code through IDA, we clearly see that, before the loop, the program is creating a value by adding stuff to the v0 register and doing other arithmetic operation. The decompiler didn’t catch anything, and I was too lazy to read docs about MIPS arch :D

magic key creation ?

Patch it :

Looking at the pseudocode generated by “retdec”, it is clear that if we set a breakpoint before and after 0x400c84, we can get the key by looking at the “comparaison” / “validation” code.

But before that, to be able to debug/break wherever we want in the binary without too much trouble, we have to patch the “ptrace(0)”. To do this, we can replace the call to ptrace(0) by somes NOPs, but wait, there’s no “NOP” in MIPS assembly (as far as I know).

Mips NOPS : http://www.cs.umd.edu/~meesh/cmsc411/mips-pipe/proj-fall11/mips-doc/node11.html tells us that “add 0 0 0” is often used as a “NOP” in MIPS asm.

First we need to find the call to ptrace in the opcode (easy) :

magic key creation ?

Then need to compile “add 0 0 0” to bytecode using an online MIPS assembler : https://alanhogan.com/asu/assembler.php which give us : 00000020

To do this :

echo $(cat routerlocker |xxd -p)|sed 's/ //g'|sed 's/0c1001dc/00000020/g' |xxd -r -p > routerlocker.patched

Our binary is now ptrace-free \o/

Debug it :

Now that we have defeated all the anti-debug tricks and obfuscation mechanism, we are ready to extract the key by setting some breakpoints at choosen places.

Here are the two breakpoint I used to extract the first char of the key ( and validate my theory ) :

set follow-fork-mode child 
break *0x00400c78
break *0x00400c80

And here is how it looks like :

magic key creation ?

So by looking at theses two registers, we will be able to extract the key, let’s try with the first char of key :

[email protected]:~# echo -en 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123' > /tmp/router.lck

[email protected]:~# gdb -q ./routerlocker.patched

(gdb) set follow-fork-mode child
(gdb) b * 0x00400c78
Breakpoint 1 at 0x400c78
(gdb) b * 0x00400c80
Breakpoint 2 at 0x400c80
(gdb) run
Starting program: /root/routerlocker.patched 
[New process 2321]
[Switching to process 2321]

Breakpoint 1, 0x00400c78 in main ()

(gdb) info r a0
a0: 0x41               <----- Our A char (first char of the current license)
(gdb) info r v0
v0: 0xb6               <----- is x0red with b6
(gdb) c
Continuing.

Breakpoint 2, 0x00400c80 in main ()
(gdb) info r a0
a0: 0x41
(gdb) info r v0
v0: 0xf7               <----- 0x41 ^ 0xb6 = 0xf7  ( "A" ^ chr(0xb6) )
(gdb) info r v1
v1: 0xc2               <----- then with the branching instruction, v1, will be compared to v0
(gdb) c                <----- as 0xf7 != 0xc2 it will branch to the failed msg and quit
Continuing.
Serial is invalid.
Ambrocious, unlock this door!
[Inferior 2 (process 2348) exited with code 03]

We now have everything to solve the challenge, on the GDB session above, we can see that the program is x0ring the license char by char, and compare the result with what is stored in the v1 register.

So to get the first char of our license, we have to do this simple maths :

[email protected]:~# python -c 'print chr(0xc2 ^ 0xb6)'
t

So the first letter of the license is ‘t’ (which looks good because of it’s printable properties). Let’s try to modify it to see if we are branched to the “failed” msg or if it goes to another char :

[email protected]:~# echo -en 'tBCDEFGHIJKLMNOPQRSTUVWXYZ123' > /tmp/router.lck
[email protected]:~# gdb -q ./routerlocker.patched
......
[New process 2361]
[Switching to process 2361]

Breakpoint 1, 0x00400c78 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x00400c80 in main ()
(gdb) c
Continuing.

Breakpoint 1, 0x00400c78 in main ()     <------ We hit the breakpoint instead of the failed msg \o/

Script it :

Okay, before making your eyes bleeding, just let me tell you that it was my first ever gdb automatisation attempt at a “crackme”. Are you ready ? :

#!/bin/bash


KEY_LEN=29
LINE='#############################'

function setkey () {
	key=$1
	key_len=${#key}
	echo -en `printf "%s%s\n" $key "${LINE:${#key}}"` > /tmp/router.lck

}

function generate () {

    pos=$1

    echo "file routerlocker.patched
	  set follow-fork-mode child
	  b * 0x00400c78
	  commands
	    i r v0
	  end

	  b * 0x00400c80
	  commands
	    i r v1
	  end
	  run
	  c" > /tmp/scr

	  for i in $(seq 1 $pos); do 
	    echo "c" >> /tmp/scr
            echo "c" >> /tmp/scr
	  done

	  echo "q
	  y" >> /tmp/scr
}

GDB="gdb -q -x /tmp/scr"

pos=0

key=''
setkey $key

while ((pos<KEY_LEN)); do
	generate $pos
	tmp=( `$GDB 2>&1 | egrep ^v | tail -n2| awk '{print $2}'|sed 's/0x//g'` )
	char=$[16#${tmp[0]}^16#${tmp[1]}]
	key+=`python -c "print chr($char)"`
	((pos++))
	echo -en "\rcurrent key is $key"
	setkey $key
done

echo

./routerlocker

Which will give us :

[email protected]:~# ./get_key.sh 
current key is that_ransomware_ran_somewhere
Thank you for purchasing RouterLocker v2.0
Your flag is: PAN{that_ransomware_ran_somewhere}

author : govlog