PHP原生类利用
参考文章:
可遍历目录的类
DirectoryIterator类
官方文档:https://www.php.net/manual/zh/class.directoryiterator.php
DirectoryIterator 类提供了一个简单的界面,用于查看文件系统目录的内容
其中的__toString()方法可以获取指定路径目录下的目录和文件名
官方文档:https://www.php.net/manual/zh/directoryiterator.tostring.php

如果直接
echo,会创建一个指定目录的迭代器。当执行到echo函数时,会触发DirectoryIterator类中的__toString()方法,输出指定目录里面经过排序之后的第一个文件名:
1 | |

要想查看全部的文件名就要对$dir这个对象进行遍历
1 | |

DirectoryIterator与 glob:// 协议结合将无视open_basedir对目录的限制,可以用来列举出指定目录下的文件
1 | |

一句话形式
1 | |

这个类只能列出目录文件名,不能读文件的内容
FilesystemIterator类
官方文档:https://www.php.net/manual/en/class.filesystemiterator.php
这个类和DirectoryIterator类很像,使用方法也是大差不差:
1 | |

1 | |

1 | |

1 | |

GlobIterator类
官方文档:https://www.php.net/manual/zh/class.globiterator.php
这个类使用和前面两个略有不同,要通过匹配的模式来利用
1 | |

1 | |

1 | |

1 | |

1 | |

可读取文件的类
SplFileObject类
官方文档:https://www.php.net/manual/en/class.splfileinfo.php
SplFileInfo类为单个文件提供面向对象的高级接口,可以用于对文件内容的遍历、查找、操作等
SplFileObject类可以读指定文件内容,直接echo只能读一行
1 | |

所以也要用foreach遍历$content对象
1 | |

可删除文件的类
ZipArchive类
官方文档:https://www.php.net/manual/zh/class.ziparchive.php
ZipArchive类是在 PHP5.2 之后引入的原生类,可以对文件进行压缩与解压缩处理
常见的类方法
ZipArchive::addEmptyDir:添加一个新的文件目录ZipArchive::addFile:将文件添加到指定zip压缩包中ZipArchive::addFromString:添加新的文件同时将内容添加进去ZipArchive::close:关闭 ZipArchiveZipArchive::extractTo:将压缩包解压ZipArchive::open:打开一个zip压缩包ZipArchive::deleteIndex:删除压缩包中的某一个文件,如:deleteIndex(0)代表删除第一个文件ZipArchive::deleteName:删除压缩包中的某一个文件名称,同时也将文件删除- ……(其余的可以看官方文档里)
ZipArchive::open
官方文档:https://www.php.net/manual/zh/ziparchive.open.php
1 | |
打开新的或已有的zip压缩包,可以读取、写入或修改
返回值:成功时返回 true,失败时返回错误代码(整数),在 PHP 8.0+ 版本中,失败时返回 false
参数:
$filename:要打开或创建的 ZIP 文件的路径。$flags:可选参数,用于指定操作模式,它是一个位掩码,可以使用|组合多个标志。
常用的$flags:
ZipArchive::OVERWRITE:总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖或删除。ZipArchive::CREATE:如果不存在则创建一个 zip 压缩包。ZipArchive::RDONLY:只读模式打开压缩包。ZipArchive::EXCL:如果压缩包已经存在,则出错。ZipArchive::CHECKCONS:对压缩包执行额外的一致性检查,如果失败则显示错误。
注意,如果设置 flags 参数的值为
ZipArchive::OVERWRITE的话,可以把指定文件删除。这里我们跟进方法可以看到const OVERWRITE = 8,也就是将 OVERWRITE 定义为了常量8,我们在调用时也可以直接将$flags赋值为8
所以就可以利用ZipArchive类调用open方法来删除目标主机上的文件
比如已有一个test.txt文件

1 | |
运行后就会删除指定的test.txt文件
1
2// ZipArchive::OVERWRITE: 总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖
// 因为没有保存,所以效果就是删除了1.txt


$flags直接写8也是一样的
1 | |


