Hacktive Security Blog

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.






Responsible disclosure - Reflected XSS on hireon.amazon.com

On March 13th, by using dnsrecon (https://github.com/darkoperator/dnsrecon) and a huge wordlist, I came across with an Amazon domain (hireon.amazon.com) with a Reflected XSS.

Usually I don't use to write an article for an XSS vulnerability, but I would share a trick I discovered during this analysis.

Looking for a not existent resource, the following error message was displayed on the web page.


If a string was concatenated to the resource id, then it was printed on the page. At the first stage I tried to use html tags and no input validation was performed at all. Then I tried to use javascript code and I have found a couple of filters for some special characters. In particular when the “dot” (.) character was detected an error message was thrown.


In order to show an alert box with the current domain name (or cookies) I should have used “document.domain” or "document.cookie" but because of dot character I had to find an alternative way. At that point I tried to use the document object like an associative array (as shown in the following screenshot) and I was able to show the current domain name within the alert box.

document["domain"] or document["cookie"]



On March 21st, Amazon changed the domain name in livecode.amazon.jobs but the XSS was still there.

Finally, on March 26th, the XSS was completely fixed.


- March 13th - First contact
- March 21st - Domain change but the vulnerability is still there.
- March 26th - Fixed

(Carlo Pelliccioni)

Facebook chat / dashboard content injection

I often wondered how link generation functionality is implemented by major social network applications and, more specifically, how the preview generation is developed.

Some time ago a friend of mine was spear-phished with a message through the Facebook chat, this happened before Facebook put in place the patch to allow the exchange of messages only between people connected as friends. While I was analyzing the message, I noticed that it looked like a legit message, with a link to a newspaper page, a title, body and image, but also with a pseudo-random generated domain like the killswitch domain used by WannaCry ransomware, written in a small gray text area below the description.

Crawling on Google I found out that someone already reported a similar finding to Facebook Security Team but it was not exactly the same issue that I discovered; they were just talking about the ability to to inject whatever URL inside opengraph tags. So I decided to go straight through that to figure out something more.


The Facebook chat and board features implement a functionality to generate a preview box whenever someone write a full URL in a message; the Facebook crawler then will scan the target website, take some markup headers parameters like og:title og:description along with an icon of the page and returns a preview box. This is a part of the already known OpenGraph Markup Language, nothing new.

What I didn't expect is that the Facebook web application uses data that client sends to generate the URLs inside the generated preview box.

Video PoC

Getting hands dirty

Starting Burp Suite, a Web application security tool, i started to intercept all the traffic from and to the platform; we'll use repubblica.it as a target, an italian newspaper.

Note: I tested it out sending message to myself for not violating the whitehat disclosure rules.


Look at those URLs, what happens if we change them?


And after forwarding the message:


The original URL, the icon, title and body is taken directly from the repubblica.it website, except for that little grayed text below "google.it". When we click on it we will get:



At this stage we know:

- The output URL is what we send from AJAX POST request

- URLs are not encoded

- Server does not validate if the input URL is the same that we typed it.

- Server does not validate if the result mismatch with our tampered request.

But we need to go a little further, in order to achieve a perfect, standalone Proof Of Concept we need to perform those actions at runtime, without interrupting outgoing HTTP requests and without using tools like Burp or OWASP ZAP.

Mitmproxy to the rescue!

Mitmproxy is basically a python software that acts as an HTTP proxy like BurpSuite but with the ability to change every aspect of an HTTP request or response at runtime; it can be even used as an interactive console to change/inspect HTTP requests. And that's exactly what we were looking for.

We wrote a small python script that takes everything we get from the POST request sent to /messaging/send endpoint and replace every original URL (www.repubblica.it) with our (www.voidzone.it) using a regular expression.


Since the server somehow does not like URL parameters I configured my website to perform a permanent redirect to a youtube video so no GET parameters are needed.

Next, we fire up our proxy


And just try to resend the same message:


And if we click it


Yeah you just got rick roll'd :)

Foot notes

Facebook security team is already aware of this issue and this is the response:


So they will consider it as "low-impact risk" and most probably they will not fix.

I'm trying to figure out why a content based social network like Facebook does not consider a content vulnerability like this, marking it as "low risk" even after I reported that someone is already using it to steal Facebook credentials.

Please note: this vulnerability has been found also on the Facebook dashboard section.
So what we learned from this, is that Facebook seems not to validate input URLs allowing every nasty user to tamper URL previews with arbitrary contents.


Home ← Older posts