-
-
[原创]rust编译平台常见攻击利用方法
-
发表于: 2025-6-2 00:16 401
-
本文由笔者首发于先知社区:rust编译平台常见攻击利用方法-先知社区 (aliyun.com)
更多文章请关注个人博客:ddostalker.github.io
近年来,随着 Rust 语言在高性能、内存安全等领域的优势被广泛认可,越来越多的开发者选择将其应用于后端开发,尤其在 WebAssembly、微服务和高并发场景中表现亮眼。
然而,Rust 生态的快速扩张也带来了新的安全挑战:尽管语言本身通过所有权机制规避了内存安全问题,但 Web 应用层的逻辑漏洞(如 SQL 注入、身份验证绕过)、第三方库的潜在缺陷(如未充分审计的 unsafe
代码滥用)以及对安全实践的过度自信(如忽略输入验证或错误配置 CORS 策略),正在导致 Rust 相关的 Web 安全问题逐渐浮出水面。
社区亟需在享受 Rust 底层安全红利的同时,建立更完善的安全开发规范,加强依赖库审计,并推动 Web 框架的漏洞响应机制,以应对日益复杂的安全威胁。而生活中在线编译的环境随处可见,非常容易成为被攻击的重点对象,而本次就是在阿里 CTF 中出现的一道 Rust 编译类题目,故总结了一套相关的相关知识。
在 Rust 生态中,Cargo.toml
文件如同项目的中枢神经,扮演着至关重要的清单(Manifest)角色。其不仅是一份静态的配置文件,还是是开发者与 Rust 编译工具链(Cargo)之间的核心。通过 toml,开发者能够以声明式语法精确控制项目——从定义包元数据(如版本、作者、许可证)、声明依赖关系(通过本地路径、Git 仓库或官方注册表[crates.io]引入第三方 crate),到定制编译策略(如特性开关、优化级别、目标平台配置),甚至扩展自定义构建脚本。
实现跨项目引用的核心机制,则隐藏在 Cargo.toml
的特定字段中:
若你提到的是管理多个关联的包(多 crate 项目),Cargo 提供了 [workspace] 配置,允许在单一仓库中组织多个 crate,共享依赖和构建缓存。
Rust 的过程宏(Procedural Macros)是元编程(metaprogramming)的核心工具之一,允许开发者在编译时对代码进行动态生成和转换。与声明宏(Declarative Macros)不同,过程宏通过自定义代码逻辑直接操作抽象语法树(AST),实现更复杂的代码生成能力。以下是过程宏的深度解析,而我们也可以通过 Cargo.toml
对其进行引用。
那么如果现在存在一个文件夹拥有如下的 Rust 项目的文件夹。
那么此时可以通过在 testaaa 项目的 Cargo.toml 中使用以下配置来对其他的过程宏项目进行引用。
Rust 的 test 宏提供了便捷的测试的方式,提供简单的 test 宏即可对单个函数到多个项目的调试,可以在 main.rs 以外的 rs 文件下添加以下代码:
我们可以在其中写入我们想要审计的函数并赋予一定的逻辑和参数,然后执行 cargo test 即可完成快速的代码审计测试。
在有的编译平台中,代码并不会在平台进行执行,而是仅对文件编译后再提供下载,那么这种情况下我们需要利用一定的 cargo 特性,来对其进行利用(如编译期执行)。
Rust 的编译存在以下三种执行:
而其中 build 构建执行和过程宏编译时执行是常可以利用的项,它们可以实现编译时期无需运行来进行命令执行。
很多时候我们无法自己创建 build.rs,但是可以利用 Cargo.toml 中的选项。
我们可以将 build 的路径换成任何我们想要的 rs 文件(包括 main.rs !我们可以通过这点在很多有限制的地方进行执行)从而实现 build.rs 执行。
创建一个项目,在 Cargo.toml 中写入以下内容,让 main.rs 本身变为 build 脚本:
然后在项目中写入以下内容,这里是在当前目录下创建如下测试文件:
然后使用 Cargo build 进行仅编译,但我们会发现,即使没有执行的情况下,还是生成了对应的文件。利用这个特性,我们可以将其中生成修改为任意危险操作,亦或是捕获编译机目录、编译时代码名称等等等等。但是要注意的一点是,如果之间进行反弹 shell 的情况下会导致编译异常而被强行中断。
不只是利用本地的 build 脚本,前面我们提到过 Rust 的依赖选项,Rust 通过从 crates.io 下载源码或读取本机的源码编译。由于 cargo 编译时会执行自身及其依赖的 build 脚本,这些 crates 中的 build.rs 文件也会进行运行。我们通过在上传的库中的 build.rs 放入相关危险代码,然后在上传编译平台的代码中引用,也能实现同样的功能。(现在 crates.io 上传的库可以进行删除,溯源的可能性大幅度降低)。
crates 上传教程发布到 crates.io - Rust语言圣经(Rust Course)
前面提到过,在 Rust 的编译期中过程宏也会进行运行,但往往上传编译并不会给我们一个上传完整的项目的机会,而通过单文件在 Cargo.toml 中启用过程宏这个方法,这里巧妙的是,cargo 会直接对 path 的目标文件进行 rustc 编译,也就是说,即使目标文件的结构并不符合 Rust 项目的标准,只要 Rust 代码没有错误都可以将其作为过程宏生成。如此一来我们遍可以在项目 a 的 main.rs 里实现过程宏,虽然不能被正常编译,但是可以在项目 b 中引用,从而实现编译。
我们可以在过程宏中构建以下代码,来进行危险操作,然后以 main.rs 上传第一次代码,此时我们是不需要配置 toml 的,当然这个编译也是不被通过的。
之后我们只需要通过其他手段获得当前文件的名称与路径,再将路径放入第二个编译项目的 toml 中:
此时编译第二个文件就将会执行第一次上传文件的代码了。过程宏编写指南
如果编译目标机器是属于 nightly 或可以对项目外的部分进行改写,那么就可以使用 cargo 的一些其他危险特性,如 metabuild 等等。
Cargo不稳定特性
排除上文所说不执行的情况,平台提供执行功能的可能性还是要更高一些,一般来说这些编译平台都会再 docker 中运行,此时,我们需要去获取更多的信息,对当前的平台信息一些获取,才能更好的进一步利用。
可以提供读取目录来判断是否处于 docker 环境:
通过执行命令来进行环境信息收集:
在很多情况我们并不能正常回显或是不能正常的触发一个程序,这种情况下我们改那么我们可以通过"挖空"程序本体,换入我们的代码,这样一来,程序就可以通过下一次调用的流程的来触发。
以下是 linux 中 rustc 和 cargo 的常见位置:
下面就是一个替换内容的例子,我们可以通过 cargo 特性利用内容中的方法,执行 Rust 代码将 cargo 替换为以下的脚本,这个脚本将会把 flag 作为错误状态码依次返回(运行一次返回一次),我们便可以通过不断运行编译流程,此时返回的错误码就为 flag 的 ascii 码。
题目来源: 阿里云CTF
见附件
题目模拟了一个简化版的GitHub Action系统,主要功能包括: