Flash Loan
Flash Loan Attack은 탈중앙 금융 서비스인 DeFi를 포함한 Smart Contract에서 발생할 수 있는 공격 입니다.
Flash Loan Attakc에 대해서 이해하기 위해서는 먼저 Flash Loan이 무엇인지를 알아야 합니다.
Flash Loan이란 "동일한 Transaction 내에서 대출을 상환할 수 있는 경우 담보 없이 대출을 받을 수 있는 무담보 대출" 입니다.
어떻게 담보 없이 대출을 받을 수 있을까요? Flash Loan은 동일한 Transaction 내에 대출이 상환되지 않을 경우 대출을 실행하지 않도록 하는 조건이 반드시 들어있습니다. 즉, 대출 상환이 불가능한 거래에 대해서는 대출을 실행하지 않기 때문에 채무 불이행이 발생할 수 없어 무담보 대출이 가능한 것 입니다.
보통 이러한 Flash Loan은 아비트라지(Arbitrage)와 같은 차익 거래에 이용됩니다.
예를 들어, A와 B거래소 간의 XRP(Ripple) 코인의 가격 차이가 100원이 발생한다고 가정해보겠습니다. A 거래소에서 코인을 매수한 뒤 B 거래소에 입금하여 이러한 가격 차익을 노려볼 수 있습니다.
차익 거래는 그 특성 상 상한선이 정해져있지만, 거래할 수 있는 코인이 많으면 많을 수록 더 많은 돈을 벌 수 있는 구조입니다. 초기 자본이 적은 사용자는 Flash Loan을 이용하여 자본 없이 차익 거래를 시도하고 이를 통해 수익을 얻을 수 있습니다.
Flash Loan의 또 다른 사용법은 바로 담보 스왑 입니다. 이는 Flash Loan을 통해 받은 대출(코인 또는 토큰, 이하 암호화폐)을 담보로 새로운 암호화폐를 대출 받는 것을 의미합니다.
이와 관련된 자세한 내용은 아래에서 더 설명드리도록 하겠습니다.
Flash Loan Attack
Flash Loan Attack은 Flash Loan을 이용하여 무담보 대출을 받은 뒤, 대출 받은 암호화폐를 이용해 시장을 교란하여 이득을 취하고 바로 대출을 갚아버리는 방식으로 이뤄집니다.
Flash Loan Attack의 흐름을 조금 더 자세히 보자면, 아래와 같은 흐름으로 작동합니다.
1. Flash Loan을 이용하여 코인 A를 대출 받음
2. 대출 받은 코인 A를 이용하여 코인 B의 가치를 조작함
3. 코인 B의 가치가 조작되었기 때문에 코인 A로 더 많은 코인 B를 스왑할 수 있음
4. 스왑된 코인 B를 이용하여 이득을 본 뒤, 다시 스왑하여 Flash Loan을 상환함
dTdX 공격 사례 분석
Flash Loan Attack Transactions : 0xb5c8bd9430b6cc87a0e2fe110ece6bf527fa4f170a4bc8cd032f768fc5219838
Ethereum Transaction Hash (Txhash) Details | Etherscan
Ethereum (ETH) detailed transaction info for txhash 0xb5c8bd9430b6cc87a0e2fe110ece6bf527fa4f170a4bc8cd032f768fc5219838. The transaction status, block confirmation, gas fee, Ether (ETH), and token transfer are shown.
etherscan.io
1. Flash Loan을 통한 무담보 대출
dYdX의 Flash Loan 기능을 활용하여 10,000 wETH를 대출받았습니다.
(당시 wETH와 wBTC의 정상적인 교환 비율은 38.5 wETH/wBTC)
구분 | 흐름 | Balance |
Flash Loan | wETH +10,000 | wETH +10,000 |
2. 담보 스왑
10,000 wETH 중 5500 wETH를 Compound에 담보로 설정하여 112 wBTC를 다시 대출 받았습니다.
(스왑으로 인해 wETH와 wBTC의 교환 비율은 49.11 wETH/wBTC가 됨)
구분 | 흐름 | Balance |
Flash Loan | wETH +10,000 | wETH +10,000 |
Compound wETH 담보 예치 | wETH -5,500 | wETH +4,500 |
Compound wBTC 담보 대출 | wBTC +112 | wETH +4,500 wBTC +112 |
3. 마진 거래를 통한 wETH 가치 조작
1300 wETH를 bZx 마진 거래에 예치하여 약 5배에 해당하는 5637.62376 wETH를 획득합니다.
이후, bZx 마진 거래는 KyberSwap을 활용하여 5637.62376 wETH를 51.34456 wBTC로 교환합니다.
(대규모 스왑으로 인해 wETH와 wBTC의 교환 비율은 109.80 wETH/wBTC으로 조작됨)
구분 | 흐름 | Balance |
Flash Loan | wETH +10,000 | wETH +10,000 |
Compound wETH 담보 설정 | wETH -5,500 | wETH +4,500 |
Compound wBTC 담보 대출 | wBTC +112 | wETH +4,500 wBTC +112 |
bZx 마진 거래 wETH 예치 | wETH -1,300 | wETH +3,200 wBTC +112 |
bzX 마진 거래 wETH x5 입금 | wETH +5,637 | wETH +8,837 wBTC +112 |
bZx 마진 거래 wETH x5 예치 | wETH -5,637 | wETH +3,200 wBTC +112 |
bZx 마진 거래 wBTC 스왑 | wBTC +51 | wETH +3,200 wBTC +163 |
4. 조작된 가치의 wETH 스왑을 통한 차익 거래 성공
Compound로부터 대출 받은 112 wBTC를 wETH로 스왑합니다. 그런데 이때 wETH의 가격이 "3. 마진 거래를 통한 wETH 가치 조작"으로 영향을 받아 5,500 wETH가 아닌 6,871.41274 wETH로 교환됩니다.
(최종적으로 wETH와 wBTC의 교환 비율은 61.35 wETH/wBTC가 됨)
즉, 공격자는 1,871 만큼의 이득을 보게 되는 것 입니다.
구분 | 흐름 | Balance |
Flash Loan | wETH +10,000 | wETH +10,000 |
Compound wETH 담보 설정 | wETH -5,500 | wETH +4,500 |
Compound wBTC 담보 대출 | wBTC +112 | wETH +4,500 wBTC +112 |
bZx 마진 거래 wETH 예치 | wETH -1,300 | wETH +3,200 wBTC +112 |
bzX 마진 거래 wETH x5 입금 | wETH +5,637 | wETH +8,837 wBTC +112 |
bZx 마진 거래 wETH x5 예치 | wETH -5,637 | wETH +3,200 wBTC +112 |
bZx 마진 거래 wBTC 스왑 | wBTC +51 | wETH +3,200 wBTC +163 |
wBTC <-> wETH 스왑 | wBTC -112 | wETH +3,200 wBTC +51 |
wBTC <-> wETH 스왑 | wETH +6,871 | wETH +10,071 wBTC +51 |
5. Flash Loan 상환
Flash Loan을 통해 무담보 대출 받았던 wETH 10,000를 상환합니다. 상환을 하였음에도 불구하고 71 wETH가 남습니다.
구분 | 흐름 | Balance |
Flash Loan | wETH +10,000 | wETH +10,000 |
Compound wETH 담보 설정 | wETH -5,500 | wETH +4,500 |
Compound wBTC 담보 대출 | wBTC +112 | wETH +4,500 wBTC +112 |
bZx 마진 거래 wETH 예치 | wETH -1,300 | wETH +3,200 wBTC +112 |
bzX 마진 거래 wETH x5 입금 | wETH +5,637 | wETH +8,837 wBTC +112 |
bZx 마진 거래 wETH x5 예치 | wETH -5,637 | wETH +3,200 wBTC +112 |
bZx 마진 거래 wBTC 스왑 | wBTC +51 | wETH +3,200 wBTC +163 |
wBTC <-> wETH 스왑 | wBTC -112 | wETH +3,200 wBTC +51 |
wBTC <-> wETH 스왑 | wETH +6,871 | wETH +10,071 wBTC +51 |
Flash Loan 상환 | wETH -10,000 |
wETH +71 wBTC +51 |
여기까지만 공격을 수행했더라도 공격자는 71 wETH을 획득하였기 때문에 대략 1.8만달러(1wETH 250달러 가정)를 벌 수 있었습니다. 공격자가 벌 수 있는 돈이 여기까지일까요?
현재 공격자의 자산 현황과 대출 현황을 확인해보면, Compound에 예치된 wETH 5,500개를 돌려받기 위해서는 wBTC 112개를 상환해야만 합니다.
당시 정상적인 교환 비율이 38.5인 것을 감안하면, 4,312 wETH으로 wBTC 112개를 살 수 있습니다.
구분 | Balance | Debt | wETH/wBTH |
Compound | wETH +5,500 | wBTC -112 | 49.11 |
bZX | wBTC +51 | wETH -4,337 | 85.04 |
즉, 공격자가 최종적으로 획득할 수 있는 wETH은 1,271(5,500-4,312+71)으로 대략 31.7만달러(1wETH 250달러 가정)라는 엄청난 차익을 획득할 수 있습니다.
사실 bZX Smart Contract 코드 내에는 이러한 Flash Loan Attack을 막을 수 있는 Oracle 시스템이 있었습니다. 그러나 Smart Contract 내의 취약점으로 인해 Oracle 시스템이 정상적으로 작동하지 않았고 공격자는 이를 통해 Flash Loan Attack을 성공적으로 수행할 수 있었습니다.
해당 취약점은 bZx 마진 거래 시 내부적으로 marginTradeFromDeposit을 호출하는 과정에서 amountIsADeposit이 True로 설정되어 Oracle 시스템이 정상적으로 실행되지 않았기 때문에 발생하는 취약점 이었습니다.
function _marginTradeFromDeposit(
// ...
loanOrderHash = _borrowTokenAndUse(
leverageAmount,
[
trader,
collateralTokenAddress, // collateralTokenAddress
tradeTokenAddress, // tradeTokenAddress
trader // receiver
],
[
0, // interestRate (found later)
amount, // amount of deposit
0, // interestInitialAmount (interest is calculated based on fixed-term loan)
loanTokenSent,
collateralTokenSent,
tradeTokenSent,
0
],
true, // amountIsADeposit
loanDataBytes
);
function _borrowTokenAndUse(
uint256 leverageAmount,
address[4] memory sentAddresses,
uint256[7] memory sentAmounts,
bool amountIsADeposit,
bytes memory loanDataBytes)
internal
returns (bytes32 loanOrderHash){
// ...
if (amountIsADeposit) {
(sentAmounts[1], sentAmounts[0]) = _getBorrowAmountAndRate( // borrowAmount, interestRate
loanOrderHash,
sentAmounts[1], // amount
useFixedInterestModel
);
// update for borrowAmount
sentAmounts[6] = sentAmounts[1]; // borrowAmount
// ...
uint256 borrowAmount = _borrowTokenAndUseFinal(
loanOrderHash,
sentAddresses,
sentAmounts,
loanDataBytes
);
function _borrowTokenAndUseFinal(
bytes32 loanOrderHash,
address[4] memory sentAddresses,
uint256[7] memory sentAmounts,
bytes memory loanDataBytes)
internal
returns (uint256)
{
// ...
sentAmounts[1] = IBZx(bZxContract).takeOrderFromiToken( // borrowAmount
loanOrderHash,
sentAddresses,
sentAmounts,
loanDataBytes
);
// Oralce System Not working
require ((
loanDataBytes.length == 0 && // Kyber only
sentAmounts[6] == sentAmounts[1]) || // newLoanAmount
!OracleInterface(oracle).shouldLiquidate(
loanOrder,
loanPosition
),
"unhealthy position"
);
참조 문서
bZx Hack Full Disclosure (With Detailed Profit Analysis)
On 02/15, we have provided a transaction-level recap on the bZx hack that recently captures various headlines in DeFi-related tweets and…
peckshield.medium.com
'Hack > Cryptocurrency' 카테고리의 다른 글
브릿지와 크로스 체인, 멀티 체인, 옴니 체인을 알아보자 (0) | 2023.06.24 |
---|---|
ERC-165 (Ethereum Request for Comments 165) (0) | 2023.06.21 |
Transaction Order Dependence Attack in Smart Contract (0) | 2023.05.05 |
[DAO Hacking] Reentrancy Attack 실제 사례 분석 (0) | 2023.04.30 |
Reentrancy Attack in Smart Contract (0) | 2023.04.29 |
댓글