CVE learn
directory
CVE is my dream.
早在我上小学时,就觉得 CVE 很帅。步入初中时,在初三那年我拿到了广东电信 IPTV 的通用漏洞。步入社会时,我拿到了 Github 的 Bug bounty。但我始终没有去拿一个 CVE 的编号。因为水是可以水的,但我已经水了 CNVD 和 Hackerone,我希望我的第一个 CVE 编号可以是不那么水的,有一点含金量的。
虽然我并不是 Security 行业或领域的从业人员,只是一个爱好者。但我一直觉得 Security 很帅。同样的,虽然我不是 Design 行业的从业人员,但我觉得可以让自己做出来的 project 会好看,有个性。
我并不是一个专精的人。很多时候我每项感兴趣的领域单拎出来都会被降维打击。虽然我单项领域无法媲美专业的从业者,但我感兴趣的领域都有点建树和作品。将 Security、Design、Animation、Computer 合在一起,与之能媲美的人可能会少那么一点。
而现在,我只有 Design 的代表作,而 Security、Animation、Computer 还没有一个能拿得出手的作品。因此我希望会在未来三年,尽可能的补全这些我感兴趣领域的代表作。可能因为 Work 比较闲的缘故,我的时间总会多那么一点来放到我感兴趣的领域上。即使达不到也没有关系,至少也是努力过了,无论如何三年后的自己总会比现在的自己稍微厉害那么一点点。
因此这个页面会记录一些我感兴趣的 CVE 和 CWE 的编号来让我学习,可能会获取到一些新的思路和对某个方面的理解。来帮助我实现这个目标,顺便看看冷门的 CWE 方向,毕竟 OWASP TOP 10 个人没有太大的兴趣。
CVE-2022-36114
CWE-400不受控制的资源消耗
利用 Cargo 的 build script 在构建阶段编译第三方的非 Rust code,同时结合 procedural macros,在执行 cargo run
的过程中触发了一个 Zip Bomb 行为。
对于这个问题的修复,rust-lang 已经给出了一个 补丁修复 Zip_bomb 的问题:
// 在解压 .crate 文件(tar.gz)时,限制最多只能解压出 512MB 的内容。
+const MAX_UNPACK_SIZE: u64 = 512 * 1024 * 1024;
同时引入了 LimitErrorReader
: 超过就抛出错误,避免资源被耗尽(比如磁盘写爆)。除此之外并没有对 macros、build 加以限制,只是建议尽可能的用 crates 上提交的 crate。
CVE-2023-40030
CWE-79{OWASP TOP TEN}: 不恰当的网页生成输入中和 XSS
看到这篇通告的时候,我就回想起为什么我看 cargo build --timings
的时候肿么没想到会有 CWE-79。--timings 是一个生成 build 时间的 html 报告。
可以通过在 Cargo.toml
中加入 features = ["<img src='' onerror=alert(0)"]
来触发一个 CWE-79, 因此对这个问题的修复就是,将之前的 Warning
, 换成了 bail!
机制。
bail!
宏来自 anyhow 用于方便地提前返回错误。
可以看一下 fix pr,非常有意思,比如 validate_feature_name
函数。
原来:遇到非法 feature name,只输出 warning
,流程继续。
if let Some(ch) = chars.next() {Add commentMore actions
if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' || ch.is_digit(10)) {
config.shell().warn(&format!(
"invalid character `{}` in feature `{}` in package {}, \
the first character must be a Unicode XID start character or digit \
(most letters or `_` or `0` to `9`)\n\
{}",
ch, name, pkg_id, FUTURE
))?;
}
}
我本来想看看 2025-6-27 的 src/cargo/core/summary.rs 这个 fix pr 的区别。但我发现了一个流量密码,那就是 Rc → Arc, 问就是 Thread Safety!
现在:遇到非法 feature name,直接 bail!
,构建中止,用户必须修正。
if let Some(ch) = chars.next() {Add commentMore actions
if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' || ch.is_digit(10)) {
bail!(
"invalid character `{}` in feature `{}` in package {}, \
the first character must be a Unicode XID start character or digit \
(most letters or `_` or `0` to `9`)",
ch,
name,
pkg_id
);
}
}
在单元测试中,来验证 feature name 是否合法,例如只有 is_ok()
是合法的,而 is_err()
是不合法的:
#[test]Add commentMore actions
fn valid_feature_names() {
let loc = CRATES_IO_INDEX.into_url().unwrap();
let source_id = SourceId::for_registry(&loc).unwrap();
let pkg_id = PackageId::new("foo", "1.0.0", source_id).unwrap();
assert!(validate_feature_name(pkg_id, "c++17").is_ok());
assert!(validate_feature_name(pkg_id, "128bit").is_ok());
assert!(validate_feature_name(pkg_id, "_foo").is_ok());
assert!(validate_feature_name(pkg_id, "feat-name").is_ok());
assert!(validate_feature_name(pkg_id, "feat_name").is_ok());
assert!(validate_feature_name(pkg_id, "foo.bar").is_ok());
assert!(validate_feature_name(pkg_id, "+foo").is_err());
assert!(validate_feature_name(pkg_id, "-foo").is_err());
assert!(validate_feature_name(pkg_id, ".foo").is_err());
assert!(validate_feature_name(pkg_id, "foo:bar").is_err());
assert!(validate_feature_name(pkg_id, "foo?").is_err());
assert!(validate_feature_name(pkg_id, "?foo").is_err());
assert!(validate_feature_name(pkg_id, "ⒶⒷⒸ").is_err());
assert!(validate_feature_name(pkg_id, "a¼").is_err());
}
CVE-2023-51051
CWE-125越界访问(out-of-bounds): 访问了本不属于该内存区域的数据。
vm-memory 的 VolatileMemory
实现未对边界进行严格校验,导致有可能读取到非法的内存区域:
VolatileMemory::get_slice
: 返回一段“易失性切片”(VolatileSlice),用于后续的底层操作VolatileMemory::read/write
: 直接从这块内存读/写数据,保证每次都实际发生内存访问VolatileMemory trait 就是定义了一套访问“虚拟机物理内存”或“特殊硬件内存区域”的低层接口,保证每次读写都真实发生,且不被编译器优化。它让不同的底层实现都能用统一的 trait 操作。
但问题来了,这个通告并不涉及 read/write
,更多的是 get_slice
方法,例如:
get_atomic_ref
aligned_as_ref
aligned_as_mut
get_ref
get_array_ref
上述方法有个共同点,都是利用 ref
类方法引起的。它们都尝试返回指向底层内存的 Rust 类型引用(如 &T、&[T]
等),而不是直接读写原始字节数据。它们的实现通常会调用 get_slice,假设返回的内存区域长度足够大。
如果自定义实现的 get_slice 返回的切片长度不足,就会导致这些 ref 方法产生越界引用,造成内存安全风险。例如:
仔细欣赏 fix pr 你会发现:
这些方法内部,都会调用 get_slice(offset, count)
拿到一块内存切片,然后把它 reinterpret_cast 成你要的类型引用。因此给 get_slice
加上 assert_eq!
就意味着:
“如果你要 8 字节引用,必须实际拿到 8 字节的内存切片,否则直接 panic,不做 unsafe 操作!”
732: 关键资源的权限分配错误
278: 不安全的保留继承权限