LabyREnth Capture the Flag (CTF): Mobile Track Solutions

Category: Events

Welcome back to our blog series where we reveal the solutions to LabyREnth, the Unit 42 Capture the Flag (CTF) challenge. We’ll be revealing the solutions to one challenge track per week. Next up, the Mobile track.

Mobile 1 Challenge: This is your last chance, choose wisely!

Challenge Created By: Jeff White @noottrak

We are given an iOS app for this challenge. When we run it in the emulator, we have some squares to touch. If we select a correct square it turns green, if we select a wrong one, it turns yellow; if we touch a wrong one again, it turns red and we lose.


The goal is to turn all the squares green to win and get the flag. We could just play the game and get lucky, or we could try and reverse the program to obtain the key. If we open the binary in IDA we can browse through the functions and quickly find an interesting function that stands out. Specifically, one of them is moving a long array of bytes to incrementing offsets of RDX at the beginning of the function. If we push ‘R’ on each byte in IDA, we can see that the key is being moved.



Mobile 2 Challenge: Multiple choice, you say?

Challenge Created By: Juan Cortes @kongo_86

We are given an APK file ‘EZfill.apk’. To start analyzing this we can either unzip it using a zip tool or use apktool so we can look at the resources, Manifest file, and the smali code. I prefer the apktool.


Examining the AndroidManifest.xml file reveals this is a rather small APK with one main activity.


The next step will be to load our APK into our emulator.


Our app shows up in our app’s list:


Opening the app gives a login screen. Let’s input some fake data and see what we get:


Seems the app is doing some checking for the email and password. At this point we can start analyzing the app’s code. We can go straight into viewing smali code or we can use Bytecodeviewer.

By examining the code, we can see decompiled code in java. We immediately notice a function that takes an array of char as a parameter and returns an array of char. Inside that function we have an array of int’s, which if we were to guess, is the encoded flag. We need to find where this function is being called from and the char array that is passed as an argument. In order to trace it back, we need to look for the onCreate function. This is the first callback when an Android application is started. If you recall from loading the app into the emulator, we saw a button so this leads us to believe a function handles the action of clicking it. Inside the onCreate function we see setOnClickListener. If we follow what is passed to setOnClickListener, it points to a class ‘b’ which has the function OnClick.


The onClick calls back to a function in CupsLogin. Since the code is slightly obfuscated and a bit hard to trace, we’ll focus on key points in order to trace this. We noticed these two little functions:



Assuming the check for the char ‘@’ and length check of 4, we can conclude that it is checking the email and/or password. Tracing this back, we can see that is being used in private void m().


Inspecting that code inside that function reveals that the ‘string’ & ‘string2’ is being passed to an object.


The object being created extends AsyncTask. According to Android’s documentation on AsyncTask, when executed, it goes thru four steps.

Inside this class were some more checks in regards to chars. Analyzing the code, we see that doInBackground calls function ‘a’. Inside this function we see a call too:


Analyzing that function, we can see that it is doing checks and building an array of char and then passing it to a function. Let’s see what values it expects.

After spending time figuring out what char values are expected, we can statically determine a few values for the password:

For the remaining chars, we can write a python script to brute force it. Don’t forget to transpose the decoding function we found earlier into python. Below is a sample script to decode this.