可进行XSS的类
Error类
官方文档:https://www.php.net/manual/zh/error.tostring.php
- 适用于 PHP 7 以后的版本,测试时候发现 PHP8 似乎也可以
- 在开启报错的情况下
在原生类 Error 类中的__toString()方法可能存在XSS

这里传入的是message参数,这个参数是可以自由控制的,写入的内容直接会回显到前端,就可能存在XSS

测试代码:
1 | |
这是用来反序列化函数unserialize(),但是没有自定义的进行反序列化的类,就需要找 PHP 原生类进行反序列化
如使用Error打XSS,POC:
1 | |
1 | |
这里使用 PHP 7.3.4 版本环境


成功进行XSS
Exception类
和 Error 类差不多
- 适用于 PHP5.2 以上的版本
- 开启报错的情况下
测试代码:
1 | |
POC:
1 | |
1 | |

可绕过哈希比较的类
Error类
官方文档:https://www.php.net/manual/zh/class.error.php

属性:
1 | |
在 Error 和 Exception 原生类中,__toString()方法用于将异常或错误对象转换为字符串
测试代码:
1 | |

可以看到输出的结果中只有payload输出出来了,表示$code的1没有输出出来,所以可以继续看下面这个测试代码:
1 | |

$a和$b这两个对象的$code是不同的,所以这两个对象本身就是不同的,success1就不会被打印出来
但是__toString()方法返回的结果是相同的,都是:
1 | |
注意,这里要让$a和$b在同一行里,因为返回的结果是包含行号的,可以看到经过MD5加密是相同的了,success2是成功打印出来的,这里就是可以绕过哈希比较
Exception类
官方文档:https://www.php.net/manual/zh/class.exception.php

和 Error 类差不多,也是继承 Throwable 类,用法和结果也是一样的,只不过 Error 只能在 PHP7 使用,Exception 可以在 PHP5 以上的
测试代码:
1 | |

1 | |

可以进行SSRF的类
SoapClient类
官方文档:https://www.php.net/manual/zh/class.soapclient.php
SoapClient 类是 PHP 中用于与 SOAP(Simple Object Access Protocol)Web 服务进行交互的核心类。它允许你调用远程 Web 服务方法,就像调用本地对象方法一样,可以提供一个基于 SOAP 协议访问 Web 服务的 PHP 客户端。
类摘要:
1 | |
当调用一个不存在的 SoapClient 方法时,PHP 会自动触发__call()魔术方法,该方法可以发送 HTTP 和 HTTPS 请求:
- 将方法名和参数转换为 SOAP 请求
- 发送请求到远程 Web 服务
- 解析响应并返回结果
所以就是因为这个方法,可以利用 SoapClient 类进行 SSRF
SoapClient 类的构造函数:

