blockwell.ai KYC Casper Token “牛皮癣广告” 事件分析

作者:知道创宇404区块链安全研究团队 时间:2018年9月13日

一、背 景

2018年9月7日早上1点左右,许多以太坊钱包账户都收到了一种名为blockwell.ai KYC Casper Token代币转进/出账消息:

令人奇怪的是这些账号均表示之前对这个Token的“一无所知”,当这些收到消息用户并没有真正收到提示的那100个代币,而那些提示有100代币转出的用户在之前也并没有拥有过这种代币,这一切都显得“莫名其妙”!更加让一部分人奇怪和担心的是,这些“转进/出账”的操作,都不需要钱包拥有者的的任何密码私钥输入,于是很多不明真相的用户担心自己的钱包是不是被人恶意攻击 ...

二、事件跟踪

首先我们从blockwell.ai KYC Casper Token

https://etherscan.io/token/0x212d95fccdf0366343350f486bda1ceafc0c2d63

交易页面,看到的交易记录都是转出100代币的记录,没有任何转入记录。

再看看实际转账到账户的交易信息

https://etherscan.io/token/0x212d95fccdf0366343350f486bda1ceafc0c2d63?a=0xa3fe2b9c37e5865371e7d64482a3e1a347d03acd

可以看到通过调用这个合约,发起了一笔代币转账,在event logs里可以看到实际的交易

然后具体的交易地址为

https://etherscan.io/tx/0x3230f7326ab739d9055e86778a2fbb9af2591ca44467e40f7cd2c7ba2d7e5d35

整笔交易花费了244w的gas,价值2.28美元,有针对的从500个用户转账给了500个用户。

继续跟踪到转账的from地址:

https://etherscan.io/address/0xeb7a58d6938ed813f04f36a4ea51ebb5854fa545#tokentxns

正如文章开头提到的那样:所有的来源账户本身都是不持有这种代币的,跟踪一下也可以发现,无论是发起交易者还是接受交易者,都没有发生实际代币的变化。

但是这些交易记录确实被保存在链上,那么这个事件的核心问题就在于:“这些记录是怎么被产生并记录的?”

三、事件原理

我们从合约分析入手

https://etherscan.io/address/0x212d95fccdf0366343350f486bda1ceafc0c2d63#code

不出所料,这种事件型的合约代码并不会直接给你开放源代码,通过利用我们404自主研发的智能合约OPCODE逆向工具,反编译后得到如下代码:

源码如下

