Search
moon
sun

Uwerx Hack Analysis

ํƒœ๊ทธ
RedSpider
Blockchain
Property
hack_template.png
์ž‘์„ฑ๋‚ ์งœ
2023/08/18

1. Overview

2023๋…„ 8์›” 2์ผ, Uwerx ํ”Œ๋žซํผ์€ flashloan Attack ์„ ์ด์šฉํ•œ ๊ณต๊ฒฉ์œผ๋กœ ์•ฝ 32.7๋งŒ(174WETH )๋‹ฌ๋Ÿฌ์— ํ•ด๋‹นํ•˜๋Š” ๊ธˆ์•ก์ด ํƒˆ์ทจ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
๊ณต๊ฒฉ์ž๋Š” WERX๋ฅผ ์†ก๊ธˆํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ทจ์•ฝ์ ์„ ์ด์šฉํ•˜์—ฌ ํ† ํฐ ๊ฐ€๊ฒฉ์„ ์กฐ์ž‘ํ•˜๊ณ  ์ด์ต์„ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค.
Uwerx์ธก์—์„œ๋Š” ๊ณต๊ฒฉ์ž์—๊ฒŒ 20%์˜ bounty๋ฅผ ์ œ์‹œํ•˜์˜€์œผ๋‚˜, ํ˜„ ์‹œ์ ์—๋Š” ์•„์ง ๊ณต๊ฒฉ์ž์˜ ์ง€๊ฐ‘์— ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ํ™•์ธํ•˜์˜€์Šต๋‹ˆ๋‹ค.

What is Uwerx?

Uwerx ํ”Œ๋žซํผ์€ ํ”„๋ฆฌ๋žœ์„œ์™€ ๊ณ ๊ฐ์„ ์—ฐ๊ฒฐ ์‹œ์ผœ์ฃผ๋Š” ๋ธ”๋ก์ฒด์ธ ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.
Proof-of-work(POW) ์‹œ์Šคํ…œ์„ ํ™œ์šฉํ•˜์—ฌ ๋ชจ๋“  ํฌํŠธํด๋ฆฌ์˜ค ๋ฐ์ดํ„ฐ์˜ ์ง„์œ„์—ฌ๋ถ€์™€ ์†Œ์œ ๊ถŒ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ์•ˆ์ „ํ•˜๊ณ  ํˆฌ๋ช…ํ•œ ๊ฑฐ๋ž˜๋ฅผ ๋ณด์žฅํ•˜๋ฉฐ, ๋ชจ๋“  ๊ฒฐ์ œ๋Š” Uwerx ์˜ ์•”ํ˜ธํ™”ํ(WERX)๋ฅผ ํ†ตํ•ด์„œ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค. WERXํ† ํฐ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž๋Š” ํ”„๋ฆฌ๋ฏธ์—„ ๊ธฐ๋Šฅ์— ์—‘์„ธ์Šคํ•˜๊ฑฐ๋‚˜, ์„œ๋น„์Šค ํ• ์ธ๋“ฑ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. Vulnerability Analysis

