查看原文
科技

Rust 研学 | Rust 安全内参 2024 Part 1

张汉东 觉学社 2024-01-14
前言
如果你喜欢 Rust ,想要深入 Rust,那么欢迎加入 Rust 研学团。 这是一个独特的、付费的学习社区,专注于探索和理解开源项目中的 Rust 代码。在这里,你将有机会不仅学习 Rust 的基础知识,还能深入其核心概念和实际应用。我们的目标是通过实际的代码案例,让你在 Rust 编程领域迅速成长。你将有机会深入研究各种开源项目,分析它们的架构、设计模式和性能优化策略。 专栏用户可私聊我加入 Rust 研学群。

本系列是对 Rust 安全数据库[1] 中记录的 Rust 及其生态库安全问题的梳理,在梳理过程中,还会通过这些库的源码实现中汲取营养,帮助我们成为更好的开发者。

目录

  • 背景:漏洞分类
  • ferris-says 非UTF-8字节转换为字符串
    • ferris-says 介绍
    • ferris-says Unsound 问题详解
  • rustvmm-sys-util 内存越界风险
    • rustvmm 介绍
    • rustvmm-sys-util 漏洞详解
  • tokio-tracing 中的 UAF 非健全问题
    • tokio-tracing 介绍
    • tokio-tracing Unsound 问题详解
    • 扩展阅读:Layer 设计模式与框架生态
  • 小结

背景:漏洞分类

Rust 安全数据库中记录的安全问题分为两大类:安全漏洞(Vulnerability)和非健全(Unsound)问题。

安全漏洞又具体分为以下几类:

  • 代码执行(Code Execution)
  • 内存损坏(Memory Corruption)
  • 权限提升(Privilege Escalation,在操作系统级别或应用程序/库内部)
  • 文件泄露/目录遍历(File Disclosure / Directory Traversal)
  • 网络安全(Web Security,例如 XSS、CSRF)
  • 格式注入(Format Injection),例如 shell 转义、SQL 注入(以及 XSS)
  • 加密失败(Cryptography Failure,例如机密性破坏、完整性破坏、密钥泄漏)
  • 隐蔽通道(例如 Spectre、Meltdown)
  • 代码中的恐慌(panic)标榜为“panic-free”(特别是如果对网络 DoS 攻击有用)
  • 内存暴露(memory-exposure)
  • 线程安全(thread-safety)

此外,RustSec 还会对生态库的健全性[2] 进行跟踪,而不管它们是否是漏洞。 因为 当使用来自安全代码的 crate 可能导致未定义行为[3]时,往往伴随着健全性问题。

健全性(Soundness)是一个类型系统概念(实际上源于逻辑学研究),意味着类型系统在某种意义上是“正确的”,即类型良好的程序实际上具有所需的属性。对于 Rust,这意味着类型良好的程序不会导致未定义的行为[4]。然而,这个承诺只适用于安全代码;对于unsafe代码,由程序员来维护这份契约。

对于安全漏洞或非健全问题发生的原因或机制,RustSec 则使用关键字来进行标识。但是 RustSec 的关键字太多了,所以我对关键字也进一步做了一个分类。

分类漏洞机制以2022年为例类型
memory safety[5]alias (多个可变借用)2022 年还未见有该漏洞漏洞

double free2022 年还未见有该漏洞
use-after-free[6]incorrect-lifetime2022 年 一例 RUSTSEC-2022-0028: Vulnerability in neon[7]漏洞

wasm2022 年 一例 RUSTSEC-2022-0016: Vulnerability in wasmtime[8]漏洞

segfault2022 年 一例 RUSTSEC-2022-0002: Vulnerability in dashmap[9]漏洞
memory-layoutcast2022 年 一例  RUSTSEC-2022-0052: Unsoundness in os_socketaddr[10]Unsound

cell2022 年 一例 RUSTSEC-2020-0164: Unsoundness in cell-project[11]Unsound
ddos[12]oom2022 年两例  RUSTSEC-2022-0055: Vulnerability in axum-core[13]  和 RUSTSEC-2022-0035: Vulnerability in websocket[14]漏洞

