函数缺陷原理分析

先看一段简单的源代码

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语句,就可以暴出数据

PHP-Audit-Labs-Day1 - in_array函数缺陷的更多相关文章

  1. PHP代码审计01之in_array()函数缺陷

    前言 从今天起,结合红日安全写的文章,开始学习代码审计,题目均来自PHP SECURITY CALENDAR 2017,讲完这个题目,会再用一道有相同问题的CTF题来进行巩固.下面开始分析. 漏洞分析 ...

  2. 代码审计学习01-in_array() 函数缺陷

    一.开始代码审计之旅 01 从今天起,学习代码审计了,这篇文章就叫代码审计01吧,题目来自 PHP SECURITY CALENDAR 2017 的第一题,结合 红日安全 写的文章,开始吧. 二.先看 ...

  3. PHP代码审计02之filter_var()函数缺陷

    前言 根据红日安全写的文章,学习PHP代码审计审计的第二节内容,题目均来自PHP SECURITY CALENDAR 2017,讲完这个题目,会有一道CTF题目来进行巩固,外加一个实例来深入分析,想了 ...

  4. [代码审计Day1] in_array代码审计

    简介 1 简介  in_array() 函数搜索数组中是否存在指定的值. 语法:in_array(search,array,type) 参数 描述 search 必需.规定要在数组搜索的值. arra ...

  5. in_array函数的第三个参数 strict

    看段代码 <?php $array = array('testing',0,'name'); var_dump($array); var_dump(in_array('foo', $array) ...

  6. php -- in_array函数

    in_array    检查数组中是否存在某个值 说明 bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE  ...

  7. PHP使用in_array函数检查数组中是否存在某个值

    PHP使用 in_array() 函数检查数组中是否存在某个值,如果存在则返回 TRUE ,否则返回 FALSE. bool in_array( mixed needle, array array [ ...

  8. 转 PHP in_array() 函数

    实例 在数组中搜索值 "Glenn" ,并输出一些文本: <?php $people = array("Bill", "Steve", ...

  9. 漏洞:阿里云盾phpMyAdmin <=4.8.1 后台checkPageValidity函数缺陷可导致GETSHELL

    阿里云盾提示phpMyAdmin <=4.8.1会出现漏洞有被SHELL风险,具体漏洞提醒: 标题 phpMyAdmin <=4.8.1 后台checkPageValidity函数缺陷可导 ...

随机推荐

  1. centos 8 gitlab 重置管理员的密码

    登录gitlab安装服务器 由于 root 账户用的很少,所以我们容易忘记它的密码,但不代表它不重要,类似 linux 的 root 账户:一旦我们忘记了 root 账号的密码,我们需要知道重置的方法 ...

  2. CentOS 8 按tab键不能自动补全问题解决方案

    CentOS中按tab键不能自动补全问题解决办法 检查一下系统有没有安装bash-completion包 [root@Sonarqube ~]# rpm -lq bash-completion yum ...

  3. MPP大规模并行处理架构详解

    面试官:说下你知道的MPP架构的计算引擎? 这个问题不少小伙伴在面试时都遇到过,因为对MPP这个概念了解较少,不少人都卡壳了,但是我们常用的大数据计算引擎有很多都是MPP架构的,像我们熟悉的Impal ...

  4. 1.3.4、通过Host匹配

    server: port: 8080 spring: application: name: gateway cloud: gateway: routes: - id: guo-system4 uri: ...

  5. sshpass用法介绍

    参考文章:http://www.mamicode.com/info-detail-1105345.html https://www.jianshu.com/p/a2aaa02f57dd p.p1 { ...

  6. java封装继承以及多态(含代码)

    封装 该露的露,该藏的藏 我们常需设计要追求,"高内聚,低耦合".高内聚就是类的内部数据操作细节自己完成.不允许外部干涉:低耦合:仅暴漏少量的方法给外部使用. 封装(数据的隐藏) ...

  7. take for granted

    解释含义1 Take for granted是一句地道的英语口语,意思是to expect someone to always be there and do things for you even ...

  8. Socket 编程介绍

    Socket 编程发展 Linux Socket 编程领域,为了处理大量连接请求场景,需要使用非阻塞 I/O 和复用.select.poll 和 epoll 是 Linux API 提供的 I/O 复 ...

  9. java基础---数组的查找算法(2)

    一.查找的基本概念 查找分为有序查找和无序查找,这里均以数组为对象,有序查找指的是数组元素有序排列,无序查找指的是数组元素有序或无序排列 平均查找长度(Average Search Length,AS ...

  10. SQL注入:sqli-labs lesson-1 小白详解

    为什么是小白详解?因为我就是小白 SQL注入早有耳闻,今天算是真正打开这个门了,但是想要跨进去应该还是没有那么容易. 在B站上听了40分钟的网课,老实说,他讲的还不错,第一遍听不懂也正常 https: ...