ํ•ด๋‹น ์ทจ์•ฝ์ ์€ Uwerx ์ปจํŠธ๋ž™ํŠธ์˜ ํ•จ์ˆ˜ _transfer ์—์„œ uniswapPoolAddress ๋กœ WERX๋ฅผ ์†ก๊ธˆํ• ๋•Œ, ์†ก๊ธˆํ•˜๋ ค๋Š” ์–‘์˜ 1% WERX์„ ์ถ”๊ฐ€์ ์œผ๋กœ burnํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.
์‹ค์ œ ๊ณต๊ฒฉ์ž ๋ฐ ์ปจํŠธ๋ž™ํŠธ๋“ค์˜ ์ฃผ์†Œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
Attacker Address : 0x6057A831D43c395198A10cf2d7d6D6A063B1fCe4 Attacker Contract : 0xDA2CCfC4557BA55eAda3cBEbd0AEFfCf97Fc14CA Attack Transaction : 0x3b19e152943f31fe0830b67315ddc89be9a066dc89174256e17bc8c2d35b5af8 Victim Address : 0x4306B12F8e824cE1fa9604BbD88f2AD4f0FE3c54
๋จผ์ €, ์ทจ์•ฝ์ ์ด ๋ฐœ์ƒํ•˜๋Š” Uwerx ์ปจํŠธ๋ž™ํŠธ ๋‚ด๋ถ€ ํ•จ์ˆ˜๋ฅผ ๋จผ์ € ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
โ€ข
Uwerx.sol
โ—ฆ
function transfer
function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); // from, to, amount return true; }
Solidity
๋ณต์‚ฌ
transfer ๋Š” WERX ์†ก๊ธˆ์„ ํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜๋กœ, owner๋ฅผ _msgSender()๋กœ ์ง€์ •ํ•˜๊ณ  from ์ฃผ์†Œ์ง€๋ฅผ owner๋กœ ์„ค์ •ํ•˜์—ฌ _transfer ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
โ—ฆ
function _transfer
function _transfer( address from, address to, uint256 amount ) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; [1] // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } if (to == uniswapPoolAddress) { uint256 userTransferAmount = (amount * 97) / 100; uint256 marketingAmount = (amount * 2) / 100; uint256 burnAmount = amount - userTransferAmount - marketingAmount; [2] emit Transfer(from, to, userTransferAmount); emit Transfer(from, marketingWalletAddress, marketingAmount); _burn(from, burnAmount); [3] } else { emit Transfer(from, to, amount); } _afterTokenTransfer(from, to, amount); }
Solidity
๋ณต์‚ฌ
[1] _transfer ๋ฅผ ์‚ดํŽด๋ณด๋ฉด, ์†ก๊ธˆ์ž์˜ ์ž”๊ณ ๊ฐ€ ์†ก๊ธˆํ•  ์–‘๋ณด๋‹ค ํฐ์ง€ ํ™•์ธํ•˜๊ณ , ์†ก๊ธˆ์ž์˜ balance๋ฅผ amount๋งŒํผ ๋นผ์ค๋‹ˆ๋‹ค.
[2] ๊ทธ๋‹ค์Œ if๋ฌธ์„ ๋ณด๋ฉด to์˜ ์ฃผ์†Œ์ง€๊ฐ€ uniswapPoolAddress ๋กœ ๊ฐ€๋Š” ๊ฒฝ์šฐ, burnAmount ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š”๋ฐ amount - (amount * 97)/100 - (amount * 2)/100 ๋งŒํผ ๊ฐ’์ด ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค.
์‹ค์งˆ์ ์œผ๋กœ, burnAmount ๋Š” ์ดˆ๊ธฐ amount์˜ 1%๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
[3] burnAmount ๋ฅผ ์ธ์ž๋กœ _burn์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
โ—ฆ
function _burn
function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; [4] } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); }
Solidity
๋ณต์‚ฌ
[4] ์œ„์˜ [1] ์˜ ๊ณผ์ •์—์„œ ์†ก๊ธˆ์ž์˜ balance๊ฐ’์„ ์ด๋ฏธ ๊ฐ์†Œ์‹œ์ผฐ์œผ๋‚˜, ์œ„์—์„œ ๊ณ„์‚ฐํ•œ burnAmount ๋งŒํผ์„ ๋” ๊ฐ์†Œ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, amount*1.01 ๋ฅผ ๋บ€๋‹ค๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
1% ์ด์ง€๋งŒ, ๋Œ€๋Ÿ‰์˜ ๊ธˆ์•ก์ธ ๊ฒฝ์šฐ ์ถ”๊ฐ€๋กœ burn์„ ์‹œํ‚ค๋ฉด Pool์˜ ํ† ํฐ ๊ฐ€๊ฒฉ์— ํฐ ๋ณ€๋™์ด ์ƒ๊ธฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ณต๊ฒฉ์ž๋Š” ์ด ์ทจ์•ฝ์ ์„ ํ†ตํ•˜์—ฌ, Pool์˜ WERXํ† ํฐ์„ burn ์‹œ์ผœ ๊ฐ€๊ฒฉ ๋ณ€๋™์„ ํ†ตํ•ด ์ฐจ์ต์„ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค. ๊ณต๊ฒฉ์ž๋Š” ํฐ ๊ธˆ์•ก์„ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด flashloan์„ ํ†ตํ•ด ๊ณต๊ฒฉ์„ ์ง„ํ–‰ํ•˜์˜€๊ณ , ์ž์„ธํ•œ ๊ณต๊ฒฉ ๊ธฐ๋ฒ•์€ ํ•˜์œ„ ์—์„œ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

