近期,降维安全实验室(johnwick.io)接到白细胞安全社区(whitecell.io)反馈的一起丢币事件.我们立即与丢币用户取得了联系,沟通丢币的过程,又分析了他丢币时所用的手机系统,可是并没有发现用户有泄露私钥的可能.但是用户购买的价值几十万的数字货币又实实在在地瞬间被黑客转移,且已被兑换成ETH,BTC等主流币.这背后究竟隐藏了何种玄机?下面就带大家一起走进科学.
丢币事件相关交易记录:
https://etherscan.io/address/0xa9cbada29093adaf1ba685ac4c6b0486a05876c7#tokentxns
在分析之前,先给大家简要说明下以太坊的私钥是什么.
它是一个64个字符的16进制数(32字节),用户需妥善保管,一旦丢失,也就意味着失去了对以太坊账户的控制权. 如果我们已经创建过一个以太坊账户,并导出了其私钥,那么即使卸载了钱包应用,只需要在别处再次导入这个私钥,就可以恢复我们的账户.
但是如果用户导入的私钥不足/超过32字节,钱包应用应当如何处理呢? 有些钱包应用(如imToken)会直接拒绝这种畸形数据,并提示用户输入了无效私钥.还有些钱包(如下面案例)会在后台自作主张地帮用户填0/截断成32字节,并成功导入修改后的私钥,强行达成共识.
在此次事件涉及到的钱包应用就如此处理用户输入数据的.经过技术分析,我们定位到问题出在钱包使用的公开库 keythereum上. 如果代码中检测到用户输入的私钥长度小于32字节,那么就使用 Buffer.concat连接合并 Buffer.alloc创建的补齐用全0数据和用户输入的私钥,构成32字节的"私钥".
相关代码片段及注释如下:
privateKeyToAddress: function (privateKey) {
var privateKeyBuffer, publicKey;
privateKeyBuffer = this.str2buf(privateKey);
if (privateKeyBuffer.length < 32) { // <-- "私钥"长度小于32字节
privateKeyBuffer = Buffer.concat([ // 拼接成32字节
Buffer.alloc(32 - privateKeyBuffer.length, 0), // 填0 buffer
privateKeyBuffer // 用户输入"私钥"
]);
}
publicKey = secp256k1.publicKeyCreate(privateKeyBuffer, false).slice(1);
return "0x" + keccak256(publicKey).slice(-20).toString("hex");
},
Buffer.concat = function concat (list, length) {
if (!isArray(list)) {
throw new TypeError('"list" argument must be an Array of Buffers')
}
if (list.length === 0) {
return Buffer.alloc(0)
}
var i
if (length === undefined) {
length = 0
for (i = 0; i < list.length; ++i) {
length += list[i].length
}
}
var buffer = Buffer.allocUnsafe(length)
var pos = 0
for (i = 0; i < list.length; ++i) {
var buf = list[i]
if (!Buffer.isBuffer(buf)) {
throw new TypeError('"list" argument must be an Array of Buffers')
}
buf.copy(buffer, pos)
pos += buf.length
}
return buffer
}
Buffer.allocUnsafe = function (size) {
return allocUnsafe(null, size)
}
function allocUnsafe (that, size) {
assertSize(size)
that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)
if (!Buffer.TYPED_ARRAY_SUPPORT) {
for (var i = 0; i < size; ++i) {
that[i] = 0
}
}
return that
}
回到开头的丢币事件,既然用户没有泄露私钥,那他是如何被黑客转移走所有数字货币的呢? 我们在审查用户整个购买数字货币的流程中,发现了一条奇怪的交易, 在(TxHash 0xebef7c15fb184be685208a32565848c70dc53495091919b0cc6134db090c3bea)用户购买数字货币的账号前置补0后居然是用户自己账号的私钥!!!
具体计算过程如下:
顺着这个线索,我们反复沟通,我们终于还原了客户被攻击的场景:
我们想这样的错误绝对不是孤案,于是我们写了个脚本爬取了以太坊从创世区块开始截至目前的所有地址,约5500万.然后以这些以太坊地址(20字节)为蓝本,前置补0填充构成私钥(32字节),并以此导出公钥,哈希出地址. 再拿这些地址在前述的5500万地址中进行碰撞查询,初步结果让人惊讶,至少有125个地址可以由这种模式得出.我们粗略检查了其中的地址,发现一些账号中的数字货币疑似已被黑客洗走.我们将这些守株待兔收割的黑客称作"狩零人",并将此种攻击命名为"狩零人"攻击.BTW,此类攻击已加入我们的智子威胁感知系统,使用本系统的合作伙伴可以第一时间获知预警信息.
部分碰撞出的地址如下:
来源:
发布人:降维安全
声明:该文观点仅代表作者本人,不代表火讯财经立场。火讯财经系信息发布平台,仅提供信息存储空间服务。
如文章涉及侵权, 请及时致函告之,本站将第⼀时间删除⽂章。邮箱:840034348@qq.com