Enter The Matrix
This year I’m not elligible to participate in the Cyber Security Challenge Belgium as I’m not a student anymore so instead I’ve decided to contribute to the competition by writting a few challenges. Enter The Matrix was my contribution to the mobile security category.
I’m a bit disapointed no one managed to solve it … I guess no one was the one. I might have misjudged the difficulty. However it was not that hard, I swear. One java class and 7 KB of ARM code, that’s all.
The Java part
The java code is very simple, as I said it’s just one class: Terminal. The following are all
java disassembly of
classes.dex using jadx.
In the constructor we have one simple android message handler which, each time it’s called,
displays a new character from
this.terminal_text. Twelve iterations after the
whole text has finished displaying, it closes the app.
So how is this message handler called? This is done in
startPrinter by a simple timer scheduled
to send an empty message every 100 milliseconds to our message handler thus progressively
this.text on the screen.
Where does the the text comes from? It’s just above in the function :-) However the last part of
the text comes from the
whiteRabbit function. And as you probably know, you need to follow the
white rabbit to see how deep the rabbit hole goes ;)
So it’s a native function.
And it’s in a library called
Enter The Matrix
Okay so in the
lib directory you have a series of subdirectory for different architectures and
inside each of them you’ll find
libmatrix.so build for each of these architectures. You’ll notice
you don’t have a
x86 one because I’m not a nice person and I didn’t enable the intel architecture
builds. The next assembly snippets come from disassembling
Hopper, you could use
objdump or radare2
as a free alternative.
Okay so the full qualified name of our java method is
eu.haxelion.enterthematrix.Terminal.whiteRabbit and thus the symbol name we have to look for in
the library is
Oooooooh fuuuuuuuck …
The flag is missing …
I sent the wrong binary to the organizers …
So sorry :-(
Wait there’s something missing! This is a real screenshot from the app running on my smartphone:
Where’s the last “Wake up!” coming from? We’re not looking at the right function!
The Red Pill
So how does the OS knowns the
Java_eu_haxelion_enterthematrix_Terminal_whiteRabbit is at
Simple, because it’s in the
.dynsym section of the binary!
The dynamic loader
ld, when loading the binary, uses the
.dynsym tables to resolve the external
functions. But in between loading the library in memory and resolving symbol addresses using its
.dynsym table, something can happen. And this thing are the library init functions. It’s normally
used to initialize global objects and C runtime stuff. And sometimes to do evil stuff, like
dynamic patching of the
.dynsym table. This is the function at offset
This function is a bit big to paste below but you should notices calls to
mprotect. This is
because the .dynsym section is loaded in a
READ | EXECUTE page and if we want to patch the
table we have to change the permission to
READ | WRITE using
Now the patching happens at
0x14c0, in between two
If we trace back were the value comes from we find these two
instructions which compute the address of the function at address
Java_eu_haxelion_enterthematrix_Terminal_whiteRabbit is actually at
Down the Rabbit Hole
whiteRabbit is a long function, but there is a very repetitive pattern of code:
What does this do? This function XOR two array of bytes, one in
r0 and the other in
r1 and the
result is placed back into
r0. It exit when encountering a null byte. So it’s simply deciphering
strings using a one time pad.
Let’s try to decrypt the first one:
That’s interesting. So all those repetitive XOR loop are simply string deobfuscation code. The
reason for obfuscating the strings was to make the real
whiteRabbit function harder to find.
Now you could deobfuscate every string or notice that the interesting part is the
strcmp at the
0x12bc. If the return value is 0 we jump to the block at address
0x1330 which decipher
And that’s the flag, finally :-)
If you’re curious what the rest of the function is doing, it’s checking if one of the Google
account linked with your phone is
email@example.com. Yes, Neo also uses Gmail.
An alternative solution was to either patch the jump in the library or use something like
frida to change to return value of the
strcmp. What you would have seen
then is the screen below:
See you next year for something even more obscure and evil. In the meantime study your ARM ;-)