untrust data2022 年一例  RUSTSEC-2021-0143: Vulnerability in kamadak-exif[15]漏洞 CVSS分 6.5 MEDIUM
directory-traversalWindows2022 年 两例: RUSTSEC-2022-0069: Vulnerability in hyper-staticfile[16]  和  RUSTSEC-2022-0043: Vulnerability in tower-http[17]漏洞
htmlxss2022 年一例 RUSTSEC-2022-0003: Vulnerability in ammonia[18]漏洞
integer-overflowout-of-bounds2022 年一例 RUSTSEC-2022-0051: Vulnerability in lz4-sys[19]漏洞 CVSS Score 9.8 CRITICAL
out-of-bounds-readout-of-bounds-read2022 年一例 RUSTSEC-2022-0046: Vulnerability in rocksdb[20]漏洞
side-channeltiming-attack2022 年一例 RUSTSEC-2022-0018: Vulnerability in totp-rs[21]漏洞 CVSS Score 4.2 MEDIUM
stack-overflowstack-overflow2022 年一例 RUSTSEC-2022-0004: Vulnerability in rustc-serialize[22]漏洞
subtypevariance/cell2022 年一例  RUSTSEC-2020-0164: Unsoundness in cell-project[23]Unsound
type-confusiontype-confusion2022 年一例 RUSTSEC-2020-0165: Unsoundness in mozjpeg[24]Unsound
typosquattingtyposquatting (恶意伪造同名 crate)2022 年一例 RUSTSEC-2022-0042: Vulnerability in rustdecimal[25]漏洞
unaligned-readwindows20222 年一例  RUSTSEC-2021-0145: Unsoundness in atty[26]Unsound
uninitialized-memoryuninitialized-memory20222 年两例   RUSTSEC-2022-0067: Unsoundness in lzf[27]RUSTSEC-2018-0022: Vulnerability in temporary[28]Unsound 和 漏洞
unsoundcovariant2022 年一例  RUSTSEC-2022-0007: Unsoundness in qcell[29]Unsound

注:CVSS 即通用漏洞评分系统,参见文后安全术语介绍部分。

CVSS 危害级别划分

  1. 分值范围 [0.1 - 3.9]  –>  低危害性
  2. 分值范围  [4.0 - 6.9] –>  中危害性
  3. 分值范围 [7 - 10]  -> 高危害性

ferris-says 非UTF-8字节转换为字符串

这是来自 Rust Security Advisory Database 编号为 RUSTSEC-2024-0001[30] 的非健全(Unsound)问题。

ferris-says 介绍

ferris-says[31]是一个可以打印 Rust 非官方吉祥物 Ferris 和指定文本的库。使用它你可以在运行时打印出下面图案:

 __________________________
