The following content was copied from samczun's bug bounty report on a bug on DDEX that he discovered and was patched consequently, this was not an actual exploit.
DDEX is a decentralized exchange platform but are in the process of expanding into decentralized lending so that they can offer their users the ability to create leveraged long and short positions. They're currently beta testing their decentralized margin exchange.
On September 9th 2019, DDEX added DAI as an asset to their margin trading platform and enabled the ETH/DAI market. For the oracle, they specified this smart contract which returns the value of DAI/USD by calculating PriceOfETHInUSD/PriceOfETHInDAI. The value of ETH/USD is read from the Maker oracle, while the value of ETH/DAI is read from either Eth2Dai, or if the spread is too great, Uniswap.
Let's assume we can manipulate the apparent value of DAI/USD. If this is the case, we would like to use this to borrow all of the ETH in the system while providing as little DAI as possible. To achieve this, we can either lower the apparent value of ETH/USD or increase the apparent value of DAI/USD. Since we're already assuming that the apparent value of DAI/USD is manipulable, we'll choose the latter.
To increase the apparent value DAI/USD, we can either increase the apparent value of ETH/USD, or decrease the apparent value of ETH/DAI. For all intents and purposes manipulating Maker's oracle is impossible, so we'll try decreasing the apparent value of ETH/DAI.
The oracle will calculate the value of ETH/DAI as reported by Eth2Dai by taking the average of the current asking price and the current bidding price. In order to decrease this value, we'll need to lower the current bidding price by filling existing orders and then lower the current asking price by placing new orders.
However, this requires a significant initial investment (as we need to fill orders then make an equivalent number of orders) and is non-trivial to implement. On the other hand, we can drop the Uniswap price simply by selling a large amount of DAI to Uniswap. As such, we'll aim to bypass the Eth2Dai logic and manipulate the Uniswap price.
In order to bypass Eth2Dai, we need to manipulate the magnitude of the spread. We can do this in one of two ways:
Clear out one side of the orderbook while leaving the other alone. This causes spread to increase positively
Force a crossed orderbook by listing an extreme buy or sell order. This causes spread to decrease negatively.
While option 2 would result in no losses from taking unfavorable orders, the use of SafeMath disallows a crossed orderbook and as such is unavailable to us. Instead, we'll force a large positive spread by clearing out one side of the orderbook. This will cause the DAI oracle to fallback to Uniswap to determine the price of DAI. Then, we can cause the Uniswap price of DAI/ETH to drop by buying a large amount of DAI. Once the apparent value of DAI/USD has been manipulated, it's trivial to take out a loan like as usual.
Demo
The following script will turn a profit of approximately 70 ETH by:
Clearing out Eth2Dai's sell orders until the spread is large enough that the oracle rejects the price
Buying more DAI from Uniswap, dropping the price from 213DAI/ETH to 13DAI/ETH
Borrowing all the available ETH (~120) for a small amount of DAI (~2500)
Selling the DAI we bought from Uniswap back to Uniswap
Selling the DAI we bought from Eth2Dai back to Eth2Dai