3. Attack Flow

3.1 Summary

์ „์ฒด ๊ณต๊ฒฉ ํ๋ฆ„์„ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•˜๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
๐Ÿ’ก
1. ๊ณต๊ฒฉ์ž๋Š” Attacker Contract๋ฅผ deployํ•ฉ๋‹ˆ๋‹ค. 2. ๊ณต๊ฒฉ์ž๋Š” flashloan์œผ๋กœ 20000WETH๋ฅผ ๋นŒ๋ฆฝ๋‹ˆ๋‹ค. 3. Uniswap V2: WERX 2 Pool ์„ ํ†ตํ•ด WETHโ†’WERX๋กœ swap์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. 4. ๊ณต๊ฒฉ์ž๋Š” imbalance๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด Uniswap V2: WERX 2 Pool๋กœ 4429817WERX ๋ฅผ ์†ก๊ธˆํ•˜์˜€์Šต๋‹ˆ๋‹ค. 5. ๊ณต๊ฒฉ์ž์˜ ์†ก๊ธˆ์œผ๋กœ ์ธํ•ด Uniswap V2: WERX 2 Pool์˜ balance๊ฐ’๊ณผ reserve๊ฐ’์˜ ์ฐจ์ด(imbalance)๊ฐ€ ์ƒ๊ธฐ๊ฒŒ ๋˜๊ณ , ๊ณต๊ฒฉ์ž๋Š” ์ด๋ฅผ ์•…์šฉํ•˜๊ธฐ ์œ„ํ•ด skim(uniswapPoolAddress(0x1)) ์„ ํ˜ธ์ถœํ•˜์—ฌ, uwerx์˜ ์ทจ์•ฝํ•œ _transfer ๊ฐ€ ํ˜ธ์ถœ๋˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค. _transfer ๋Š” Uniswap V2: WERX 2 Pool์˜ imbalance๊ฐ’์˜ 1% ๋ฅผ ์ถ”๊ฐ€์ ์œผ๋กœ burn์„ ์‹œํ‚ค๊ฒŒ ๋˜๊ณ , WERXํ† ํฐ์˜ ๊ฐ€๊ฒฉ์ด ๊ธ‰ ์ƒ์Šนํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ ํ›„ sync() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด ์ž”๊ณ ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. 6. ๊ณต๊ฒฉ์ž๋Š” ์ฐจ์ต์„ ์–ป๊ธฐ ์œ„ํ•ด WERXโ†’WETH๋กœ swap์„ ํ•ฉ๋‹ˆ๋‹ค. 7. ๊ณต๊ฒฉ์ž๋Š” flashloan์„ repayํ•˜๊ณ , ์ฐจ์ต์„ ์–ป๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
uniswapPoolAddress
์•„๋ž˜๋Š” Step 1~7์— ๋”ฐ๋ผ, Attack Flow์™€ Token Flow๋ฅผ ๋„์‹ํ™”์‹œํ‚จ ์ž๋ฃŒ์ž…๋‹ˆ๋‹ค.

3.2 Attack Transaction

