in_array
in_array(mixed $needle, array $haystack, bool $strict = false): bool
用于检查数组中是否存在某个值,参数说明如下:
needle
:待搜索的值,如果needle
是字符串,则比较是区分大小写的;haystack
:待搜索的数组;strict
:如果strict
的值为true
则还会检查needle
的类型是否和haystac
中的元素相同;
一个例子
<?php
$whitelist = range(0, 32);
$file = $_FILES['solution'];
if (in_array($file['name'], $whitelist)) {
move_uploaded_file(
$file["tmp_name"],
"./" . $file["name"]
);
echo "File uploaded successfully";
}
这道题的漏洞点在于使用了in_array
来对上传文件的名称进行检查,判断其是否在白名单中。而根据官方文档对in_array
的定义,这里并没有设置参数strict
,这个参数默认为false
,即并没有启用严格模式,导致了in_array
并不会检查needle
的类型是否和haystac
中的元素相同,而是进行强制类型转换之后再进行比较。
例如:
<?php
$whitelist = range(1, 24);
$filename = '6shell.php';
echo (in_array($filename, $whitelist));
显然6shell.php
经过强制类型转换之后变成了6
,在白名单中。
利用这个缺陷,就可以通过构造文件名,使其经强制类型转换之后处在白名单中,进而绕过检查:
POST / HTTP/1.1
Host: 127.0.0.1:10001
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary61ZqAmwPvCxh1p6m
Content-Length: 220
------WebKitFormBoundary61ZqAmwPvCxh1p6m
Content-Disposition: form-data; name="solution"; filename="6shell.php"
Content-Type: application/octet-stream
<?php phpinfo();?>
成功利用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
<?php
$servername = "127.0.0.1";
$username = "root";
$password = "root";
$dbname = "fucker";
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;
}
db.sql
CREATE
DATABASE fucker;
USE
fucker;
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 ('FUCK_CTF{1n0rrY_i3_Vu1n3rab13}');
利用上述文件,搭建一个CTF环境。
代码分析
先看index.php
:
建立数据库连接之后,从数据库的users
表中查询所有记录,然后设置了一个白名单,白名单的内容是users
表中用户的ID值;
然后从GET传参获取一个参数id
,通过stop_hack()
函数过滤id
中的敏感字符;
如果id
不含敏感字符,并且在白名单中,就使用拼接的SQL语句执行查询操作,并返回查询结果;
再看config.php
:
先是设置好数据库连接的相关信息,然后定义了一个敏感字符过滤函数,过滤掉以下字符串:
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
漏洞利用
这里主要有两个考点,一个是in_array
的缺陷绕过,另一个是updatexml
注入。
题目中使用了in_array
来检查参数值是否在白名单中,并且没有设置强匹配,可以利用上面提到的强制类型转换缺陷进行绕过;
而在stop_hack
函数中,禁止了较多常用的SQL函数,但没有禁止updatexml
,所以这里考虑使用updatexml
进行注入。
先来复习一下updatexml
注入的产生原因。
updatexml
的基本用法: