微信又改版了,为了我们能一直相见
你的加星和在看对我们非常重要
点击“长亭安全课堂”——主页右上角——设为星标🌟
期待与你的每次见面~
前言
2020年11月21日,Pickle Finance的PickleJar遭到攻击,损失了19.76Million Dai。2020年11月22日,Banteg 和 Pickle Finance 团队发表了该攻击构成的技术细节。本文将介绍漏洞的核心原理。
前置知识
AMM:是一种即时兑换模式。通常的交易所(中心化/去中心化),需要买卖双方对价格达成一致,才能完成交易;而AMM 只需买方认可流通池中的Token价格即可立刻完成交易。
流动性:用户将资产转入交易平台之中获得收益,而在平台中的总资产额度,就构成了流通池。流通池中代币总额越多,深度越好,大额交易带来的影响也就更小,不至于因为一笔几十万美元的交易就让价格产生大幅波动。Defi的一个风险点就是流通池不够大,容易产生较大滑点。
漏洞介绍
Pickle Jar 的代码是Fork的Yearn Vaults v1,其中Controler v4合约用于控制其他合约,同时是用户主要调用的入口合约。Pickle Jar在原代码上增加了Jars之间的Swap功能,但该功能对targets的检查不完备,产生了漏洞。
漏洞1: 缺少外部合约地址检查,导致任意代码执行漏洞
我们先找到Pickle Jar新增的函数swapExactJarForJar,位置在Controller-v4.sol,L250
该函数用于Jar之间的转账操作,把Token从_fromJar转移到_toJar 要转移的金额为_fromJarAmount,最少到账金额为_toJarMinAmount。
除此之外,我们可以看到函数还有另外两个参数_targets和_data 这两个参数在L318被正式调用:
我们跟进_execute函数
可见函数的核心就是delegateCall,以此调用外部_target合约函数,input为_data 到这里,我们需要回到swapExactJarForJar函数里面,看一下_target合约地址有哪些限制。找到如下检查
require(approvedJarConverters[_targets[i]], "!converter");
可见,_target只能是approvedJarConverters白名单中的合约。
通过查看approvedJarConverters变量得知,白名单合约仅有两个:
-
UniswapV2ProxyLogic
-
CurveProxyLogic
存在漏洞的是CurveProxyLogic合约,该合约用于管理流动池,因此实现了add_liquidity、remove_liquidity_one_coin两个函数。
阅读两个函数的代码,我们发现了第二处外部合约调用。
在合约的第52行,CurveProxyLogic合约调用了curve变量指向的外部合约。调用的函数名签名为curveFunctionSig,参数为liquidity和0。
可见,curve和 curveFunctionSig直接来自函数的参数,没有进行任何限制或检查。
故而,攻击者可以通过调用ControllerV4.SwapExactJarForJar函数,来以ControllerV4合约作为msg.sender执行任意外部代码。调用链图示如下:
漏洞2: 缺少对_toJar的检查,导致任意命令执行
问题同样在ControllerV4 的 swapExactJarForJar函数中, 该函数缺少对_toJar参数的检查。当执行到323行时,将会调用_toJar地址指向的外部合约的deposit函数。
攻击者可以在外部合约的deposit函数中,将_toJarToken转账到自己的钱包里。
漏洞利用
在本次攻击事件中,攻击者的漏洞利用流程如下:
第一步:调用earn函数
首先,攻击者调用了 pDai 的earn函数,将Jar中所有的DAI币转移给对应的Strategy 在Pickle-Finance中,DAI币对应的Strategy是strategy-cmpd-dai-v2合约 该合约在收到earn函数发过来的DAI币时,会mint等量的cDAI。
攻击者第一步攻击的简要流程图如下:
第二步:利用漏洞1
攻击者利用漏洞1来调用Strategy.withdraw函数
注意两点:
-
Strategy 定义了两个withdraw函数,攻击者调用的是其中参数为IERC20的这个。 -
withdraw函数对msg.sender做了检查,仅允许Controller调用该函数。这里攻击者通过controller的任意命令执行绕过了检查。在这一步中,攻击者将Strategy中所有的cDAI转移到了ControllerV4合约。
第三步:利用漏洞2
接着,攻击者利用漏洞2,将Controller中的所有cDAI转移到自己钱包中。
最后,得到所有cDAI的攻击者来到Compound,将手中的cDAI凭证换为真正的DAI币。至此,整个攻击流程结束。
修复措施
北京时间2020年11月22日,上午11:58,PickleJar关闭了deposit函数。
北京时间 2020年11月22日,下午15:16,Pickle Jar 团队调用了Controller-v4的revokeJarConverter管理函数,从白名单中删除了CurveProxyLogic合约。
虽然从白名单中删除了漏洞合约,使漏洞无法被利用。但是CurveProxyLogic合约中的漏洞依然存在,Pickle Jar团队还在进行进一步修复。
总结
Pickle Jar的子合约CurveProxyLogic中调用了call函数,同时没有检查外部合约地址。这使得攻击者可以执行任意代码,篡改合约的状态。作为合约的开发人员,需要谨慎使用delegatecall和call函数,严格限制这两个函数的参数。
另一方面,合约项目方可以通过赏金计划、漏洞平台等方式吸引社区中的白帽黑客来发现项目中的安全漏洞,帮助项目的完善和安全性提升。最后,项目方可以联系国内有区块链合约安全相关服务的厂家(比如长亭科技)来支持代码审计工作。
引用
-
Pickle Team官方声明
-
yearn.finance核心开发banteg的事件分析
-
攻击者的全部交易 transactions tree
-
pickle finanace 合约代码
-
长亭科技 Chaitin
本文始发于微信公众号(长亭安全课堂):Pickle Jar 盗币事件分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论