์‹ค์ œ ์‚ฌ์šฉ๋œ transaction์„ ๋ณด๋ฉด์„œ ์ž์„ธํ•˜๊ฒŒ ์„ค๋ช…๋“œ๋ฆฌ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
โ€ข
Attack Transaction : 0x3b19e152943f31fe0830b67315ddc89be9a066dc89174256e17bc8c2d35b5af8
1.
๊ณต๊ฒฉ์ž๋Š” flashloan์„ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด ๊ณต๊ฒฉ์ž ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
2.
๊ณต๊ฒฉ์ž ์ปจํŠธ๋ž™ํŠธ๋Š” Balancer์—์„œ flashloan์„ ์ด์šฉํ•˜์—ฌ 20,000WETH ๋ฅผ ๋Œ€์ถœํ•ฉ๋‹ˆ๋‹ค.
3.
ํ˜„์žฌ Uniswap V2: WERX 2 Pool์—๋Š” 5,097,936WERX , 174 WETH ๊ฐ€ ๋‹ด๊ฒจ์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.
4.
flashloan์„ ํ†ตํ•ด ๋ฐ›์€ 20,000WETH ๋ฅผ Uniswap V2: WERX 2 Pool(Pool๋กœ ์ค„์—ฌ์„œ ๋ถ€๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.)์„ ์ด์šฉํ•˜์—ฌ ๋Œ€๋žต5,053,637WERX ๋กœ swapํ•ฉ๋‹ˆ๋‹ค. swapํ›„์— Pool์—๋Š” 44,298WERX ์ •๋„๊ฐ€ ๋‚จ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
5.
๊ณต๊ฒฉ์ž๋Š” imbalance๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด Pool์— ๋‚จ๊ฒจ์ง„ WERX์˜ 100๋ฐฐ์ธ 4,429,817WERX ๋ฅผ Pool๋กœ ์†ก๊ธˆํ•ฉ๋‹ˆ๋‹ค.
6.
๊ณต๊ฒฉ์ž๋Š”Uniswap V2: WERX 2 Pool์˜ skim ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ทจ์•ฝ์ ์„ ํŠธ๋ฆฌ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
โ€ข
function skim
function skim(address to) external lock { address _token0 = token0; // gas savings address _token1 = token1; // gas savings _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0)); _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1)); }
Solidity
๋ณต์‚ฌ
skim ์€ ํ˜„์žฌ ์—…๋ฐ์ดํŠธ ๋˜์ง€ ์•Š์€ ํ’€์˜ reserve ๊ฐ’๊ณผ ์‹ค์ œ ํ’€์˜ balance๋ฅผ ๋งž์ถ”๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜๋กœ, ์ฐจ์•ก๋งŒํผ to ์˜ ์ฃผ์†Œ์ง€๋กœ ๋ณด๋‚ด์„œ, ๋‘ ๊ฐ’์„ ๋งž์ถฐ์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
skim์—์„œ๋Š” ํ† ํฐ ์Œ์— ๋Œ€ํ•ด _safeTransfer ๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
value๊ฐ’์€ ํ˜„์žฌ Pool ์˜ ์‹ค์ œ ์ž”๊ณ ์™€ reserve๊ฐ’์˜ ์ฐจ์•ก์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
์œ„์˜ 5๋ฒˆ์—์„œ ๊ณต๊ฒฉ์ž๊ฐ€ Pool์— 442,9817WERX ๋ฅผ ์†ก๊ธˆํ–ˆ์œผ๋ฏ€๋กœ, ๊ทธ ์†ก๊ธˆ์•ก์ด ์ฐจ์•ก์ด ๋ฉ๋‹ˆ๋‹ค.
โ€ข
function _safeTransfer
function _safeTransfer(address token, address to, uint value) private { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED'); }
Solidity
๋ณต์‚ฌ
_safeTransfer ์—์„œ๋Š” token.call์„ ์‚ฌ์šฉํ•ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ, abi.encodeWithSelector(SELECTOR, to, value) ๋Š” ํ˜ธ์ถœํ•  ํ•จ์ˆ˜์™€ ํ•ด๋‹น ํ•จ์ˆ˜์— ์ „๋‹ฌ๋  ์ธ์ˆ˜๋ฅผ ์ธ์ฝ”๋”ฉํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. SELECTOR๋Š” ํ˜ธ์ถœํ•˜๋ ค๋Š” ํ•จ์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฐ”์ดํŠธ ๊ฐ’์ž…๋‹ˆ๋‹ค.
bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
Solidity
๋ณต์‚ฌ
SELECTOR๋Š” transferํ•จ์ˆ˜๋กœ ์ •์˜๋ฅผ ํ•ด๋‘์—ˆ์Šต๋‹ˆ๋‹ค.
๊ฒฐ๊ณผ์ ์œผ๋กœ, Uniswap V2: WERX 2 Pool์˜ skim โ†’ _safeTransfer โ†’ token.call โ†’ transfer(Uwerx) โ†’ _transfer(Uwerx) ์ˆœ์„œ๋Œ€๋กœ ํ˜ธ์ถœ์ด ๋ฉ๋‹ˆ๋‹ค.
๊ณต๊ฒฉ์ž๋Š” skim์„ ํ˜ธ์ถœํ•  ๋•Œ, ์ˆ˜์‹  ์ฃผ์†Œ๋ฅผ uniswapPoolAddress(0x1) ๋กœ ์„ค์ •ํ•˜์—ฌ ์ทจ์•ฝ์ ์„ ํŠธ๋ฆฌ๊ฑฐ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. skim ๋ถ€ํ„ฐ transfer (Uwerx)๊นŒ์ง€ ๊ณผ์ •์€ ์œ„์— ๊ธฐ์ˆ ํ•˜์˜€์œผ๋ฏ€๋กœ, _transfer (Uwerx) ๋ถ€ํ„ฐ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
โ€ข
function _transfer
function _transfer( address from, address to, uint256 amount ) internal virtual { //...// //...// unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } if (to == uniswapPoolAddress) { //...// uint256 userTransferAmount = (amount * 97) / 100; uint256 marketingAmount = (amount * 2) / 100; uint256 burnAmount = amount - userTransferAmount - marketingAmount; _burn(from, burnAmount); //...//
Solidity
๋ณต์‚ฌ
Uwerx.sol์˜ _transfer ์— ์ธ์ž๊ฐ€ ์–ด๋–ป๊ฒŒ ๋“ค์–ด๊ฐ€๋Š”์ง€ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
โ—ฆ
From : Uniswap V2: WERX 2 Pool
โ—ฆ
To : uniswapPoolAddress(0x1)
โ—ฆ
Amount : 4,429,817WERX ( reserve ๊ฐ’๊ณผ ์‹ค์ œ ํ’€์˜ balance ์ฐจ์•ก)
โ—ฆ
burnAmount : 4,429,817WERX/100 = 44,298WERX
์ด๋ ‡๊ฒŒ ์ธ์ž ๊ฐ’์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๊ณ , if๋ฌธ์œผ๋กœ ์ง„์ž…ํ•˜์—ฌ _burn ์„ ํ˜ธ์ถœํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
โ€ข
function _burn
function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; [4] } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); }
Solidity
๋ณต์‚ฌ
_burn ํ•จ์ˆ˜์˜ account์— Uniswap V2: WERX 2 Pool ์˜ ์ฃผ์†Œ๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๊ณ , ๊ณต๊ฒฉ์ž๊ฐ€ ๊ธฐ์กด์— Pool์— ์žˆ๋Š” WERX์˜ 100๋ฐฐ(4,429,817WERX) ๋งŒํผ ์†ก๊ธˆํ•ด์„œ imbalance๋ฅผ ๋งŒ๋“ค์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, Pool์€ 1% ์ฆ‰, ๊ฑฐ์˜ Pool์— ์žˆ๋Š” ์ž”์•ก๋งŒํผ burn์„ ๋‹นํ•˜๊ธฐ๋•Œ๋ฌธ์— ์†Œ๋Ÿ‰์˜ WERX๋งŒ ๋‚จ๊ฒŒ๋ฉ๋‹ˆ๋‹ค.
7.
๊ทธ ๋‹ค์Œ์œผ๋กœ, sync๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ Pool์˜ ์ž”๊ณ ๋ฅผ update์‹œํ‚ต๋‹ˆ๋‹ค.
pool์—๋Š” 100WERX ์™€ 20,174WETH ๊ฐ€ ๋‚จ๊ฒŒ๋ฉ๋‹ˆ๋‹ค.
8.
์ฐจ์ต์„ ์–ป๊ธฐ ์œ„ํ•ด ๊ณต๊ฒฉ์ž๋Š” 4. ๊ณผ์ •์—์„œ Pool์— ์†ก๊ธˆ ํ•˜๊ณ  ๋‚จ์€ 623,820WERX ๋ฅผ WERX -> WETH ๋กœ ๋‹ค์‹œ swap์„ ํ•ฉ๋‹ˆ๋‹ค. swap์„ ํ•˜๋ฉด WERX์˜ ํ† ํฐ ๊ฐ€์น˜๊ฐ€ ๊ธ‰ ์ƒ์Šนํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, Pool์— ์žˆ๋Š” ์ „์ฒด 20,174WETH ๋ฅผ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
9.
์ตœ์ข…์ ์œผ๋กœ, ๊ณต๊ฒฉ์ž๋Š” flashloan์œผ๋กœ ๋ฐ›์€ 20,000WETH ๋ฅผ ์ƒํ™˜ํ•˜๊ณ , 174WETH ๋งŒํผ์˜ ์ด์ต์„ ์–ป๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ธˆ์•ก์€ ๊ณต๊ฒฉ์ž์—๊ฒŒ ์•„์ง ๊ทธ๋Œ€๋กœ ๋‚จ์•„์žˆ์Šต๋‹ˆ๋‹ค.

