本文描述了一位用户通过 Flashbots Auction 恢复被黑客盗取的 ENS 代币的过程。用户借助先进的技术与工具,克服了一系列困难,成功找回价值约 13000 美元的代币,并在确保代币确实属于受害者后将其归还。这段经历展示了区块链技术在应对网络安全和资金盗窃时的重要性。
Flashbots Auction 真是太棒了。它允许高级以太坊用户做一些曾经几乎不可能的事情(至少对于那些不经营几个大型以太坊挖矿池的人来说)。由于围绕 MEV 提取机器人引起的广泛关注,我不会怪你认为这只是它的唯一用途。但是,本周我使用它从一个被黑的账户中找回了一袋 ENS 代币(当时价值约为 13,000 美元)并将其归还给了合法所有者。听起来很有趣?那么我来告诉你我是怎么做到的。
在绝望之下,推特用户 LittleBitPrince 在一条公开推文中发布了他的 12 个单词的种子短语。他最近被黑客攻击,所有资金和 NFT 都被盗。在最后一次绝望的反击中,这位用户向全世界广播了他的种子短语,以阻止黑客完全逍遥法外。
LittleBitPrince 随后删除了推文*,但它大致是这样的:
“我的一个以太坊账户被黑了:0xcaC8E5397C09d1b1503Ab45A5fc7F8428BCf6DE5。我的所有代币和 NFT 都没了。有 260 个 ENS 代币可以领取,如果你能在黑客醒来之前领取它们,就可以得到它们。我的种子短语是:program deny train foot scrap marble anxiety oblige hybrid clean [xxx] [xxx]。不客气。
那些不是他真正的种子单词或地址,但用户确实删除了最后两个单词。
黑客显然没有意识到该账户有资格获得可观的 ENS 空投。任何试图将 ETH 发送到该账户以支付索赔的尝试都将迅速被黑客抢走。即使 ENS 以某种方式被索赔,他们也很可能会立即被转移到黑客那里。
*在看到他的推文被删除后,我征得了 LittleBitPrince 的许可来发布这一内容,他表示可以,所以不要对我不满。
无论出于何种原因,受害者在推文中排除了他种子短语的最后两个单词。在进行任何其他操作之前,我必须首先找出这两个单词是什么。如果你已经知道种子短语的工作原理(或者不在乎),可以跳过此步骤。
去掉最后两个单词后,破解可能看起来是一项非常困难的任务。毕竟,英语中有超过五十万个单词,对吗?其实并不是。让我简单介绍一下种子短语是如何工作的。广泛使用的种子短语来自比特币改进提案 BIP-39。有一组已知的 2048 个单词,以特定顺序排列(为了查找效率是按字母顺序的)。每个单词代表 11 位数据。单词在单词列表中的索引被转换为位,并将这些位字符串连接在一起。例如,单词 “program” 是列表中的第 1375 个单词(0 开头计数)。1375 的二进制表示为 10101011111。所以这是前 11 位。对每个后续的单词应用相同的过程,你将得到 132 位数据。在这 132 位数据中,前 128 位用于熵,最后 4 位用于校验和。
从这 128 位熵转换到私钥还有其他几个步骤,但我不会详细说明,因为这与故事无关。如果你想了解更多,可以阅读 BIP-44。
这一切的意思是,去掉最后两个单词后,仅失去了 18 位熵。这意味着如果要进行暴力破解,我们只需尝试 262,144 种组合。对任何现代硬件来说这简直是小菜一碟。
恢复种子短语的最有效方法是循环遍历所有这些组合,计算校验和,推导出私钥并比较产生的以太坊地址。但是,由于我在与时间赛跑,可能需要 30 分钟或更长时间才能将其编码到脚本中,我决定走一条稍微不那么高效的路线,利用我现有的工具组合以下脚本,节省时间:
const HDWallet = require('ethereum-hdwallet')
const bip39 = require('bip39');
const fs = require('fs')
const seedStart = 'program deny train foot scrap marble anxiety oblige hybrid clean'
const words = fs.readFileSync('./words.txt').toString('utf-8').split('\r\n')
for(let x = 0; x < words.length; x++){
for(let y = 0; y < words.length; y++){
const mnemonic = seedStart + ' ' + words[x] + ' ' + words[y]
const valid = bip39.validateMnemonic(mnemonic)
if(valid){
const wallet = HDWallet.fromMnemonic(mnemonic)
const address = wallet.derive(`m/44'/60'/0'/0/0`).getAddress().toString('hex')
if(address.toLowerCase() === 'caC8E5397C09d1b1503Ab45A5fc7F8428BCf6DE5'.toLowerCase()) {
console.log(mnemonic)
process.exit()
}
}
}
console.log(x + ' of ' + words.length)
}
一个种子短语可以生成多个密钥对,我假设该账户是由第一个路径派生而来的(即 `m/44'/60'/0'/0/0`)。幸好,这个假设是正确的。否则,这可能会需要更长时间才能破解。
这个脚本比上述理想解决方案的时间要长,因为它还在某种程度上进行暴力破解校验和位。但是多亏现有的库,我能够在不到 5 分钟的时间内构建这个脚本。每一分钟都是宝贵的,因为很可能还有其他黑帽正在竞争这个奖品。我知道以这种方式测试 4,194,304 种组合仍然相对快速(不到 5 分钟),而写这个幼稚的暴力破解程序节省的时间很容易弥补效率的降低。
手握种子短语和私钥后,我开始找回 ENS 代币。当时我并不知道黑客是否有机器人可以抢走发送到该地址的任何 ETH。但这是一种相当常见的设置,基于这条推文,我假设情况就是这样,否则所有者不会放弃账户。这意味着我必须原子性地发送 ETH,索赔 ENS,然后将 ENS 转回到我控制的账户。进入 Flashbots Auctions。
Flashbots Auctions 允许用户私下向矿工提交一系列交易,这些交易不会出现在公共内存池中——这样它们就无法被抢跑交易者和夹击机器人分析——并且保证按提供的顺序归为一组。这些特性对于多种用例非常有用(提取 MEV 是突出的一个),但在这种情况下,你也许能看到它如何被用来规避黑客的机器人。
Flashbots 提供了一个 javascript 库,它处理准备和提交交易包的大部分繁重工作。他们提供的 示例代码 是一个优秀的起点,这正是我在这里用来尽快发起包的东西。除了填写配置值之外,我们需要修改的部分在这里:
const signedTransactions = await flashbotsProvider.signBundle([\
{\
signer: wallet,\
transaction: legacyTransaction\
},\
{\
signer: wallet,\
transaction: eip1559Transaction\
}\
])
`signBundle` 接受一个数组,包含我们想要提交的交易(连同签名者),并按我们希望它们被挖矿的顺序排列。我需要执行 3 笔交易以使恢复工作。
第一步很简单,只需以下对象,应该是显而易见的:
{
from: myAddress,
to: hackedAddress,
gasPrice: GAS_PRICE,
gasLimit: 21000,
chainId: CHAIN_ID,
value: ethers.utils.parseEther('0.06'),
nonce: myNonce++
}
下一步将稍微复杂一些,因为我实际上不确定 ENS 空投的运作方式。我不想浪费时间去挖掘智能合约来找出。相反,我imported 被黑账户到 MetaMask 中,并通过 ENS 领取网站上的索赔流程。我跟随这个过程,一直到最后一步,MetaMask 确认窗口出现时,然后我从那里复制了输入数据:
我使用这段数据准备了第二个交易对象(数据被截断):
{
from: hackedAddress,
to: '0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72',
gasPrice: GAS_PRICE,
gasLimit: 150897,
chainId: CHAIN_ID,
nonce: hackedNonce++,
data: '0x761229030000000000000000...'
}
0xC1836… 是 ENS 代币和空投索赔合约地址。最后一笔交易相对简单,我使用类似于上述的方法进行代币转移。
{
from: hackedAddress,
to: '0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72',
gasPrice: GAS_PRICE,
gasLimit: 100000,
chainId: CHAIN_ID,
nonce: hackedNonce++,
data: '0xa9059cbb0000000000000000...'
}
对于 Gas Price,我选择了一个比当前市场价格高的值。我只需要足够的金额使得矿工愿意接受交易包,但我并不需要与其他机器人竞争同样的 MEV 机会。在这种情况下支付大额贿赂是没有必要的。
准备好交易包后,下一步是进行模拟。这让你知道你的交易是否会失败,并且是一个很好的合理性检查,确保到目前为止你准备的所有内容都是正确的。Flashbots 提供的示例代码在这里进行了模拟:
const simulation = await flashbotsProvider.simulate(signedTransactions, targetBlock)
// 使用 TypeScript 鉴别
if ('error' in simulation) {
console.warn(`Simulation Error: ${simulation.error.message}`)
process.exit(1)
} else {
console.log(`Simulation Success: ${JSON.stringify(simulation, null, 2)}`)
}
如果模拟失败,它会给你提供有用的信息并退出。在我的案例中,一切都准备好了,所以我启动了包,并祈祷一切顺利。这一段时间你的手心会出汗,你会紧张地盯着终端输出期待一个积极的结果。在几个区块内,我的交易包被包含在内,我的账户中获得了 260 个 ENS。成功!
总体来说,从发现推文到在我账户中拥有 ENS 代币,花了不到 30 分钟。
现在我已经将资金安全地放在自己的账户中,是时候与 LittleBitPrince 联系,告诉他这个好消息了。他的故事有一些奇怪之处,还有一些我发现的其他信息,我不会详细说明,但简而言之,在归还之前,我想确保这些 ENS 代币确实属于他。他可能从其他地方得到了这些种子单词,希望有人来归还给他,而不是直接给真正的所有者。在这种时刻,你永远不能太小心。
我通过推特 DM 联系了他。我还找到了一些这个账户在遭到攻击之前已久与之互动的地址。重要的是,其中一个地址包含了足够的资产,假如黑客控制了这个账户,他们早就会把它掏空。我要求 LittleBitPrince 使用这个账户为我签名一个我提供的消息。在所有环节都确认后,我将 ENS 代币转给了他,结果他欣喜若狂。
谢谢阅读。如果你愿意,可以关注我的 twitter。
- 原文链接: medium.com/@kanewallmann...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!