1 | |
$wsdl:是 WSDL 文件的位置(可以是URL、本地文件路径),如果设置为null就是表示非 WSDL 模式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 1. URL 地址(最常见)
$client1 = new SoapClient("http://example.com/service?wsdl");
// 2. 本地文件路径
$client2 = new SoapClient("/path/to/local/service.wsdl");
// 3. NULL(非 WSDL 模式)
$client3 = new SoapClient(null, [
'location' => 'http://example.com/soap',
'uri' => 'http://example.com/namespace',
'style' => SOAP_RPC,
'use' => SOAP_ENCODED
]);
// 4. 字符串形式的 XML WSDL
$wsdlXml = file_get_contents('service.wsdl');
$client4 = new SoapClient($wsdlXml);$options:如果是 WSDL 模式,这个参数是可选的,如果在非 WSDL 模式,就必须要设置location和uri,其中location是要将请求发送到的 SOAP 服务器的 URL,而uri是 SOAP 服务的目标命名空间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
39
40
41
42
43
44
45
46
47
48
49
50
51
52$options = [
// 1. 位置和URI设置(非WSDL模式必需)
'location' => 'http://example.com/soap-endpoint',
'uri' => 'http://example.com/namespace',
// 2. 认证信息
'login' => 'username',
'password' => 'password',
'authentication' => SOAP_AUTHENTICATION_BASIC, // 或 SOAP_AUTHENTICATION_DIGEST
// 3. 代理设置
'proxy_host' => 'proxy.example.com',
'proxy_port' => 8080,
'proxy_login' => 'proxyuser',
'proxy_password' => 'proxypass',
// 4. SSL/TLS 设置
'local_cert' => '/path/to/certificate.pem',
'passphrase' => 'cert_password',
'ssl_method' => SOAP_SSL_METHOD_TLS,
// 5. 连接设置
'connection_timeout' => 30, // 连接超时(秒)
'keep_alive' => true, // 保持连接
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
// 6. 编码和格式
'encoding' => 'UTF-8',
'soap_version' => SOAP_1_1, // 或 SOAP_1_2
'style' => SOAP_RPC, // 或 SOAP_DOCUMENT
'use' => SOAP_ENCODED, // 或 SOAP_LITERAL
// 7. 缓存设置
'cache_wsdl' => WSDL_CACHE_NONE, // 缓存策略
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
// 8. 调试设置
'trace' => true, // 启用跟踪
'exceptions' => true, // 抛出异常
// 9. 流上下文(SSL/代理等)
'stream_context' => stream_context_create([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
'http' => [
'timeout' => 30,
'user_agent' => 'PHP SoapClient',
]
]),
];
SSRF
可以将第一个参数设置为null,第二个参数的location设置为目标URL
测试代码:
1 | |
需要开启extension=soup,在php.ini中找到并将前面的分号删去即可

先在vps或者虚拟机监听一个端口

然后运行测试代码

可以看到成功触发SSRF

但是这只局限于 HTTP/HTTPS 协议
SSRF + CRLF
如果 HTTP 头部存在 CRLF 漏洞的话,还可以通过 SSRF + CRLF 配合插入任意的 HTTP 头
测试代码:
1 | |


HTTP 打 Redis
测试代码:
1 | |


对于如何发送 POST 的数据包,这里面还有一个坑,就是
Content-Type的设置,因为我们要提交的是 POST 数据Content-Type的值我们要设置为application/x-www-form-urlencoded,这里如何修改Content-Type的值呢?由于Content-Type在User-Agent的下面,所以我们可以通过SoapClient来设置User-Agent,将原来的Content-Type挤下去,从而再插入一个新的Content-Type
测试代码:
1 | |


可以进行XXE的类
SimpleXMLElement类
官方文档:https://www.php.net/manual/zh/class.simplexmlelement.php
SimpleXMLElement 的构造方法 SimpleXMLElement::__construct:
官方文档:https://www.php.net/manual/zh/simplexmlelement.construct.php
1 | |

所以这里可以设置第三个参数$dataIsURL为true,就可以实现远程 XML 文件载入,可以进行XXE
第一个参数$data谁设置为要引入的外部实体的URL,第二个参数$options设置为2即可
相关题目:[SUCTF 2018]Homework
反射类Reflection
官方文档:https://www.php.net/manual/zh/book.reflection.php
读取类的属性和方法名
ReflectionClass类
官方文档:https://www.php.net/manual/zh/class.reflectionclass.php
ReflectionClass 类报告了一个类的有关信息,可以读取到类的属性和方法名
看 ReflectionClass 类的构造函数ReflectionClass::__construct
官方文档:https://www.php.net/manual/zh/reflectionclass.construct.php

测试代码:
1 | |

再比如在flag.php中新建一个类 FLAG
1 | |
在测试文件1.php中:
1 | |

可以看到指定类里面的方法和属性名
RCE
ReflectionFunction
官方文档:https://www.php.net/manual/zh/class.reflectionfunction.php
ReflectionFunction 类的invokeArgs方法可以用来写Webshell
官方文档:https://www.php.net/manual/zh/reflectionfunction.invokeargs.php


可以利用这个方法来RCE、甚至写Webshell,测试代码:
1 | |