This will generate about 1,500 different variations. We can then grep for lines that have ‘PAN{‘ and that slims it down to 16. Interestingly enough, we get the flag with eight different keys.


If we enter any of the right combos in the app we get:



Mobile 3 Challenge: This executable can be run on any of the 15 BEST phones in circulation!

Challenge Created By: Josh Grunzweig @jgrunzweig 

For this particular challenge, users are given an application created for the Windows mobile platform. There are a couple of ways to go about solving this challenge; however, approaching it statically is probably the easiest versus setting up the Microsoft Windows mobile emulator or debugging the code on an actual phone. Should the user run the application in an emulated environment, they are presented with the following:


Figure 1 Application running in emulated environment

Looking at the provided binary, if we unzip the file two times, we’re presented with the following files:


Figure 2 Unzipped files from Windows mobile application

The executable in question is a .NET binary, which we can decompile using a program such as dnSpy. Looking at the underlying code, we can see that obfuscation has been implemented to make it more difficult to read and understand.


Figure 3 Decompiled code in Windows mobile application

Stepping through the code and renaming functions along the way allows us to get an understanding of what is going on. The code performs the following:

  1. The code checks to see if the SystemProductName is ‘Virtual’, indicating it is running within an emulator.
  2. A Boolean check is performed against a function that always returns True, which must be manually changed.
  3. The screen resolution is checked to look for a width greater than 1000 and a height of 5.

Should all of these conditions be met, the code execution will proceed. Otherwise, the user is presented with a response of ‘Hounds Released!’.


Figure 4 One of the many error messages encountered

If the user makes it this far, the code will start looking at the provided user input to determine if the correct key is provided. The length of the key is first inspected. If the key does not have a length of 12, a random error message will be returned. However, should it have a length of 12, code execution proceeds. The code then makes the following checks:

  1. Checks to see if the SystemProductName starts with a ‘V’, which it should since our previous check was looking for ‘Virtual’. The 6th byte of this string (‘a’) is stored to later be used in the key.
  2. The first byte of the provided key is checked for an ordinal of 66, or the character ‘B’.
  3. The embedded MrBurns.jpg file is loaded, and the 7th byte is read and later used in the key (‘J’).
  4. The SystemManufacturer is checked for a length of 5 and the last three characters of ‘kia’. Based on all possible manufacturers, we can conclude that this code is looking for a string of ‘Nokia’. The first two characters are stored to a variable and later used in the key (‘No’).
  5. The 2nd through 4th characters of the provided key are XORed against a key and checked for a result of ‘DIE’. Reversing this process allows us to conclude the application is looking for an inputted string of ‘AdP’ at bytes 2 through 4 of the provided key.
  6. The 5th through 8th characters of the provided key are checked against a long embedded string, looking for a value of ‘uzzl’.
  7. The same checks are performed against the 9th and 10th characters, looking for ‘3’ and ‘r’ respectively.
  8. Finally, further checks are made against the last two bytes of the provided key, looking for ‘!’ and ‘?’.
  9. An embedded Garbage.jpg is read and attempted to be decrypted against the provided key if all of these conditions are met.

In addition to everything encountered, various red herring code snippets are included to waste the reverser’s time. Also, string obfuscation was used via the l1() and llll() functions, which unhex and XOR strings respectively. To make things slightly more difficult, function overloading is also used to make reversing a bit trickier.

Taking everything learned, we can conclude that the application is looking for a key of length 12. The string of ‘aNoJ’ is then concatenated at the end. Based on the checks encountered, we can conclude that the provided key must be that of ‘BAdPuzzl3r!?’. Using this key gives us the following result:


Figure 5 Key after using correct input string


Mobile 4 Challenge: (walk or swipe) and (baby and you and cry)

Challenge Created By: Juan Cortes @kongo_86

For this mobile challenge we are given an APK named ‘Swip3r’. As with the previous APK, we start our analysis by using apktool and load it up into our emulator to get an understanding of the application. We will use Android’s adb tool to install the application. Below are screenshots highlighting these steps.


(APK Tool)


(adb install)


(App icon in the emulator)

We continue by doing a basic dynamic analysis of the application. This gives us a better understanding of what the challenge may require in order to be solved. The app launches with a button that says “Give me the child”. Clicking it changes the screen to the character ‘Jared’ with “Your eyes can se so cruel…” on the bottom of the screen. Clicking on the screen doesn’t appear to do anything. As the name of the APK suggest, if we swipe we see that the screen changes to a baby crying with the text “0oo0oopps!: 1”. If we continue swiping the screen in a different direction, we see the text ““0oo0oopps: 2”. Once again swiping increases that number after the text. So it appears that the app tracks the number of times we swipe and as it suggests we are doing something wrong.


(Starting the app)


(Jared appears)


(Crying baby)

At this point, we have some understanding of the app due to our dynamic analysis. Let’s perform a static analysis of the APK structure and see if we find anything interesting.

The AndroidManifest.xml, while an important file for APK analysis, reveals nothing interesting in this particular app’s manifest. However, if we go into the lib folder, we see the following two folders and each of them holds a native library: “”. My guess is this is not your typical android library and is a custom library.



At this point, we can either load the library on IDA or attempt to decompile the APK’s code using Bytecode Viewer. Why not both?

Let’s start with looking at the decompiled Java code. The application only has two classes: MainActivity.class & Swip3r.class. The Swip3r class is rather small and we see that it sets the title and starts the activity ‘MainActivity”.



Now we’ll examine the code in the MainActivity.class. We can quickly see the interesting strings we observed from our basic dynamic analysis. We see the app attempts to load the library: ‘swiipiin’. Lastly, at the end of the class file, we see two native methods being called: ‘wel()’ and ‘well()’. One which takes no parameters and the other takes eight parameters.


(oops strings)


(library loading)


(Native Methods)

At this point we know that app uses the native library with the two native methods. We focus on finding where these two methods are being used and then moving to analyze the native library methods. The easy crude way is to just copy the decompiled code into Notepad++ and search for these native methods. We find that ‘wel()’ is used once in the OnCreate() method and ‘well()’ is used three times inside the onFling() method. Lastly, we can see that both methods after being called, returns a value that is being used to display text.





Let’s switch over to IDA for a bit and examine the native library. Under exports we see the native methods we previously found in our decompiled Java code. Examining ARM assembly hurts my eyes more than looking at x86 assembly, but we will attempt to analyze these methods a bit. By looking at the code, we can tell that its loading a string “Your eyes can se so cruel” and then returns. If you remember, we saw this text when we loaded the app in our emulator. It coincides with the fact that this method, after being called, returns a value that is used as a string in displaying text. Looking at the other method ‘well()’ we can see it calls a function (Note: I have renamed this function) ‘well_func1_F8C’. Looking at that function and using IDA’s ‘Xrefs from’ we see it calls ten other functions. If you look at those ten functions, most of them have a loop with some sort of bitwise operation, which tells me there is some decoding going on. As I mentioned previously, looking at ARM assembly is not as pleasant as x86. We take another approach and examine the decompiled Java code.


(IDA Exports)




(Example Func Loop)


(Example2 Func Loop)

The nice thing about ByteCode Viewer is that you get options from different Java decompilers. For example, if we focus on the ‘OnFling()’ method using JD-GUI decompiler, we get nasty nested while loops. If we choose Procyon decompiler, we get a nice set of nested IF…ELSE statements which is far better for reading the code. If you copy over the decompiled code to Notepad++, it makes it easier to collapse code and see a ‘big picture’ of what’s going on.


(Big picture)

A quick Google search will help you understand what is the point of the OnFling() method as well as its parameters. This will help us understand what we need to do in order to simplify the completion of this challenge. At this point, we can break down the code looking at each IF and ELSE IF and determine what values are needed. Starting with the first set of conditional statements we see that the code is checking the values from motionEvent.getY(), motionEvent.getX(), motionEvent2.getY(), motionEvent2.getX(). These values are used to detect a motion on a screen on using the X and Y axis, which in turn detect when the user moves UP, DOWN, LEFT, RIGHT. Inside each conditional statement we see that the app sets several int’s with values that are checked throughout this OnFling() method. Knowing that we want the application to call the native method ‘well()’ we can begin analyzing what values should be set with the right swipe motions as well as stay away from the motions that set the values which calls the “0oo0oopps!” message. After studying the code, we can conclude that the right combination is: UP, LEFT, DOWN, RIGHT, UP. Enjoying a handful of Jared pictures the last image holds the key: PAN{jAr3d_sayz_’swwip3r_!NO!_swipp11nn’}













Quick little easter-egg, if you notice the app has detection for a method ‘onLongPress()’. Hence if you hold down on the screen’s emulator a nice little string comes on top of the screen. This appears to be base64, however is three different base64 strings, each of which decode to: This is a Google shortened URL which leads to a YouTube video of the Labyrinth song “Within you – David Bowie 1986.” Unfortunately the Internet gods have since removed this video. Interestingly there were 26 clicks made to this URL all from different countries. Note: the real number is 27, since one click was done during testing. Get more information about the URL.




(url decoded)


Mobile 5 Challenge: Can you escape the labyrinth with your life?

Challenge Created By: Jeff White @noottrak

Similar to the first iOS challenge, we’re provided with the ARM compiled binary and a simulator version.

Looking through the simulator folders we can see a lot of images with ominous names such as “death_01.jpg”, “tunnel_01.jpg”, and “win_01.jpg”. If we load the binary into IDA and look at the strings, we see some interesting messages with “LOSE”, “WIN”, and the start of a URL.


We can also see multiple base64-encoded strings.


Pulling the base64-encoded strings out and decoding them shows they are reversed words and sentences.

Based on the strings, it looks like the app is some kind of game so let’s go ahead and boot it up in a simulator and see what it does before we dive in. For a quick and simple simulation, you can use the website for iOS or APK simulation.



Using the arrow keys, we can navigate around the “Labyrinth” and see some of the base64-decoded strings on each screen.


Eventually we fail and get our “***LOSE***” message.


Resetting a few times, we eventually make our way out of the maze, which appears random since repeating the same steps would cause us to lose at different points.


When you win, it says you have the “High Score!” and that you can submit your score to After clicking submit though, we’re presented with the below message.


The error message states that it was unable to resolve and “no flag for you”. Checking the domain, it indeed does not resolve so presumably this is what the message referred to. When I play the game again, we see it’s using a different domain this time,, which it states also cannot be resolved.


Playing through a couple of times, this pattern just continues to repeat.

If you recall from our base64 encoded strings, after all of the messages being displayed in the game is a short list of single words.

You can easily see the correlation. It appears that two of the words are being appended to “http://pan” to build a domain. Smells DGAish to me! If this is the final list of 12 words, and we know they can be next to each other (lostlost), then it’s just 12^2 for the combinations, which is easily generated and checked.

Iterating through the generated list and performing DNS lookups, we come across this gem.

Browsing to this domain, it just returns the string “M4z3Cub3″.


So it seems the app is building domains from this wordlist but fails to progress because of resolution errors. What if we simply change our host lookup so that all of these domains resolve to the known live site? is great for quickly looking at an app, but it doesn’t offer us anything in the way of debugging. Since I’m not cool enough to have a physical iOS device that I can load this on, I’ll need to figure out how to load this into a local simulator so that I can affect the domain resolution.

The next part was slightly confusing so I’ll explain it here to hopefully make someone’s life easier. Basically, if you create an Xcode project and run it on a simulator, it generates an .app file for that simulated device. If you right click on the .app file under Products in Xcode, you can open the folder that contains your file, then simply move over the .app included with this challenge. The directory path looks like this:


The directory “labyREnth_mobile5-XXX” is the project name and then the generated device ID that was created in Xcode for our simulated device. Loading our simulator, we see the app we copied over, along with our empty project app.


Now that we have the app running locally, we’ll modify our /etc/hosts file with all of the domains we enumerated and point them all to the IP we resolved for


Next, we’ll run our app and navigate the game until we trigger the win condition.





Mobile 6 Challenge: This handheld is best handheld

Challenge Created By: Richard Wartell @wartortell 

For this challenge we’re handed a file with an extension of .86p. Not exactly a common extension and the file is only 2kb, so let’s open it in a hex editor:


Immediately we can see some strings that look interesting, but the first and most important is “**TI86**”. So maybe we’re dealing with a TI86 assembly program? Let’s try to find an emulator where we can run this.

Wabbitemu comes to the rescue here. Wabbitemu will allow us to run TI calculator assembly programs. Since this program is for a TI86 calculator, we install the TI86 ROM for the calculator. We can then load the program by opening OGMob.86p and running the following series of keys:

This creates the line, Asm(OGMob), which is how you execute assembly programs on a TI86 calculator. When we execute the program, we get the following info:


Playing around with this by hitting some keys, it looks like the display changes based on key presses, but no matter what we keep getting back to a screen that says “FAILURE”. At this point, we need to start looking at this in a disassembler. Looking around for resources on reversing a TI86 binary, we find a decent blog post.

However, loading at the suggested addresses here doesn’t work, but playing around in the Wabbitemu debugger shows us that the binary needs to be loaded at 0xD6FD. We can figure this out simply from trial and error, getting the bytes to match up with what is showing up in our debugger.

Once this is loaded correctly, we can finally take a look at things in graph mode. The main graph of the program looks like this:


However, we have a lot of function calls that don’t seem to go anywhere in IDA, though they point to places in the debugger. So if we Google the addresses with TI86, we find this link about the constants. So it turns out that all of these are internal functions in calculator. By creating a new segment and storing our constants there, we can nicely label our IDB so that we can see what it’s doing:


Now we can finally put the pieces together and we see a lot of sequences that look like _getkey, compare, conditional branch. Finally, we’re seeing the logic of what we saw before with entering keys and then seeing “FAILURE”. Presumably, if we enter the right key combination, we’ll get the key for the challenge.

If we walk through the logic for the _getKey calls, and use the site we referenced earlier for our key constants, we get the following sequence:

ENTER CLEAR ( 8 6 7 – 5 3 0 9 )

When we enter these in the right order, this is what our screen shows:


Which is the key:


Bonus round:

As we were walking through those key presses, there is a large body of code that executes, and the screen changes every other key press. If we look at the data that code is referencing, we find it all pointing to constants and then using those constants to draw onto the screen. Each constant seems to be made up of 8 bytes based on the gaps:


Nothing really comes up if we look at these as just letters or numbers, for example, here are the first two of the 8 byte constants:


But, if we look at these in binary, something much more interesting pops up:

01110111  00100100

01010101  00100101

01010101  00110100

01010101  00110101

01101111  10101101

01001000  10101100

01001000  10100101

01001000  10100100

Though not immediately obvious, if you step back from this, or convert it to white/black space, this does in fact have the key for the challenge in it. The first two constants above contain the binary representation of an 8bit by 8bit sprite of the letters PAN. If we do this with all the constants, we’ll get the full key from there.


Leave a comment below to share your thoughts about these challenges. Be sure to also check out how other threat researchers solved these challenges:

Mobile 1

Mobile 2

Mobile 3

Mobile 4

Mobile 5

Mobile 6

Got something to say?

Get updates: Unit 42

Sign up to receive the latest news, cyber threat intelligence and research from Unit42