天价手续费分析:我不是真土豪
2021-09-29 22:35:23 Author: wiki.ioin.in(查看原文) 阅读量:29 收藏

By:Thinking@慢雾安全团队

事件背景

分析源自一笔转账金额 10w USDT,手续费却高达 7,676 枚 ETH 的天价手续费交易。

(https://etherscan.io/tx/0x2c9931793876db33b1a9aad123ad4921dfb9cd5e59dbb78ce78f277759587115)

关键代码分析

根据此 Issue 

(https://github.com/ethereumjs/ethereumjs-monorepo/issues/1497 ) 中的描述,开始了分析。

我们以倒叙的方式来说明问题,这样更方便理解。核心问题是 `ethjs-util` `intToBuffer` 不支持传入浮点型的数据。

首先看看关键的代码,提得比较多的是 `ethereumjs` 的问题,主要聚焦讨论的是 `maxPriorityFeePerGas``maxFeePerGas` 这两个参数的值,由于传入的浮点型,导致计算错误,得到了错的手续,从而发生了天价手续的事件

经过分析后,这两个参数都是经过 `toBuffer` 进行处理的,所以开始分析 `toBuffer`

(https://github.com/ethereumjs/ethereumjs-monorepo/blob/cf95e04c6a/packages/tx/src/eip1559Transaction.ts#L200-L201)

this.maxFeePerGas = new BN(toBuffer(maxFeePerGas === '' ? '0x' : maxFeePerGas))    this.maxPriorityFeePerGas = new BN(      toBuffer(maxPriorityFeePerGas === '' ? '0x' : maxPriorityFeePerGas)    )

`toBuffer` 会去调用 `ethjs-util``intToBuffer` 函数,这个函数主要处理了两件事情。

(https://github.com/ethjs/ethjs-util/blob/e9aede6681/dist/ethjs-util.js#L1950)

function intToBuffer(i) {  var hex = intToHex(i);  return new Buffer(padToEven(hex.slice(2)), 'hex');}

1. 将 int 转成 Hex

(https://github.com/ethjs/ethjs-util/blob/e9aede6681/dist/ethjs-util.js#L1939)

function intToHex(i) {  var hex = i.toString(16); // eslint-disable-line  return '0x' + hex;}

2. 判断是否可以被 2 整除,如果不行需要在字符开头添加一个 0 ,这里主要是为了能够成功的将数据 2 个 1 组写入到 buffer。

(https://github.com/ethjs/ethjs-util/blob/e9aede6681/dist/ethjs-util.js#L1920)

function padToEven(value) {  var a = value; // eslint-disable-line
if (typeof a !== 'string') { throw new Error('[ethjs-util] while padding to even, value must be string, is currently ' + typeof a + ', while padToEven.');  } if (a.length % 2) { a = '0' + a;  } return a;}

以出错的示例数据:`33974229950.550003` 进行分析,经过 `intToBuffer` 函数中的 `intToHex``padToEven` 处理后得到 `7e9059bbe.8ccd` ,这部分浏览器 js 和 nodejs 的结果都是一致的。

不一致的地方是在 new Buffer 的操作:

`new Buffer(padToEven(hex.slice(2)), 'hex');`

处理方式分析:浏览器 js

通过 webpack 打包好 js 文件并对文件进行引用,然后在浏览器上进行调试分析。

首先输入的示例字符 `33974229950.550003` 会进入到 intToBuffer 的函数中进行处理。

同步分析 intToBuffer 的处理过程,这部分和 0x01 的代码逻辑是一样的,处理转换部分得到的结果是 `7e9059bbe.8ccd`

接下来分析如何将转换后的字符填充进入的 buffer 中,通过这步可以得到 buffer 的内容是 `126, 144, 89, 187, 14, 140, 205` 对应的是 `7e, 90, 59, bb, e, 8c, cd`。

> 0x7e -> 126> 0x90 -> 144> 0x59 -> 89> 0xbb -> 187> 0xe -> 14> 0x8c -> 140> 0xcd -> 205

这里发现 `e.` 这部分的小数点消失了,于是开始解小数点消失之迷,追踪到 `hexWrite` 这个函数,这个函数会将得到的数据 2 个一组进行切分。然后用了 parseInt 对切分后的数据进行解析。

然而 `parseInt('e.',16) -> 14`  ===  `parseInt('e',16) -> 14` 消失的小数点被 `parseInt` 吃掉了,导致最终写入到 buffer 中的数据发生了错误,写入 buffer 的值是 `7e9059bbe8ccd`

处理方式分析:nodejs

由于浏览器上出问题的是 `7e9059bbe.8ccd` 在写入 buffer 的时候小数点被 `parseInt` 吃掉了导致数据出错,但是经过分析,node 的数据也是错误的,且产生错误的原因是和浏览器的不一样。

首先我们先看下如下的示例:

node 三组不同的数据填充到 buffer 得到的结果居然是一样的,经过分析 node 的 buffer 有个小特性,就是 2 个一组切分后的数据,如果没法正常通过 hex 解析的,就会把那一组数据以及之后的数据都不处理了,直接返回前面可以被正常处理的那部分数据。可以理解为被截断了。这部分可以参考 node 底层的 buffer 中 `node_buffer.cc` 中的代码逻辑。

> new Buffer('7e9059bbe', 'hex')<Buffer 7e 90 59 bb>
> new Buffer('7e9059bbe.8ccd', 'hex')<Buffer 7e 90 59 bb>
> new Buffer('7e9059bb', 'hex')<Buffer 7e 90 59 bb>

执行结果的比较

node 由于会将原始数据 `7e9059bbe.8ccd` 中的 `e.` 及之后的数据进行截断,所以最终错误的值是 `7e9059bb`相比正确的值 `07e9059bbe` 小。

node 的执行结果: 

浏览器由于会将原始数据 `7e9059bbe.8ccd` 中的 `.` 吃掉,所以最终错误的值是 `7e9059bbe8ccd`,相比正确的值 `07e9059bbe` 大很多。

浏览器的执行结果:

问题的原因

`ethjs-util` `intToBuffer` 函数不支持浮点型的数据,且在这个函数中没有判断传入的变量类型,来确保变量类型是预期内的。由于 `ethereumjs` `toBuffer` 引用了 `ethjs-util` `intToBuffer` 进行处理,也没有对数据进行检查。导致了这次事件的发生,所幸最终善良的矿工归还了“天价手续费 7626 ETH”。

吸取的教训

从第三方的库的角度来看,在编码过程中应该要遵循可靠的安全的编码规范,在函数的开头要对传入的数据进行合法性的检查,确保数据和代码逻辑是按照预期内执行。

从库的使用者的角度来看,使用者应该要自行阅读第三方库的开发文档和对接文档,并且也要对代码中接入第三方库的逻辑进行测试,通过构造大量的数据进行测试,确保业务上能够正常按照期望执行,保证高标准的测试用例的覆盖率。

参考:

https://github.com/ethereumjs/ethereumjs-monorepo/issues/1497

https://blog.deversifi.com/23-7-million-dollar-ethereum-transaction-fee-post-mortem/

https://www.chainnews.com/news/611706276133.htm

往期回顾

Avalanche 链上闪电贷攻击事件 —— Zabu Finance 被黑分析

慢雾 | 智能合约安全审计服务全面增加 Solana 安全审计项

DeFi 安全事件频发,如何规避黑资产,保持平台合规性?

慢雾:Cream Finance 被黑简要分析

Bluehelix 入驻慢雾区,发布「安全漏洞与威胁情报赏金计划」

慢雾导航

慢雾科技官网

https://www.slowmist.com/

慢雾区官网

https://slowmist.io/

慢雾 GitHub

https://github.com/slowmist

Telegram

https://t.me/slowmistteam

Twitter

https://twitter.com/@slowmist_team

Medium

https://medium.com/@slowmist

币乎

https://bihu.com/people/586104

知识星球

https://t.zsxq.com/Q3zNvvF

火星号

http://t.cn/AiRkv4Gz

链闻号

https://www.chainnews.com/u/958260692213.htm


文章来源: https://wiki.ioin.in/url/nYWW
如有侵权请联系:admin#unsafe.sh