4. Conclusion

์ด๋ฒˆ ์ทจ์•ฝ์ ์€ Uwerx์˜ _transfer ํ•จ์ˆ˜์—์„œ uniswapPoolAddress ๋กœ ์†ก๊ธˆํ•˜๋Š” ๋กœ์ง์—์„œ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค. flashloan Attack์€ ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ฐœ์ƒํ•˜๋Š” ๊ณต๊ฒฉ์œผ๋กœ ํฐ ๊ธˆ์ „์  ์†์‹ค๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ† ํฐ์„ transfer ํ•˜๊ฑฐ๋‚˜ burn ํ•  ๋•Œ๋Š” ๋ถ€์ •ํ™•ํ•œ ๊ณ„์‚ฐ์ด ๋˜์ง€ ์•Š๋Š”์ง€ ์ •๋ฐ€ํ•œ ํ™•์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
78ResearchLab์—์„œ๋Š” ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ์ฝ”๋“œ/์„œ๋น„์Šค ๋กœ์ง์— ๋Œ€ํ•œ ์ •์ /๋™์  ๋ถ„์„์œผ๋กœ ๋ณด์•ˆ ๊ฐ์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
๋ณด์•ˆ ๊ฐ์‚ฌ๋ฅผ ํ†ตํ•ด ์ด๋Ÿฌํ•œ ๊ณต๊ฒฉ์œผ๋กœ๋ถ€ํ„ฐ ์•ˆ์ „ํ•œ Web3 ํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค.
contact@78researchlab.com ๋กœ ๋ฉ”์ผ์„ ๋ณด๋‚ด์ฃผ์‹ ๋‹ค๋ฉด ์—ฐ๋ฝ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

5. Reference