20199305 2019-2020-2 《网络攻防实践》综合大实践
The Art of The Scam: Demystifying Honeypots in Ethereum Smart Contracts
一、前言
以太坊是一个平台,它上面提供各种模块让用户来搭建应用,如果将搭建应用比作造房子,那么以太坊就提供了墙面、屋顶、地板等模块,用户只需像搭积木一样把房子搭起来,因此在以太坊上建立应用的成本和速度都大大改善。具体来说,以太坊通过一套图灵完备的脚本语言(Ethereum Virtual Machinecode,简称EVM语言)来建立应用。平台之上的应用,其fg实就是智能合约,这是以太坊的核心。合约是一个活在以太坊系统里的自动代理人,他有一个自己的以太币地址,当用户向合约的地址里发送一笔交易后,该合约就被激活。它不仅速度更快、花费更低,而且还避免了手动填写大量表格可能导致的人为错误,解决了传统合约中的信任问题,大幅降低了信任成本。随着智能合约变得越来越受欢迎,价值也越来越高,它们也越来越成为攻击者感兴趣的目标。在过去几年中,一些智能合约被攻击者利用。然而,一种更积极主动的方法的新趋势似乎正在上升,攻击者不再搜索易受攻击的合同。相反,他们试图通过部署包含隐藏陷阱的看似脆弱的合同来引诱受害者进入陷阱。这种新型合约通常被称为蜜罐。
二、论文
1、引入
本文通过调查蜜罐智能合约的普遍性、行为和对以太坊区块链的影响,首次对蜜罐智能合约进行了系统分析。作者开发了一个蜜罐技术的分类法,并使用它来构建蜜獾———一种使用符号执行和定义良好的启发式方法来公开蜜罐的工具。对200多万个智能合约进行了大规模的分析,结果表明,蜜獾不仅具有很高的精度,而且还具有很高的效率。具体而言,本文做出了以下贡献:
-
对以太坊出现的一种新型欺诈蜜罐进行了第一次系统分析;
-
确定蜜罐使用的常见技术,并将其分类;
-
提供蜜獾——一种自动检测以太坊智能合约中的蜜罐的工具;
2、背景介绍
下面提供工作设置所需的背景,包括对智能合约、以太坊虚拟机和以太扫描区块链浏览器的描述。
(1)区块链
一条区块链就是一个对所有人完全公开的分布式账本。一但某些数据被记录到一条区块链中后,数据就会很难改变。新的区块会被发给网络上的所有人,每个人再验证这个区块以确保这个区块没有被篡改过,被验证正确后,每个人都会把新区块加到自己的区块链中。被篡改的区块将会被网络上的其他用户拒绝。
(2)智能合约
本质上来说,智能合约是一段程序,它以计算机指令的方式实现了传统合约的自动化处理。简单讲,智能合约就是双方在区块链资产上交易时,触发执行的一段代码,这段代码就是智能合约。智能合约程序不只是一个可以自动执行的计算机程序,它本身就是一个系统参与者,对接收到的信息进行回应,可以接收和储存价值,也可以向外发送信息和价值。这个程序就像一个可以被信任的人,可以临时保管资产,总是按照事先的规则执行操作。尽管有多种编程语言,Solidity仍然是以太坊中开发智能合约最突出的编程语言。Solidity的语法类似于C和JavaScript的混合体。它附带了许多特定于智能合约的独特概念,例如资金转移或调用其他合约的能力。
(3)以太坊虚拟机
以太坊区块链由相互不信任的节点组成的网络组成,这些节点共同构成分散的公共账本。该分类账允许用户通过向网络提交交易来创建和调用智能合约,这些交易由所谓的矿工处理。矿工在验证数据块期间使用一个专用虚拟机(称为以太坊虚拟机)执行智能合约。EVM是一个基于堆栈、无寄存器的虚拟机,运行低级字节码,由一组操作码指令表示。为了保证合同的终止,从而防止矿工陷入无休止的执行循环,引入了矿工费的概念,它把执行每一条指令的成本联系起来。在发布交易时,发送者必须支付执行智能合约的矿工费用。智能合约的执行导致世界状态σ的修改,世界状态σ是存储在区块链上的一种数据结构,将地址a映射到帐户状态σ[a]
。智能合约的帐户状态由两个主要部分组成:余额σ[a]b
,它包含合同所拥有的以太币,以及存储σ[a]s
,它包含合同的所有数据。存储被设置为存储内容的关键,是智能合约保持执行状态的唯一方法。除了世界状态σ
,EVM还拥有一个交易执行环境I
:
1)正在执行的智能合约的地址Ia
2)交易输入数据Id
3)交易发送方Is
4)交易Iv
EVM本质上可以看作是一个基于交易的状态机,它接受输入σ
和I
,输出一个修正的世界状态σ'
。
(4)以太坊区块链浏览器
以太坊区块链浏览器是一个收集和显示区块链特定信息的在线平台。它充当一个区块链导航器,允许用户轻松查找上的各个区块、交易和智能合约的内容。它在其探索能力的基础上提供多种服务。其中一项服务是,智能合约创建者可以发布其源代码,并确认存储在特定地址下的字节码是编译指定源代码的结果。它还为用户提供了对智能合约发表评论的可能性。
3、以太坊蜜罐
下面提供了蜜罐的一般定义,并介绍了作者对蜜罐的分类
(1)蜜罐
蜜罐是一种智能合约,在用户向其发送资金的前提下,它假装将资金泄漏给任意用户(受害者)。然而,用户提供的资金将被困住,蜜罐创建者(攻击者)将能够取回它们。蜜罐一般分为三个阶段:
1)攻击者部署一个看似脆弱的合约,并以资金的形式设置诱饵;
2)受害人试图利用合约(转移最低所需资金,但未成功);
3)攻击者将诱饵连同受害者丢失的资金一起取走。
攻击者不需要特殊功能就可以设置蜜罐,而只需要必要的资金来部署智能合约和放置诱饵。
(2)蜜罐的分类
作者总共收集了24个蜜罐,并提取了8种不同的蜜罐技术。不同的技术按其操作级别分为三类:
1)以太坊虚拟机
2)Solidity编译器
3)以太坊区块浏览器Etherscan
第一类利用EVM的异常行为欺骗用户。尽管EVM遵循一套严格且公开的规则,但用户仍然可能被存在不一致行为的智能合约所误导。第二类涉及从Solidity编译器引入的问题。虽然有些编译器问题是众所周知的,但其他问题仍然没有记录在案,如果用户不仔细分析智能合约或不在实际条件下测试它,则可能会被忽略。第三类与Etherscan网站上显示的有限信息相关。Etherscan可能是以太坊最著名的区块链浏览器了,许多用户完全信任其中显示的数据,而不怀好意者正好利用了这点。下面来具体讲讲以太坊智能合约中的几种蜜罐技术
(3)蜜罐技术
1)平衡紊乱
上图描述了一个蜜罐的例子,它使用了一种我们称之为平衡紊乱技术。multiplicate
表示合约的余额(本余额)以及此函数调用的交易中包含的值(交易值)。如果此段代码中我们可以看到,函数的调用方包含的值如果大于或等于智能合约的当前余额,则余额会被传输到任意地址。因此,居心不良的用户会相信他所需要做的,就是用一个高于当前余额的值调用这个函数,作为回报,他将收获本钱以及合约余额。但是,如果用户试图这样做,他会很快意识到第5行没有执行,因为第4行的条件不成立。原因是因为在实际执行智能合约之前,余额已随交易值递增。最后的结果只能是合约永远比你有钱。下图为示例代码
2)继承障碍
有一个继承自合同Ownable的合同KingOfTheHill:函数takeAll只允许变量拥有者中存储的地址提取合同余额,但可以通过调用消息值大于当前值的回退函数来修改owner变量。现在,如果用户试图调用函数以将自己设置为所有者,则交易成功。但是,如果他或她后来试图收回余额,交易失败。原因是在第9行声明的变量所有者与在第2行声明的变量所有者不同。调用者希望第9行的所有者将被第2行的所有者覆盖,但事实并非如此。Solidity编译器将这两个变量视为不同的变量,因此在第9行写入调用者也不会导致修改合约Ownable中定义所有者。下图一个继承障碍蜜罐的例子,它使用了继承障碍的技术
3)跳跃空字符串文本
所示的合同允许用户通过向合同的功能投资发送最小数量的以太币来进行投资。所示的合约允许投资者可以通过调用剥离功能来撤回投资。从代码上来看没有什么能阻止投资者剥离比最初投资额更大的资产,有些天真的用户认为可以利用剥离的功能。但是实际上Solidity编译器的编码器将跳过函数loggedtranfer(第14行)参数提供的空字符串文本。其效果是,此参数之后的所有参数的编码向左移动32字节,因此函数调用参数msg接收target的值,而target被赋予currentOwner的值,最后currentOwner接收默认值零。因此,最终loggedtranfer函数执行到currentOwner而不是target。用户试图利用智能合约的明显漏洞,最后却将投资转移给合约所有者。下图为跳过空字符串文本蜜罐的示例
4)类型演绎溢出
在Solidity中,当将变量声明为类型var时,编译器使用类型演绎法从分配给该变量的第一个表达式中自动推断出可能的最小类型。下图描述了一个蜜罐示例,它使用了一种我们称为类型演绎法溢出的技术。最初,合约表明用户可以将投资翻番。但是变量i的类型为uint8,该类型的最大值为255,小于2*msg . value(2 * 0.1 ether = 2 * 1017 wei)。因此第7行的循环将是无限的。尽管如此,如果变量multi小于amountToTransfer,循环仍然可以停止。这是可能的,因为amountToTransfer被赋值为multi,multi最终在第8行发生整数溢出,将小于amountToTransfer,一旦循环退出,合约将会将一个值返还给访问者,尽管其金额最多为255 wei(以太币的最小子面值为1 ether=10^18 wei),因此远远低于用户最初投资的价值,访问者亏大了。
5)未初始化的结构
未初始化结构蜜罐。为了收回合约的余额,合约要求用户要支付一笔以太币并猜测合约中存储的随机数。然而,任何用户都可以很容易地获得随机数的值,因为存储在区块链上的每个数据都是公开可用的。用户只需从区块链中读取随机数,然后通过支付以太币并提供正确的数字来调用函数guessNumber。但是,结构没有像受害者想象的那样通过关键字正确初始化。结果,Solidity编译器将结构(player)中包含的第一个变量的存储位置映射到合约(randomNumber)中包含的第一个变量的存储位置,从而用调用方的地址覆盖随机数,致使第14行的条件失败。值得注意的是,蜜罐创建者知道用户可能试图猜测覆盖的值。因此,创建者在第10行这儿将数字限制在1到10之间,这大大减少了用户生成满足此条件的地址的机会。
6)隐藏状态更新
除正常交易外,Etherscan还显示所谓的内部消息,这些消息是源自其他合约而非用户帐户的交易。但是Etherscan不显示包含空交易值的内部消息。下图中余额被传递给能够猜出计算存储散列的正确值的人。贪婪的用户尝试调用未受保护的SetPass函数,该函数允许使用已知值重写哈希,前提是至少有1个以太币被传输到合约。在分析Etherscan上的内部消息时,用户将找不到调用pashasbeenset函数的任何证据,因此假设pashasbeenset设置为false。但是,为了无声地更新变量passHasBeenSet的状态,蜜罐创建者利用Etherscan执行的过滤,从另一个合约调用函数passHasBeenSet并使用空交易值。因此,通过查看显示在Etherscan上的内部消息,不知情的用户会认为变量设置为false,并放大胆地将以太币传输到SetPass函数。
7)隐藏转移
Etherscan在一个HTML textarea元素中显示源代码,在这个元素中,较大的代码行将只显示到一定的宽度,而代码行的其余部分将被隐藏并单独可见。下面的的合约利用了这个“特性”,在函数drawall的第4行引入了一长串空白,有效地隐藏了下面的代码。如果函数的调用方不是所有者,则隐藏代码将抛出,从而阻止随后向函数的任何调用方传递余额。
8)稻草人合约
用户需要首先调用Deposit(定金)并传输最小数量的以太币。最后,用户调用CashOut函数,该函数执行对TransferLog中存储的合约地址的调用。但是实际上蜜罐创建者没有使用合约log的地址初始化合约。相反,它是用另一个地址初始化的,而此时这个地址指向的是实现同一接口的其他合约,而如果函数AddMessage的调用方不是蜜罐创建者,则执行异常,用户执行的调用将始终失败。另一种选择,是在转移余额之前使用Delegatecall。Delegatecall允许攻击者将用户地址与其自己的地址交换,所以当从Delegatecall返回时,余额将转移给攻击者而不是用户。
4、蜜獾
(1)结构
蜜獾将EVM字节码作为输入,并返回一个关于它检测到的不同蜜罐技术的详细报告作为输出。蜜獾主要由三部分组成:符号分析、现金流分析和蜜罐分析。符号分析的结果随后传播到现金流分析组件和蜜罐分析组件。现金流分析组件使用符号分析的结果来检测合同是否能够接收和转移资金。最后,蜜罐分析组件旨在结合启发式和符号分析的结果来检测本文研究的不同蜜罐技术。这三个组件中的每一个都使用Z3 SMT解算器来检查公式满足性(对变量取值使得某个公式成立)
(2)实现
下面简要描述每个主要组件的实现细节
符号分析
符号分析的目的是收集各种可能有助于以后分析的信息。此信息包括存储写入的列表、执行路径P
的列表、不可行和可行的基本块的列表、执行的乘法和加法的列表以及调用的列表C
。一个调用由元组(Cr,Cv,Cf,Ca,Ct,Cg)
组成,其中Cr
是接收者,Cv
是调用值,Cf
是被调用的合约函数,Ca
是函数参数列表,Ct
是调用类型(即调用或委托调用),Cg
是调用的矿工费。
现金流分析
蜜罐必须能够接收资金(例如受害者的投资)和转移资金(例如攻击者的赃物)。现金流分析的目的是通过排除无法接收或转移资金的合约来提高蜜獾的检测效率。编译器在编译期间添加一个检查,如果非应付函数接收到大于零的交易值,则该检查将还原交易。基于这些观察,首先遍历P中包含的所有可能的执行路径,从而验证合约是否能够接收资金。之后,使用Z3 SMT解算器来验证在执行路径p的给定路径条件下,约束Iv>0
(交易值大于0)是否可以满足,如果p满足约束,便可知资金可以流入合约。同时通过遍历C中包含的所有调用并检查是否存在调用C(其中Cv是符号的或Cv>0)以及遍历P中包含的所有执行路径并检查是否存在以自毁方式终止的执行路径P。如果找到至少一个满足上述条件的调用C或执行路径p,资金可能流出合约。
蜜罐分析
蜜罐分析包括几个子组件。每个子组件负责检测特定的蜜罐技术。每种蜜罐技术都是通过试探法来确定的,下面描述每个子组件的实现
1)平衡紊乱
遍历调用列表C中包含的所有调用,如果有调用符合Cv=Iv+σ[Ia]b
,调用值=交易值(受害者打入的钱)+合约余额,则报告一个平衡紊乱。
2)遗传障碍
在字节码级别检测继承无序相当困难,因为字节码不包含有关继承的信息。因此,我们利用特定于此蜜罐技术的实现细节:一是存在一个写入存储位置的Is
(用于诱骗受害者);二是存在一个调用C,其路径条件包含Is
和存储变量之间的比较,其存储位置不同于1中标识的存储位置。(这个是合约拥有者的地址)
3)跳过空字符串文本
遍历C中包含的所有调用,并检查调用C中Ca
中的参数数是否小于Cf
所期望的参数数,如果小于,说明中间有一个参数被略过,报告一个跳过空字串文本字节
4)类型演绎溢出
遍历C中包含的所有调用,检查调用C中是否存在Cv包含乘法或加法的结果,同时乘法或加法是通过and mask
截断的,值为0xff
(表示8位整数的最大值255),如果存在,这符合类型演绎溢出的现象
5)未初始化结构
使用正则表达式提取结构的存储位置,结构的第一个元素指向基本块中的存储位置,具体地讲就是调用列表C中存在一个调用C,其中Cv包含来自结构的存储位置的值,或者C的路径条件取决于结构的存储位置,那就报告一个未初始化结构
6) 隐藏状态更新
遍历C中包含的所有调用并检查是否存在调用C来检测隐藏状态更新,该调用C的路径条件取决于可以通过另一个函数修改的存储值,而不是进行资金转移(源码中即使资金转移了,还是无法调用成功,因为条件不满足,这个无法满足的条件在受害者看来是满足的)。
7)隐藏转移
如果存在两个连续的调用C和C'存在于同一执行路径p上,且其调用接收者不同,满足Cr∈σ[Ia]s∧Cv=σ[Ia]b,C'r=Is∧C'v=σ[Ia]b
,上报一个隐藏转移
8)稻草人合约
验证两个连续的调用c和c'是否沿着相同的执行路径p存在,其中Cr =C'r
。然后两种情况:一是C在C'之后执行,其中C't=DELEGATECALL∧Cv=σ[Ia]b∧Cr=Is
;另一种是C在C'之前执行,其中C't=CALL∧Is∈C'a
。
5、结果分析
(1)实验结果
在151935个独特的智能合约集上运行HONEYBADGER,在分析的151935份合约中,48487份被标记为现金流合约。换言之,在分析的合约中,只有32%能够接收和发送资金。总共690个合约被确定为蜜罐,其中包括22个平衡障碍(BD)、75个继承障碍(ID)、11个跳过空字符串文字(SESL)、5个类型演绎溢出(TDO)、80个未初始化结构(US)、382个隐藏状态更新(HSU)、14个隐藏传输(HT)以及101个稻草人合约(SMC)
(2)验证
为了确认蜜獾的正确性,作者对标记为蜜罐的合约的源代码进行了手动检查。通过手动扫描源代码以确定检测到的蜜罐技术的特征来验证标记的合约。例如,如果合约被标记为平衡紊乱,则检查源代码是否包含一个函数,该函数在且仅当发送到函数的值大于或等于合约的余额时,才将合同的余额传输给调用方。
真阳性(TP)、假阳性(FP)和精度p(in%)。其中p计算为p=TP/(TP+FP),真阳性表示合同就所报告的技术而言确实是蜜罐,假阳性表示合同就所报告的技术而言不是蜜罐。总的来说,蜜獾显示了非常高的精确度和非常低的假阳性率。分析的8个蜜罐技术中有5个达到了0%的假阳性率。
(3)状态
作者使用简单的启发式方法将每个地址标记为攻击者或受害者。如果一个地址是:1)创建了蜜罐;2)是向蜜罐发送以太币的第一个地址;3)收到的以太币比实际花在蜜罐上的以太币多,则该地址会被标记为攻击者。如果一个地址没有被标记为攻击者,并且收到的以太币少于在蜜罐上实际花费的以太币,则该地址被标记为受害者。最后,利用这些信息判断蜜罐是成功的、中止的还是仍然处于活动状态。如果检测到受害者,则蜜罐标记为成功;如果余额为零且未检测到受害者,则蜜罐标记为中止;如果余额大于零且未检测到受害者,则蜜罐标记为活动。
上图显示了每个蜜罐技术成功、中止和活动的蜜罐数。结果表明,跳过空字符串文字是最有效的蜜罐技术,成功率约为78%,而隐藏传输是最不有效的技术,成功率仅为33%。蜜罐的总成功率似乎很低,约为37%
(4)盈利能力
作者统计了每种蜜罐技术的盈利能力。盈利能力按收到金额计算(支出金额+交易费用),最赚钱的蜜罐是稻草人合同蜜罐,平均值为1.76以太币,而最不赚钱的蜜罐是未初始化结构蜜罐,平均值为0.46以太币。
三、实验复现
1、源代码
(1)下面展示的的是探测工具蜜獾的源代码文件截图:
(2)部分代码截图
2、环境配置
本实验全程都在Kali虚拟机中完成,Ubantu虚拟机亦可
(1)安装solc,solidity编写的以太坊智能合约可通过命令行编译工具solc来进行编译,成为以太坊虚拟机中的代码。solc编译后最终部署到链上形成我们所见到的各种智能合约。在虚拟机中逐条输入以下命令:
sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get install solc
(2)安装EVM(以太坊虚拟机),以太坊底层通过EVM支持智能合约的执行和调用。调用智能合约时根据合约的地址获取合约代码,生成具体的执行环境,然后将代码载入到EVM虚拟机中运行。在虚拟机中逐条输入以下命令:
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get install ethereum
(3)安装Z3求解器,z3是一个底层的工具,它最好是作为一个组件应用到其它需要求解逻辑公式的工具中,来检查逻辑表达式的可满足性。在本篇论文中,它被用来判断被检查的合约中是否存在蜜罐技术,在虚拟机中逐条输入以下命令:
python scripts/mk_make.py --python
cd build
sudo make install
(4)安装Web3.py库,web3.py库交互的共同入口是web3对象。web3对象提供API,用于python开发的应用与以太坊区块链进行交互。在虚拟机中输入以下命令:
pip install web3
相关环境文件如下图
3、评估以太坊蜜罐
环境已经配置好了,接下来就是检验工具效果了,从提前准备好的合约中选取了一款合约MultiplicatorX3.sol
MultiplicatorX3.sol
输入以下命令:
python honeybadger/honeybadger.py -s honeypots/MultiplicatorX3.sol
由上可知,这个合约存在现金流变化以及一种蜜罐技术,即平衡紊乱,说明这是一个蜜罐,同时也证明了该工具是有效的
接下来就继续检测其他的合约,结果在下面的表格种有呈现,其中的合约地址在合约文件中第一行就有:
四、问题与解决
Q:在用蜜獾检查以太坊智能合约时,出现下面的状况
A:上图的大概意思是出现这种状况跟Z3求解器没装好有关,于是首先我检查了下命令是否有问题,然后再次尝试用命令直接在虚拟机种安装,但试过多次无果,遂决定直接在浏览器中下载安装一下,之后就可以用了。但这个过程也耗费了不少时间,我应该一开始就去直接下载的。。。
Q:在安装Web3.py库时发现与Kali虚拟机自带的python不兼容
A:在网上找了个在虚拟机中更新python版本的的办法,通过指令sudo vim /etc/profile
打开文件,在后面加上export PATH=~/anaconda3/bin:$PATH
,然后再运行指令source /etc/profile
就可以了
sudo vim /etc/profile
打开文件,在后面加上export PATH=~/anaconda3/bin:$PATH
,然后再运行指令source /etc/profile
就可以了Q:在检查智能合约时又出现另一种状况
A:上面显示合约文件utils.py
的第303行有问题,仔细看是因为缺了括号(pyhton版本不同带来的困扰),修改完后又出现了其他行数缺括号的现象,一一添上后就可以了。
utils.py
的第303行有问题,仔细看是因为缺了括号(pyhton版本不同带来的困扰),修改完后又出现了其他行数缺括号的现象,一一添上后就可以了。