ATutor account take over using type juggling
Last updated
Last updated
There are several vulnerabilities in ATutor 2.2.1 and a lot of them are discussed in OSWE. In this post I will describe a way to exploit a type juggling vulnerability in order to update/change the password of an existing user by knowing the user id. By default the id is an increasing number starting from 1, so it is 1 for the first user, 2 for the second etc. This specific exploit I am going to describe is not discussed in OSWE, however it is based on type juggling which is covered in the course.
Before we begin, you can check out this article for how to setup ATutor 2.2.1 on a VM (personally I used docker with an ubuntu 14 server image).
You can also download ATutor 2.2.1 from here:
By examining the code of the password_reminder.php
file in the web root, we can get an idea of the internal mechanism that is used to process the password change request after a user has visitied the forgot password area of the application.
Specifically on line 72, an else if
branch starts that checks if several request parameters are set. This is the branch that is taken when a user clicks the password reset link that was sent to the registered email:
First, the app checks if 'the link has expired' based on the value of g
, however, since we control this value, this should be easy to bypass. Moving on, the app uses the value of the id
parameter to check if the user exists and fetches the email and the password hash from the database. If the id corresponds to a valid user (and so the email value is not empty), it calculates a value stored in $hash_bit
and compares it with the value of the paramter h
(which is under our control). If the values do not match it throws an error.
Let's assume for a second that we can bypass this comparison in line 97 and avoid the error. In that case the code continues as below:
Starting from line 114, we see that if the form_change
parameter is set, and if the password_error
is empty (which means that there are no error messages), we reach line 132 where the application fetches the new password from the form_password_hidden
parameter and uses it to update the user's password in the database (line 134-135).
This means that if we manage to somehow bypass the condition we saw previously in line 97, then we can set the desired values in the above parameters to update the password of the target user to an arbitrary hash we control.
A common way to exploit type juggling in PHP is by (ab)using the exponential notation. This happens beacuse in php we can use the e
letter to imply the mathematical exponential notation. For example, check these cases:
In our case, if we set the value of the h
parameter (which we control directly) to zero (0) and we somehow force the value of $hash_bit
to seem like an exponential notation representation (e.g. 0e49398498
, 000e243423
) then the comparing entities will be equal (0=0) so we will avoid entering the if statement which will result to an error.
The hash_bit
value is a substring of the $hash
value which is the sha1 calculation of the sum of the values that are stored in the id
and g
parameters. By the way, only those two parameters are added, the value of $row['password']
which represents the hash is not accounted in practise. For example lets add the following line in our code to do some tests:
As you can see the sum we are getting is only derived from the values of id
and g
however that does not mean that the value of $row['password']
is empty:
Moving forward, we will focus on this part of the code:
Since we control both id
and g
we can try to use a certain pair of values that will cause $hash_bit
to produce a value that will be a valid exponential notation that results to zero. This might sound like impossible but it is not! Also, in our case, we can bruteforce the values offline on our local system using python without having to rely on sending requests to the server. This yields results very fast, in less than a second.
So for the id
we will use the value of the target user, in our case this will be '1'. For g
we can use any value we want in order to produce the desired hash, we just have to make sure that it will not trigger the 'link expired' condition in line 79. We can use the following super simple python script to get a proper value for g
that satisfies our criteria:
If we run this we get the following value almost immediately:
Indeed, the hashbit value we obtained seems to be a valid exponential notation representation that equals to zero. All we have to do now is to craft our request using those values and see if it works:
Once again, we use the id
of the target user we want to update the password, g
holds a value that will produce a hashbit that is a valid exponential notation representation that equals to zero, the h
parameter is zero in order to be equal to the hashbit variable and satisfy the condition, form_change
and password_error
are set so that we reach the code in line 134 as we discussed earlier, and finally form_password_hidden
contains the hash of a known SHA1 password ('user1'). Based on the response we can assume that the exploit worked so we can check the database:
Indeed the password hash was updated to the desired value. We can create a full python script to automate the whole process:
This will change the password of user with id 1 to 'user1'.