contract 0x212D95FcCdF0366343350f486bda1ceAfC0C2d63 { mapping(address => uint256) balances; uint256 public totalSupply; mapping (address => mapping (address => uint256)) allowance; address public owner; string public name; string public symbol; uint8 public decimals; event Approval(address indexed _owner, address indexed _spender, uint256 _value); event Transfer(address indexed _from, address indexed _to, uint256 _value); event OwnershipRenounced(address indexed previousOwner); event TransferOwnership(address indexed old, address indexed new); function approve(address _spender, uint256 _value) public returns (bool success) { allowance[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { // 0x841 require(to != address(0)); require(balances[_from] >= _value); require(allowance[_from][msg.sender] >= _value); balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(_value); allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value); Transfer(_from, _to, _value); return true; } function decreaseApproval(address _spender, uint256 _subtractedValue) { // 0xc0e uint oldValue = allowance[msg.sender][_spender]; if (_subtractedValue > oldValue) { allowance[msg.sender][_spender] = 0; } else { allowance[msg.sender][_spender] = oldValue.sub(_subtractedValue); } Approval(msg.sender, _spender, allowance[msg.sender][_spender]); return true; } function balanceOf(address _owner) constant returns (uint256 balance) { // 0xe9f return balances[_owner]; } function renounceOwnership() { // 0xee7 require(owner == msg.sender); emit OwnershipRenounced(owner); owner = address(0); } function x_975ef7df(address[] arg0, address[] arg1, uint256 arg2) { require(owner == msg.sender); require(arg0.length > 0, "Address arrays must not be empty"); require(arg0.length == arg1.length, "Address arrays must be of equal length"); for (i=0; i < arg0.length; i++) { emit Transfer(arg0[i], arg1[i], arg2); } } function transfer(address arg0,uint256 arg1) { require(arg0 != address(0x0)); require(balances[msg.sender] > arg1); balances[mag.sender] = balances[msg.sender].sub(arg1); balances[arg0] = balances[arg0].add(arg1); emit Transfer(msg.sender, arg0, arg1) return arg1 } function increaseApproval(address arg0,uint256 arg1) { allowance[msg.sender][arg0] = allowance[msg.sender][arg0].add(arg1) emit Approval(msg.sender, arg0, arg1) return true; } function transferOwnership(address arg0) { require(owner == arg0); require(arg0 != adress(0x0)); emit TransferOwnership(owner, arg0); owner = arg0; } }

从代码中可以很明显的看到一个特殊的函数x_975ef7df,这是唯一一个涉及到数组操作,且会触发Tranfser事件的函数。

function x_975ef7df(address[] arg0, address[] arg1, uint256 arg2) { require(owner == msg.sender); require(arg0.length > 0, "Address arrays must not be empty"); require(arg0.length == arg1.length, "Address arrays must be of equal length"); for (i=0; i < arg0.length; i++) { emit Transfer(arg0[i], arg1[i], arg2); } }

从代码中可以很清晰的看到, 在对地址列表的循环中,只触发了Transfer事件,没有任何其余的操作。

我们知道遵守以太坊ERC20标准的合约代币才会被承认为ERC20代币,ERC20代币会直接被交易所承认。而 在ERC20标准中规定,transfer函数必须触发Transfer事件,事件会被记录在event log中,是不是说明平台和交易所在获取ERC20代币交易信息,是通过event log事件获取的呢?我们来测试一下。

四、事件复现

首先我们需要编写一个简单的ERC20标准的代币合约

contract MyTest { mapping(address => uint256) balances; uint256 public totalSupply; mapping (address => mapping (address => uint256)) allowance; address public owner; string public name; string public symbol; uint8 public decimals = 18; event Transfer(address indexed _from, address indexed _to, uint256 _value); function MyTest() { name = "we are ruan mei bi"; symbol = "RMB"; totalSupply = 100000000000000000000000000000000000; } function mylog(address arg0, address arg1, uint256 arg2) public { Transfer(arg0, arg1, arg2); } }

合约代币需要规定好代币的名称等信息,然后我们定义一个mylog函数。

这里我们通过remix进行部署(由于需要交易所获得提示信息,所以我们需要部署在公链上)

测试合约地址

https://etherscan.io/address/0xd69381aec4efd9599cfce1dc85d1dee9a28bfda2

注:这里需要强调的是:转出/入账的地址都是可以自定义的,这也就是为什么所有的来源账户本身都是不持有这种代币的原因。

然后直接发起交易

然后我们的imtoken提示了消息,注意收到的消息了包含了我们的代码里 symbol = "RMB";的值rmb

回看余额可以发现没有实际转账诞生。

五、事件目的

通过上面分析及测试,我们发现整个事件最后只说了一件事情就是伪照了大量的虚假交易记录,并没有其他“实质”性的恶意操作,那么这个事件的目的是什么呢?

我们回顾下整个事件的流程:

创建一个token ---> 伪造交易记录 ---> 钱包或交易平台获取交易记录 ---> 推送给用户

如果能找到自定义的消息,那么这是一条完美的消息推广链!这个事件的始作俑者非常聪明的利用了token名这个自定义输入点:blockwell.ai KYC Casper Token,blockwell.ai这个就是本次事件的主要目的,牛皮癣小广告推广这个网站。

看你有的人会说如果只是用来做广告推广的话,完全可以使用代币的真实转账记录来推广,而不是利用伪造交易记录。这里需要提醒大家的是“广告费”的问题,这个“广告费”也就是合约操作里的gas消耗,伪造交易记录只需要Transfer操作的gas可以大大节省这个“广告费”,本次事件整个过程的话费的“广告费”约2.28美元的gas,就实现了对1000个用户有针对的推送了精准广告。

六、总 结

结合以往的各种事件,相比于区块链的各种有限应用场景里,在“恶意”攻击或者利用的层面,攻击者们表现出了惊人的“创意”,本次事件利用了”交易所/平台却盲目信任符合ERC20标准的合约“的特点,使用了以太坊平台本身实现的“bug”,利用了最少的“广告费”实现了精准的用户广告推送。

另外一个值得我们去关注的点就是被用来做消息推送的点是可以自定义的,那么可能导致的风险是非常值得思考的:比如推送钓鱼网站信息,推送其他非法类型的小广告及言论,会导致钱包等平台应用方的用户的其他不可以预期的风险!我们也提醒各大钱包、交易所等平台警惕此类风险,必要时针对这些可自定义点进行相关识别及过滤。

本文分享自微信公众号 - Seebug漏洞平台(seebug_org)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-09-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏企鹅号快讯

解密区块链(五):从BT下载来看区块链激励制度的重要性

区块链本质上是一个分布式的公共账本,这一点在前面的比特币系列文章,区块链系列文章都有过阐述,这样一种分布式的公共账本设计,主要用来解决账本(数据)的安全性和真实...

388100
来自专栏极客编程

区块链面试招聘中可能会被问到的40个问题

这是与数字货币有关的主要问题之一。 事实上,这是一个数字通证被多次使用的条件,因为通证通常由易于克隆的数字文件组成。它只会导致通货膨胀,组织不得不承受巨大的损失...

11220
来自专栏区块链交易系统

区块链怎么赚钱?区块链技术具有的四大作用是什么呢?

区块链交易系统开发公司专业研发互联网金融系统多年,感兴趣的朋友可以联系我们源中瑞。

9710
来自专栏数据猿

【案例】蜂巢链:基于区块链的资产证劵化

资产证券化作为一种新的投融资工具,可以完善资本市场的结构,改善资源配置,提高资金金运作效率,从而促进经济结构的优化,实现盘活存量量、为实体经济服务的目的。 作者...

58880
来自专栏养码场

一周播报|明明BUG这么多,死也不给看代码?这位程序员你咋这么矫情......

Q:有两张表(一个库),一个是用户表、一个是会员表,一个会员记录对应多条用户记录,有一个事务过程如下:每更新用户表中一条记录,更新(update)对应会员表中的...

9420
来自专栏金融民工小曾

【支付系统设计从0到1】支付宝架构中记账功能设计分析

支付宝架构的PPT中对记账部分的说明,分内外账户,如图所示。那么支付宝系统为什么要采用如此设计呢?

32610
来自专栏FreeBuf

快讯 | 继CoinHive后,JQuery官方博客也被黑了

FreeBuf 先前报道过,分发挖矿 javascript 的网站 CoinHive 被黑了,而且影响范围很广,今天,又有新闻爆出,前端领域经久不衰的老牌第三方...

27190
来自专栏SAP最佳业务实践

SAP最佳业务实践:FI–应付账款(158)-1业务概览

用途 该业务情景主要描述的是在应付帐款模块中对供应商相关业务进行会计记帐 优点 应付帐款模块与采购管理模块完全集成 应付帐款模块中的所有明细帐数据会直接...

42390
来自专栏恒思考

区块链学习计划

princeton_bitcoin_book Mastering Bitcoin request_whitepaper

32720
来自专栏码洞

以太坊合约分析之拍卖算法

拍卖的基本流程很简单,一般就是拍卖人亮出一件商品,拼命的宣传吹嘘商品的价值,然后进入一轮又一轮的竞价过程,待拍卖时间结束,价高者和拍卖人一手交货一手交钱。

12840

扫码关注云+社区

领取腾讯云代金券