这是一个缓冲区溢出越界写漏洞
漏洞存在于在 libraries/chain/webassembly/binaryen.cpp 文件的 78 行,
- Function binaryen_runtime::instantiate_module:
- for (auto& segment : module->table.segments) {
- Address offset = ConstantExpressionRunner<TrivialGlobalManager>(globals).visit(segment.offset).value.geti32();
- assert(offset + segment.data.size() <= module->table.initial);
- for (size_t i = 0; i != segment.data.size(); ++i) {
- table[offset + i] = segment.data[i]; <= OOB write here !
- }
- }
这里的 table 是一个 std :: vector 包含在函数表中的名称, 在将元素存储到 table 中时,|offset| 字段没有被正确检查. 注意在设置该值之前是有一个 assert 断言的, 它会检查偏移量, 但不幸的是 assert 仅适用于 Debug 版本, 不适用于发布版本.
table.resize(module->table.initial);
|module->table.initial| 这个代码片段读取的值是根据函数表声明, 在 WASM 文件中的读取的, 该字段的有效值为 01024.
|offset| 字段的值是根据数据段从 WASM 文件中读取的, 它是一个带符号的 32 位值.
所以通过这个漏洞, 我们可以在 table 向量之后的内存, 越界写入一定范围的内容.
重现漏洞过程
1. 编译最新的 EOS 代码 release 版本
./eosio-build.sh
2. 启动 EOS 节点 Start EOS node, 完成如下所有必要的配置
https://github.com/EOSIO/eos/wiki/Tutorial-Getting-Started-With-Contracts
3. 设置一个漏洞合约
我们提供了一个会造成程序崩溃的 WASM 漏洞验证文件 (POC)
在这个 PoC 中, 我们简单的设置了 |offset| 字段引用 0xfffffff 地址, 所以会触发越界写造成程序崩溃
开始测试 PoC:
cd poc
cleos set contract eosio ../poc -p eosio
顺利的话我们会看到 nodeos 进程出现 segment fault 错误
崩溃信息:
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x0000000000a32f7c in eosio::chain::webassembly::binaryen::binaryen_runtime::instantiate_module(char const*, unsigned long, std::vector<unsigned char, std::allocator<unsigned char>>) ()
- (gdb) x/i $pc
- => 0xa32f7c <_ZN5eosio5chain11webassembly8binaryen16binaryen_runtime18instantiate_moduleEPKcmSt6vectorIhSaIhEE+2972>: mov %rcx,(%rdx,%rax,1)
- (gdb) p $rdx
- $1 = 59699184
- (gdb) p $rax
- $2 = 34359738360
- Here |rdx| points to the start of the |table| vector,
And |rax| is 0x7FFFFFFF8, which holds the value of |offset| * 8.
利用漏洞实现远程代码执行
利用此漏洞可以在 nodeos 进程中实现远程代码执行, 漏洞利用方法是将恶意合约上传到受害节点, 并让节点解析恶意合约. 而在真正的攻击中, 攻击者可能会向 EOS 主网络发布恶意合约.
EOS 超级节点解析恶意合约触发漏洞后, 攻击者将可以完全控制这个节点.
攻击者可以窃取超级节点的私钥或控制新区块的内容, 更重要的是攻击者可以将恶意合约打包成一个新块并发布进行攻击, 最终整个网络中的所有节点都将受到攻击并被控制.
我们完成了概念性的漏洞验证程序, 并在基于 64 位 Ubuntu 系统的 nodeos 上进行了测试. 这个漏洞的攻击过程是这样的:
1. 攻击者将恶意合约上传到 nodeos 服务器.
2. 服务器 nodeos 进程解析引发漏洞的恶意合约.
3. 使用越界写入的原生代码, 我们可以覆盖 WASM 模块实例的 WASM 内存缓冲区, 在恶意 WASM 代码的帮助下, 最终可以在 nodeos 进程中实现了任意内存读 / 写操作, 并绕过了 64 位操作系统上的 DEP / ASLR 等常见的攻击缓解技术.
4. 漏洞利用一旦成功, 会启动一个反向 shell 连接攻击者.
来源: https://www.cnblogs.com/wushangguo/p/9114444.html