在github发现了个代码审计学习的资料,项目叫PHP-Audit-Labs,红日安全做的,跟着学习下做个记录吧。
知识点:in_array(),在不知道列名的情况跑数据,make_set()。
in_array():检查数组中是否存在某个值,可以接受3个参数(a,b,c)。在b中寻找a,如果存在则返回true,否则返回false。第三个参数c默认为false,如果指定为TRUE ,则 in_array() 函数会进行强检查,检查a的类型是否和b中的相同,则返回 TRUE,否则返回 FALSE。
将他的代码下到本地,我这使用phpstudy,用它给的sql语句在本地数据库里运行一下,再将数据库账号密码改下,访问,配置成功。
先黑盒测试,待会在对照代码测试。需要传一个id。随便传一个id=1,返回数据,添加单引号,报错。传id=1' and '1'='1,也出错。传id=1 and 1=1,返回正常,传id=1 and 1=2返回异常。存在数字型注入。
尝试union查询,发现union被过滤了。接着用updatexml(1,concat(0x7e,(select database()),0x7e),1),发现concat被过滤了。用burpsuite的intruder模块fuzz测试下,大概过滤了or,union,concat,sub,join这些关键字。直接盲注吧,用mid()代替substr(),id=2 and if(mid((select database()),1,1)='d',sleep(5),1)。
然后想起了or被过滤了,用不了information.schema,也就是说跑不了表名和列名。emmmm接下来盲猜一下它的表(admin,user,users之类的),猜到表名users,列名从页面显示中得到有(id,name,email,salary)。
如果实在不知道列名,可以通过这个姿势
select `4` from (select 1,2,3,4 union select * from users)a
正常查询
发现列名变成了1,2,3,4
成功查到第3列
但是这里过滤了union,此处用不上这个姿势。
接着对应着代码看,先看index.php,大意是得到一个id,经过stop_hack()处理,再用in_array()处理后查询。
//index.php
<?php
include 'config.php';
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("连接失败: ");
}
$sql = "SELECT COUNT(*) FROM users";
$whitelist = array();
$result = $conn->query($sql);
if($result->num_rows > 0){
$row = $result->fetch_assoc();
$whitelist = range(1, $row['COUNT(*)']);
}
$id = stop_hack($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";
if (!in_array($id, $whitelist)) {
die("id $id is not in whitelist.");
}
$result = $conn->query($sql);
if($result->num_rows > 0){
$row = $result->fetch_assoc();
echo "<center><table border='1'>";
foreach ($row as $key => $value) {
echo "<tr><td><center>$key</center></td><br>";
echo "<td><center>$value</center></td></tr><br>";
}
echo "</table></center>";
}
else{
die($conn->error);
}
?>
在config.php中,过滤了一些关键字。
//config.php
<?php
$servername = "localhost";
$username = "root";
$password = "root";
$dbname = "day1";
function stop_hack($value){
$pattern = "insert|delete|or|concat|concat_ws|group_concat|join|floor|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex|file_put_contents|fwrite|curl|system|eval";
$back_list = explode("|",$pattern);
foreach($back_list as $hack){
if(preg_match("/$hack/i", $value))
die("$hack detected!");
}
return $value;
}
?>
再看下题解,发现它用make_set()替代了concat()。搜了一下它的用法
id=4 and (select updatexml(1,make_set(3,'~',(select flag from flag)),1))
make_set(bits,str1,str2,,,,),将第一个参数转化为二进制,在1处打印字符str,0处不打印,详情如下
1的二进制为0001,倒过来为1000,打印a。
2的二进制为0010,倒过来为0100,打印b。
7的二进制为0111,倒过来为1110,打印a,b,c。
[图片上传中...(图片.png-7b38da-1562846283165-0)]