【漏洞分析】Reflection Token 反射型代币攻击事件通用分析思路
在本篇文章中,我将通过一个攻击事件引出 Reflection Token 攻击事件的一个通用分析思路。
关于 Reflection Token 的其他案例分析,可以参考BEVO代币攻击事件分析及复现一文。
TomInu 攻击事件
TomInu Token 是一个反射型代币 reflection token,于2023-01-26遭到黑客攻击,攻击者获利35577美元。
TomInu(被攻击合约): 0x2d0e64b6bf13660a4c0de42a0b88144a7c10991f
攻击交易: https://phalcon.blocksec.com/tx/eth/0x6200bf5c43c214caa1177c3676293442059b4f39eb5dbae6cfd4e6ad16305668
攻击过程较为简单,攻击者通过几个常规操作就完成获利。

- 攻击者借出闪电贷
- 用 WETH swap 出 1465904852700232013011 TINU
- deliver 1465904852700232013011 TINU
- skim 得到 1733770910894426471783 TINU(在这一步已经完成了获利)
- 把 TINU swap 为 WETH
- 归还闪电贷
本次攻击事件通过推文告警并且进行了分析,但是很可惜分析的结论略显含糊的。红框标注的部分并不是攻击者获利的真正原因。(为什么不是真的)
推文:https://twitter.com/QuintenDes/status/1618730379447508998

并且,目前在在网络上搜索到的所有关于 reflection token 攻击事件的成因分析中提到:“由于攻击者 deliver 了一笔 token ,导致了 pair 中的 token 升值,从而能够 skim 出更多的 token 进行获利”。这类分析大多是理所当然地下结论,没有通过实际的计算推导,妄下结论误导读者。(为什么这么说)
攻击过程很简单,先 deliver 然后 skim,就能够获利了。根据这个攻击过程的特征,我们直接找 rToken增发代码,定位漏洞点。(为什么可以这么做)
以上的几个为什么都将会在后面“为什么”这一章节进行解释,读者可以先带着疑问进行阅读。
漏洞分析
在 _transferStandard 函数中可以看出,TomInu 代币在进行转账 rAmount 时需要收取 team 和 fee 两种手续费,并将扣除了手续费后的 rTransferAmount 转给收款人。

其中 team 手续费 rteam 留存在本合约中,fee 手续费 rfee 则是直接销毁。

此时他们的数量关系应该为:rTransferAmount = rAmount - rteam - rfee
问题出现在 _getRValues 函数中,该函数在计算 rTransferAmount 的过程中忽略了 rteam 参数,计算 rTransferAmount = rAmount - rfee 得到的结果比实际结果要大,造成了 rToken 的增发。也就是说,因为这个计算问题,市场上实际流通的 rAmount 总和是要大于 rTotal 的值的。

代币合约在执行 _transfer() 函数的时候,会将本合约的代币 swap 成 ETH,这个操作使得增发的代币一直累计在 pair 中。

