近日,慢雾安全团队观测到一起自动化盗币的攻击行为,攻击者利用以太坊节点 Geth/Parity RPC API 鉴权缺陷,恶意调用 eth_sendTransaction 盗取代币,持续时间长达两年,单被盗的且还未转出的以太币价值就高达现价 2 千万美金,还有代币种类 164 种,总价值难以估计(很多代币还未上交易所正式发行)。如下图:
攻击过程
慢雾安全团队综合受害者情报、Reddit 资讯及蜜罐日志分析,回溯攻击行为
可能为:
全球扫描 8545 端口(HTTP JSON RPC API)、8546 端口(WebSocket JSON RPC API)等开放的以太坊节点,发送 eth_getBlockByNumber、eth_accounts、eth_getBalance 遍历区块高度、钱包地址及余额
不断重复调用 eth_sendTransaction 尝试将余额转账到攻击者的钱包
当正好碰上节点用户对自己的钱包执行 unlockAccount 时,在 duration 期间内无需再次输入密码为交易签名,此时攻击者的 eth_sendTransaction 调用将被正确执行,余额就进入攻击者的钱包里了
备注:
unlockAccount 函数介绍
该函数将使用密码从本地的 keystore 里提取 private key 并存储在内存中,函数第三个参数 duration 表示解密后 private key 在内存中保存的时间,默认是 300 秒;如果设置为 0,则表示永久存留在内存,直至 Geth/Parity 退出。详见:
https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_unlockaccount
攻击时间线
我们在 Etherscan 上对攻击者钱包地址进行细致的分析,得到如下主要攻击时间线:
2016/02/14 03:59:14 PM 第一次 IN(进账),这天是情人节
2016/02/16 06:33:30 PM 第二次 IN,时隔 2 天,猜测自动化攻击程序首次上线
2016/05/19 07:46:33 PM 第一次 OUT(出账),此时共 IN 51 笔
2016/07/20 06:35:57 PM 第二次 OUT,此时共 IN 57笔
2017/05/11 06:34:35 PM Shapeshift(知名交易所) IN 7 笔,跨度 71 天
2017/06/10 02:39:53 AM OUT 最后一笔,此时共 IN 约 207 笔
2017/06/21 07:46:49 AM f2pool(知名矿池) IN 36 笔,跨度 4 小时
这种时间线的跟踪可以侧面辅助分析攻击者的行为痕迹。
影响态势
通过慢雾安全团队独有的墨子(MOOZ)系统对全球约 42 亿 IPv4 空间进行扫描探测,发现暴露在公网且开启 RPC API 的以太坊节点有 1 万多个。这些节点都存在被直接盗币攻击的高风险。
防御建议
更改默认的 RPC API 端口,配置方法如:--rpcport 8377 或 --wsport 8378
更改 RPC API 监听地址为内网,配置方法如:--rpcaddr 192.168.0.100 或 --wsaddr 192.168.0.100
配置 iptables 限制对 RPC API 端口的访问,举例:只允许 192.168.0.101 访问 8545 端口:
iptables -A INPUT -s 192.168.0.101 -p TCP --dport 8545 -j ACCEPTiptables -A INPUT -p TCP --dport 8545 -j DROP
账户信息(keystore)不要存放在节点上 (因为账户不在节点上,所以就不会用到 unlockAccount 了)
任何转账均用 web3 的 sendTransaction 和 sendRawTransaction 发送私钥签名过的 transaction
私钥物理隔离(如冷钱包、手工抄写)或者高强度加密存储并保障密钥的安全
进一步思考
通过这个事件的追踪调查及攻击手法的完整复现,我们越发意识到网络空间遵循黑暗森林法则,这个法则参考自《三体》:“宇宙就是一座黑暗森林,每个文明都是带枪的猎人,像幽灵般潜行于林间,轻轻拨开挡路的树枝,竭力不让脚步发出一点儿声音,连呼吸都必须小心翼翼,他必须小心,因为林中到处都有与他一样潜行的猎人,如果他发现了别的生命,能做的只有一件事,开枪消灭之。
我们仔细复盘了这起持续两年且现在还在活跃的攻击的所有细节,如果我们是攻击者,我们一个脚本工程就可以轻松拿下全球数以万计,甚至百万千万的数字资产。我们需要特别提下攻击者的手法,不知道大家有没有注意到,攻击的第一步为什么调用的是 eth_getBlockByNumber 来获取区块高度?这个调用的细节是:eth_getBlockByNumber("0x00", false),如果区块高度不是最新的,那么这个调用就会报错,后续也就没必要执行钱包地址、余额等查询操作,因为余额不准确,且最后一步的转账操作肯定没法完成。这种攻击逻辑的设计,对于这个攻击场景来说是一种非常暴力美学的设计。
我们从上面提到的“攻击时间线”来看,攻击者其实很不一般,这种潜伏的攻击发生在以太坊历史上第一个知名的黑客攻击事件 The DAO 事件之前(2016/06/17),且当时是以太坊技术被市场正式认可的时期。可以看出,攻击者是非常早期的以太坊技术研究者,并且很懂黑客工程化技术。攻击者在实战过程中不断优化这套工程。
我们还对 RPC API 相关模块功能进行进一步分析,发现了一些潜在的安全风险,也是需要大家注意的,比如:如果 RPC API 启用了 personal 模块,就可以通过 personal_unlockAccount 方法爆破账户密码,假如爆破成功,就可以一次性实现解锁 + 转账。如果 RPC API 启用了 miner 模块,就可以通过 miner_setEtherbase 方法修改挖矿的钱包地址。
在我们的墨子(MOOZ)系统的全网探测中,我们发现这些暴露在公网的以太坊节点开启的 RPC API 模块不尽相同。这为更复杂的攻击提供了差异条件。
从防御分析角度,我们还发现,Geth 等的日志机制不完善,无法记录 RPC API 请求的来源 IP(配置 --debug、--verbosity 5 均无效),导致直接在被攻击的以太坊节点上取证溯源攻击者 IP 是一件很麻烦的事。
虽然以太坊本身的健壮性已经经受住考验,但是安全是一个整体。通过这个事件我们可以看出以太坊生态的一些安全缺陷,有的安全缺陷可能会被认为这是一种机制,需要使用者注意,但这个对使用者来说做安全的门槛太高。高门槛的安全一定是会更容易滋生这种大攻击事件。
最后,希望我们这篇文章的披露能给这个生态带来更多安全。
声明:本文来自慢雾区,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。