Hacktive Security Blog

Prestashop <= 1.7.6.0 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)

undefined

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 <= 1.7.6.0 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.

Timeline:

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).

 

undefined

 

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.

undefined

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.

undefined


undefined

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

undefined

Video of the attack

 

Fix Available

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

Credits

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:

undefined

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

undefined

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:

undefined

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:

undefined

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.

undefined

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.

undefined

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.

undefined

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

Variables:

  • 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:

undefined

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

undefined

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:

undefined

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.

undefined

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:

undefined

High level

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

undefined

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

The loop, in python, looks like this:

undefined

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.

undefined

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 :

undefined

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.

undefined

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:

undefined

And:

undefined

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:

undefined

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.

References

https://github.com/wapiflapi/exrs/tree/master/reverse

https://github.com/radare/radare2

 

(Alessandro)

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.

undefined

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.

undefined

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"]

undefined

undefined

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.

Timeline

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

(Carlo Pelliccioni)

Home ← Older posts