< Hello fellow Rustaceans! >
 --------------------------
        \
         \
            _~^~^~_
        \) /  o o  \ (/
          '_   -   _'
          / '-----' \

该库也提供二进制版本,你在 cargo install fsays 安装之后,在终端可以使用 fsays 'Hello fellow Rustaceans!' 命令打印。你还可以定制打印宽度等等。

这虽然是一个非常小型的库,但是它来自于 Rust 官方。所以,安全性需要得到重视。

ferris-says Unsound 问题详解

该库 unsound 版本的核心函数 say 源码摘要如下:

pub fn say<W>(input: &[u8], max_width: usize, writer: &mut W) -> Result<()>
where
    W: Write,
{
 // Final output is stored here
    let mut write_buffer = SmallVec::<[u8; BUFSIZE]>::new();

    // Let textwrap work its magic
    let wrapped = fill(unsafe { str::from_utf8_unchecked(input) }, max_width);

    // 省略其他代码

}

这里 say  函数是一个安全函数(未标记 unsafe)。它的参数 input 是一个 &[u8],可以理解为就是一个字节序列。这个 input 参数直接被传入到了 unsafe block 中的 str::from_utf8_unchecked 方法。

其实这段代码写的并不符合 Unsafe 安全抽象规范。在 Rust 生态中,早已形成一个约定俗成,就是在使用 unsafe block 的时候,要为其增加注释,比如:

 // Let textwrap work its magic
 // SAFETY: input 永远都是 UTF-8 编码
    let wrapped = fill(unsafe { str::from_utf8_unchecked(input) }, max_width);

上面代码我为其增加了// SAFETY 注释,用于说明该 unsafe block 在这里为什么是安全的。假如我是开发者,我能确定传入的 input 一定会是 UTF-8 编码,所以假设这里是安全的。

但是事实是如此吗? ferris-says 会被用到任何想不到的场景,外部传入的字符串很可能不会是 UTF-8 编码。 所以,这里是不安全的。

遵循 Unsafe 安全抽象规范,对使用 unsafe block 的地方增加 // SAFETY 注释,有助于开发者去深入思考这里的安全边界。

我们发现,这里并不安全,所以需要修改:

pub fn say<W>(input: &[u8], max_width: usize, writer: &mut W) -> Result<()>
where
    W: Write,
{
 // Final output is stored here
    let mut write_buffer = SmallVec::<[u8; BUFSIZE]>::new();

    // Let textwrap work its magic
    let wrapped = fill(
        str::from_utf8(input).map_err(|_| std::io::ErrorKind::InvalidData)?,
        max_width,
    );

    // 省略其他代码

}

这里将 from_utf8_unchecked 换为 from_utf8,让其检查传入的字节序列是否为 UTF-8 编码,如果不是则报错。这样我们就消除了 Unsound 风险。

该库在 0.3.1 版本中已经修复了这个问题。

rustvmm-sys-util 内存越界风险

这是来自 Rust Security Advisory Database 编号为 RUSTSEC-2024-0002[32] 的安全问题。CVSS 危害等级为 MEDIUM( 5.7 分 )。

rustvmm 介绍

`rust-vmm`[33] 是一个开源项目,它提供了一套虚拟化组件,任何项目都可以使用这些组件快速开发虚拟化解决方案,而无需重新实现常见组件,如 KVM 包装器、virtio 设备和其他 VMM 库。

rust-vmm 项目是一个共同努力、共同拥有的开源项目,目前包括来自阿里巴巴、亚马逊、Cloud Base、Crowdstrike、英特尔、谷歌、Linaro、红帽以及个人贡献者的贡献者。

rust-vmm 包含了很多独立组件,各司其职,其中就包含 `rustvmm-sys-util`[34],该库提供构建虚拟机监视器(VMM)和 hypervisors 的辅助工具和实用程序的模块集合。

hypervisor 是一种将操作系统与硬件抽象分离的方法,以达到 host machine 的硬件能同时运行一个至多个虚拟机作为 guest machine 的目的,这样能够使得这些虚拟机高效地分享主机硬件资源。

在 Rust 生态中,一般以 -sys 作为后缀命名的库或文件目录,通常都意味着这是对对底层资源的 Rust 绑定。vmm-sys-util 也不例外,它封装了x86_64aarch64 平台 和 Linux/Windows 操作系统的安全抽象,用于文件处理、事件文件描述符、ioctls 等基础操作。

rustvmm-sys-util 漏洞详解

rustvmm-sys-util 中有一个模块叫 fam。该模块用于处理 C 语言中的柔性数组成员(Flexible Array Member)[35]

FAM 也叫 灵活数组成员,是 C 编程语言 C99 标准引入的一个特性。FAM 是一个没有固定维度且大小灵活的数组。这样的数组最好作为结构体的最后一个成员声明,并且它的大小是可变的(可以在运行时更改)。结构体必须除了可变数组成员外,还必须包含至少一个命名成员。用 sizeof 计算结构体大小时,柔性数组成员是不计入结果的。整个结构体的内存是连续的。

在 KVM API中有许多这种类型的结构:包含一个或多个固定大小的字段(Header),后面跟随一个可变大小的数据区域。在这种结构中,Header 通常包含有关数据区域(如其大小、类型或状态)的元数据信息。所以,在如 KVM API 这样的系统编程接口中,FAM 通常被用于处理这种类型的数据结构。

使用 FAM 的一大缺点,就是内存管理的复杂性增加,以及可能存在内存溢出风险。Rust 不直接支持 FAM。

所以,在 rustvmm-sys-util 中这样来实现 FAM :

继续滑动看下一个

Rust 研学 | Rust 安全内参 2024 Part 1

张汉东 觉学社
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存