为什么
在这个章节中,会对前面的暴言暴论进行解释
- 为什么推文中的关于漏洞成因的结论是含糊的不准确的。
- 什么说关于 reflection token 攻击事件的成因分析中提到“由于攻击者 deliver 了一笔 token ,导致了 pair 中的 token 升值,从而能够 skim 出更多的 token 进行获利”的分析结论都是错误的。
- 为什么看到攻击过程先 deliver 然后 skim 就完成获利之后,我得出的结论是直接找 rToken增发代码定位漏洞点。
首先我将举几个例子来模拟整个 deliver-skim 的过程,为了使得这个例子尽可能的简单,这个过程中将不考虑任何手续费的收取。
场景1:
只有 attacker 和 pair 持有所有的 token
rTotal 1000, tTotal 100, rate 10
pair: rAmount 500, tAmount 50
attacker: rAmount 500, tAmount 50
attacker deliver 500 rAmount
rTotal 500, tTotal 100, rate 5
pair: rAmount 500, tAmount 100
attacker: rAmount 0, tAmount 0
此时,pair 的 tAmount 从 50 变成了 100。接下来 attacker 将调用 skim 来获利了是不是?
attacker calls pair.skim()
rTotal 500, tTotal 100, rate 5
pair: rAmount 250, tAmount 50
attacker: rAmount 250, tAmount 50
attacker 如愿以偿获利了吗?没有,attacker 和 pair 又回到了最初的 50 tAmount,并不能通过这个操作来进行获利。
场景2:
attacker, pair 以及一些其他用户共同持有所有的 token
rTotal 1000, tTotal 100, rate 10
pair: rAmount 250, tAmount 25
attacker: rAmount 500, tAmount 50
others: rAmount 250, tAmount 25
attacker deliver 500 rAmount
rTotal 500, tTotal 100, rate 5
pair: rAmount 250, tAmount 50
attacker: rAmount 0, tAmount 0
others: rAmount 250, tAmount 50
attacker calls pair.skim()
rTotal 500, tTotal 100, rate 5
pair: rAmount 125, tAmount 25
attacker: rAmount 125, tAmount 25
others: rAmount 250, tAmount 50
pair 回到了原始的 25 tAmount,而 attacker 由原来的 50 亏损到了 25 tAmount。坚定持有的 others 由 25 上涨到了 50 tAmount。
通过上面的两个例子,我们可以得出结论,只有当 attacker 和 pair 所持有的代币份额合计 100% 的情况下,deliver-skim 的操作 attacker 才不会亏损。而两者份额不足 100% 的情况下,deliver-skim 的操作反而会导致 attacker 遭受损失。也就是说 attacker 通过 deliver-skim 的操作无论怎么样都是不赚的,最好的情况是 attacker 和 pair 所持有的代币份额合计 100% 的情况下才不至于亏损。
那么…有没有更好的情况呢?好到…两者持有的代币份额合计起来…超过100%?
比如,发生了代币增发?
场景3:
由于代码存在 rToken 相关的计算错误,导致代币增发的发生,具体表现为 rToken 的实际流通量大于 rTotal 的数量。
rTotal 1000, tTotal 100, rate 10
pair: rAmount 400, tAmount 40
attacker: rAmount 800, tAmount 80
others: rAmount 400, tAmount 40
sum_rAmount = 1600 > rTotal = 1000
pair.rAmount + attacker.rAmount = 1200 > rTotal = 1000
attacker deliver 800 rAmount
rTotal 200, tTotal 100, rate 2
pair: rAmount 400, tAmount 200
attacker: rAmount 0, tAmount 0
others: rAmount 400, tAmount 200
attacker calls pair.skim()
rTotal 200, tTotal 100, rate 2
pair: rAmount 80, tAmount 40
attacker: rAmount 320, tAmount 160
others: rAmount 400, tAmount 200
至此,attacker 从原来的 80 tAmount,通过 deliver-skim 操作获利达到 160 tAmount。
通过这个场景,也就可以解释为什么看到攻击过程中通过 deliver-skim 操作获利时,首先想到的就是去找代码中使得 rAmount 增发的计算操作。因为只有 rAmount 发生了增发,pair 和 attacker 的份额大于 100%,且增发部分需要留存在 pair 合约中,才能够满足通过 deliver-skim 操作进行获利的基础条件。
后记
在 TomInu 攻击事件发生的4个月后,存在相同漏洞的 ADU token 再次被攻击。
ADU token attack tx:https://explorer.phalcon.xyz/tx/bsc/0xc6f6b70e9e35770b699da9b60244d461d02db66859df42319c3207d76931423c
为什么会写这篇文章,因为当我想对这些攻击事件进行学习与分析的时候,我查看了网络上的分析文章,他们给出的漏洞成因含糊不清毫无根据。我读了很多篇分析文章,说辞都是大同小异地糊弄。还没分析清楚就胡乱指点,最终被忽悠的就是真心想研究清楚的人。走了不少弯路,把弯路总结成这篇文章,感谢你的阅读。
【漏洞分析】Reflection Token 反射型代币攻击事件通用分析思路的更多相关文章
- 以太坊钱包开发系列4 - 发送Token(代币)
以太坊去中心化网页钱包开发系列,将从零开始开发出一个可以实际使用的钱包,本系列文章是理论与实战相结合,一共有四篇:创建钱包账号.账号Keystore文件导入导出.展示钱包信息及发起签名交易.发送Tok ...
- Go-Ethereum 1.7.2 结合 Mist 0.9.2 实现代币智能合约的实例
目录 目录 1.什么是 Mist 2.Mist 在哪里下载? 3.Mist 有哪些依赖? 4.如何安装 Mist? 4.1.安装 Mist 依赖工具包 4.2.安装 Mist 4.3.启动 Mist, ...
- AMR无限增发代币至任意以太坊地址的漏洞利用及修复过程
AMR无限增发代币至任意以太坊地址的漏洞利用及修复过程 0x00 项目简述 Ammbr主要目标是打造具有高度弹性且易于连接的分布式宽带接入平台,同时降低上网相关成本.Ammbr打算创建具有人工智能和智 ...
- Facebook币Libra学习-6.发行属于自己的代币Token案例(含源码)
在这个简短的概述中,我们描述了我们在eToro标记化资产背后实施技术的初步经验,即MoveIR语言中的(eToken),用于在Libra网络上进行部署. Libra协议是一个确定性状态机,它将数据存储 ...
- STO(Security Token Offering)证券型通证、代币发行介绍
STO(Security Token Offering)证券型通证.代币发行介绍:STO(Security Token Offering)是一个新的融资概念.通过证券化的通证进行融资.早在2017年年 ...
- 通过以太坊发行代币(token)
2017年开始,区块链ICO项目层出不穷,市场热度一波更胜一波,很多ICO都是通过以太坊智能合约发行自己的代币(token),具体怎样才能发行代币呢?本文进行具体详细的介绍. 准备工作 以太坊官网ER ...
- 区块链代币(Token)笔记 — — 术语
前言 接触区块链和数字货币差不多有大半年时间,一直在赶项目进度,现在有空整理补习一下相关的知识,只谈代币不谈区块链
- 证券化代币的时代已经到来,STO将引爆区块链经济
STOs 似乎会在 2019 年取代 ICOs,即使不是完全取代,但置换的比例也会相当大.所有在美上市的公司都将按照 SEC 制定的相关规定进行交易.Vellum Capital 的 CEO 兼管理合 ...
- MakerDAO 代币解释:DAI, WETH, PETH, SIN, MKR(一)
Maker DAO Token Maker DAO 系统是由多个智能合约 ( Sai Tap, Sai Tub, Vox, Medianiser, etc.), 和 ERC-20 代币组成. 他们一起 ...
- 【在 Nervos CKB 上做开发】Nervos CKB 脚本编程简介[3]:自定义代币
原文作者:Xuejie 原文链接:https://xuejie.space/2019_09_06_introduction_to_ckb_script_programming_udt/ Nervos ...
随机推荐
- Web通用漏洞--文件包含
Web通用漏洞--文件包含 文件包含原理 在项目开发过程中,开发人员通常会将重复使用的函数写入单个文件中,在使用该类函数时,直接调用文件即可,无需重新编写,这种调用文件的过程成为文件包含.在文件包含过 ...
- 初识Storm之HelloWorld程序源码
1. 新建一个Maven项目,pom.xml代码如下: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xs ...
- 微信小程序 setData accepts an Object rather than some undefined 解决办法
问题 setData accepts an Object rather than some undefined setData接受一个对象而不是一些定义 让我猜猜, 你一定是在加载index页面(首页 ...
- vue3探索——5分钟快速上手大菠萝pinia
温馨提示:本文以vue3+vite+ts举例,vite配置和ts语法侧重较少,比较适合有vuex或者vue基础的小伙伴们儿查阅. 安装pinia yarn yarn add pinia npm npm ...
- Springboot多种字段copy工具比较
结论:推荐使用spring自带的copy工具,不能copy的手动set 1.springboot自带的BeanUtils.copyProperties package com.admin; impor ...
- 在线问诊 Python、FastAPI、Neo4j — 创建 节点关系
目录 关系:症状-检查 关系:疾病-症状 代码重构 relationship_data.csv 症状,检查,疾病,药品,宜吃,忌吃 "上下楼梯疼,不能久站,感觉有点肿"," ...
- C++类内存分布+ Studio工具
书上类继承相关章节到这里就结束了,这里不妨说下C++内存分布结构,我们来看看编译器是怎么处理类成员内存分布的,特别是在继承.虚函数存在的情况下. 工欲善其事,必先利其器,我们先用好Visual Stu ...
- CIC滤波器仿真与实验过程及结果记录
整理于2023-10-08 0.0 前言: 前面介绍了使用matlab中的Filter Designer工具箱进行CIC抽取滤波器设计的仿真过程与结果.下面在前面的基础上针对现有的[正点原子ZYNQ] ...
- ChatGPT 是如何产生心智的?
一.前言 - ChatGPT真的产生心智了吗? 来自斯坦福大学的最新研究结论,一经发出就造成了学术圈的轰动,"原本认为是人类独有的心智理论(Theory of Mind,ToM),已经出现在 ...
- Java 魔法值处理的四种方法
Java 魔法值处理方案 魔法值的定义 方法一 静态常量(不推荐) 方法二 接口中定义 方法三 定义在实体类 方法四 使用枚举类 enum 总结 魔法值的定义 魔法值是Java中突兀出现在代码中的常量 ...