函数缺陷原理分析

先看一段简单的源代码

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. ES6的 class的基本语法

    1.类的由来 JavaScript 语言中,生成实例对象的传统方法是通过构造函数,但是这种写法跟传统的面向对象语言差异很大,容易让不熟悉这门语言的程序员感到困惑,下面有一个例子    ES6提供了更接 ...

  2. keycloak~管理平台的查询bug与自定rest中文检索

    对于keycloak来说,它的管理平台在它的源码中的admin-client中,它会定义相关的rest接口规范:在我们使用keycloak管理平台时,其中有一个组的查询,在我们查询中文组时,它是不支持 ...

  3. Ubuntu 更换内核

    Ubuntu 更换内核步骤: 下载内核源码,例如wget https://git.kernel.org/torvalds/t/linux-4.17-rc2.tar.gz 按照需要的环境,sudo ap ...

  4. 接口自动化框架搭建Unittes+HTMLTestRunner

    本次主要尝试搭建接口自动化框架,基于 unittest+HTMLTestRunner 框架主要模块: config: 存放配置文件 lib: 封装了一些接口前置函数:处理各种事物 log: 存放生成的 ...

  5. 12 shell case in语句

    Shell也支持两种分支结构(选择结构),分别是 if else 语句和 case in 语句.当分支较多,并且判断条件比较简单时,使用 case in 语句就比较方便了. if else 语句与ca ...

  6. bugku秋名山车神

    不断的刷新,发现表达式一直在变换,这种必须写脚本,才能跟上速度.直接上代码 import re import requests s=requests.session() r=s.get("h ...

  7. ESP32-使用ADC笔记

    基于ESP-IDF4.1 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "freertos/FreeRT ...

  8. Java和C#语法对比(转)

        Java C# 访问修饰符 public 修饰类.接口.变量.方法. 对所有类可见. 修饰类.接口.变量.方法. 对所有类可见.   internal 无. 修饰类.接口.变量.方法. 类,接 ...

  9. C语言:小数(float double)

    小数分为整数部分和小数部分,它们由点号.分隔,例如 0.0.75.0.4.023.0.27.-937.198 -0.27 等都是合法的小数,这是最常见的小数形式,我们将它称为十进制形式.此外,小数也可 ...

  10. java02实验:方法

    一:素数输出 1.实验要求: (1)编写判断该数是否为素数的方法,并在主方法中对其进行调用. (2)注意编程规范:程序开头部分的目的,作者以及日期:必要的空格与缩进,适当的注释等: (3)实验报告中要 ...