Layer7 2015: Login
Category: web - Points: 50
Description: http://prob.layer7.kr/login/index.php
The address in the description points to a login form. The page also provides us the PHP code behind the login form:
Analysis
Each time the php is run a new array $database
is created storing the passwords of the two users: Layer7
and Admin0
. However, the password are composed by 31337 random characters, which makes the login page impossible to bruteforce or guess. If the password does not match, the password for the user Layer7
is printed, but since it will be regenerated the next time, this information is useless.
Next we noted that the username is validated using the filter
function, allowing only usernames which satisfy the following regex:
[A-Z][a-z][0-9].*
Since Layer7
and Admin0
do not satisfy the regular expression, they are not even usable in the login form, so we have to look for something else.
When the $username
is not in the $database
array, $database[$username]
returns a NULL
value. Moreover, this value is compared to the $password
using the PHP loose comparison (PHP uses loose comparison with ==
and !=
, while it uses strict comparison with ===
and !==
).
The PHP loose comparison operator has a really strange behavior. This is its truth table:
The highlighted row is used when one of the two operands is NULL
. Since we want the result to be true, we need the password to be either FALSE
, 0
, NULL
, ARRAY
or ""
. So we can just provide an empty password and the comparison should be verified, giving the flag in return.
Exploit
We have to provide an username which verifies the filter
function (such as Aa0
) and an empty password, which is different from not providing the password
field at all (otherwise the isset
would return false and the message Input Password
would be displayed). We can use different tools to POST the needed data to the webpage. The easiest way is to use wget
:
wget -qO- http://prob.layer7.kr/login/index.php --post-data='username=Aa0&password='
This gives the following output:
<form method=post><input type=text name=username><input type=password name=password><input type=submit value=Login></form><br>Message : flag is a52a12ec82efd713433b16aea3f32cae!
So the flag is: a52a12ec82efd713433b16aea3f32cae
How to improve the code?
The problem here is that loose comparison tries to compare different types. The code could be improved using strict comparison, which has the following truth table:
Since we have no way to pass a NULL
parameter through a form, that would have prevented the exploit.