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

Import not-so-smart-contracts #146

Open
wants to merge 74 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
930ee7c
Initial commit
montyly Aug 28, 2017
0dff584
Add new examples:
montyly Aug 29, 2017
8600067
Minor modifs in Readme(s)
montyly Aug 29, 2017
5f20de6
Improve reentrancy example
montyly Sep 7, 2017
6f1ec23
Make titles consistent
Oct 12, 2017
7f1374e
Add unchecked external call
Oct 12, 2017
1c0e2e3
Add links to lines of code
Oct 12, 2017
8562893
Update README.md
Oct 13, 2017
b9c064d
Update README.md
Oct 13, 2017
1fdd900
small edits
Oct 13, 2017
ae8d419
Added credits and contact info (#5)
ESultanik Oct 13, 2017
0b51260
Add race condition (#3)
montyly Oct 13, 2017
8818252
README nitpics
dguido Oct 15, 2017
c3f5799
correct path
dguido Oct 15, 2017
a47392b
Create LICENSE
dguido Oct 16, 2017
c256330
Clean reentrancy exploit
montyly Mar 6, 2018
2d775c2
Added a README on variable shadowing
ggrieco-tob Mar 19, 2018
0e66892
Added honeypot examples
Apr 2, 2018
93c7242
wrong -> incorrect
dguido Jul 26, 2018
ae26f59
Small spelling correction in file name...
Aug 29, 2018
7928b85
Fix syntax error so solc compiles...
Aug 29, 2018
4397d5e
Denial of service vulns added
blperez01 Aug 31, 2018
d1742f9
Fixed README
blperez01 Aug 31, 2018
7fa9467
Created readme's for two new vulns and updated master readme accordingly
blperez01 Aug 31, 2018
46a7d81
updated bad randomness readme. still need examples
blperez01 Aug 31, 2018
041c3d1
Updated integer overlow and denial of service readme
blperez01 Aug 31, 2018
2cf5365
updated missing constructor and race condition READMEs
blperez01 Aug 31, 2018
a9aaec1
added forced ether reception example
ggrieco-tob Sep 4, 2018
df804f0
More README updates
blperez01 Sep 4, 2018
4196490
Create inherited_state.sol
ggrieco-tob Mar 19, 2018
a2b15da
Another round of readme updates
blperez01 Sep 4, 2018
d214ed6
Fixed links
blperez01 Sep 4, 2018
1afbaea
fixed top level readme, added references
blperez01 Sep 4, 2018
939b0ab
Fix syntax error so solc compiles...
Aug 29, 2018
f619b7f
Update README.md
ggrieco-tob Sep 4, 2018
3c93d0e
More educational clarification for the reentrancy example
ESultanik Sep 5, 2018
0abd0e5
Added forced ether reception to README
blperez01 Sep 5, 2018
d516ef2
Added links, slight format changes
blperez01 Sep 5, 2018
64cfc2a
Fix path to relative link to code
ESultanik Sep 5, 2018
15714f4
Fixed variable shadowing link
blperez01 Sep 5, 2018
2335b71
Update README.md
blperez01 Sep 5, 2018
d075aa8
Update list_dos.sol
blperez01 Sep 19, 2018
5adefba
Update README.md
blperez01 Sep 19, 2018
c46872a
Changed missing constructor to more sensible name
blperez01 Sep 19, 2018
e33f629
removed missing constructor folder
blperez01 Sep 19, 2018
456fa55
Update README.md
blperez01 Sep 19, 2018
7b8fa0b
Fixed the link to the constructor example
Sep 24, 2018
7e72091
Fixed main README link to Wrong Constructor Name
Sep 24, 2018
ea3625d
Added theRun as an example of bad randomness
Sep 27, 2018
c9b9d42
added theRun source code
Sep 27, 2018
56e4af4
updated README to reference the local source code
Sep 27, 2018
d7ac0a7
Add a new reference to unchecked external calls
Oct 3, 2018
6b162c0
Add the same reference to Denial of Service
Oct 4, 2018
778153e
Update README.md
dguido Oct 5, 2018
2966bba
Update README.md
dguido Oct 5, 2018
777bc54
Update README.md
dguido Oct 5, 2018
cba8e6f
updated not-so-smart-contracts to add SpankChain
Oct 9, 2018
d2846df
add more examples
tayvano Feb 25, 2020
77a96d3
Update theRun.sol
May 31, 2022
821d22d
mv everything into subfolder
Oct 3, 2022
00d656c
Merge branch 'master' into import-nssc
Oct 4, 2022
3b41791
Add 10 vulnerabilities
montyly Apr 24, 2020
dac979c
rm nssc/solidity/license
Oct 4, 2022
0d9ada8
describe solc version impact on integer over-/under-flows
Oct 4, 2022
cd1dab9
fix spelling mistakes
Oct 4, 2022
63dbb5c
init solhint config
Oct 4, 2022
e1e63ac
bad_randomness editing pass
Oct 4, 2022
a332427
dos & ether reception editing pass
Oct 4, 2022
4281a02
honeypot editing pass
Oct 4, 2022
897c9ad
remove incorrect interface examples
Oct 4, 2022
953c20a
reentrancy editing pass
Oct 4, 2022
d9e440a
rm old examples already covered by honeypot examples
Oct 4, 2022
edda363
more cleanup & update nssc/solidity readme
Oct 4, 2022
6ca3a11
fix typo
Oct 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add new examples:
 - missing constructor
 - unprotected function
 - wrong interface
Add DAO and Paritity Wallet source code
montyly authored and bohendo committed Aug 29, 2017
commit 0dff584abf9caae887712da7daa7576a7524b11d
25 changes: 25 additions & 0 deletions missing_constructor/Missing.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
pragma solidity ^0.4.15;

contract Missing{
address private owner;

modifier onlyowner {
require(msg.sender==owner);
_;
}

// The name of the constructor should be Missing
// Anyone can call the IamMissing once the contract is deployed
function IamMissing()
public
{
owner = msg.sender;
}

function withdraw()
public
onlyowner
{
owner.transfer(this.balance);
}
}
11 changes: 11 additions & 0 deletions missing_constructor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Unprotected function

## Principle:
- Wrong constructor name

## Attack
Anyone can call the function that was suppose to be the constructor, and changes the related state variables.

## Known exploit
[Rubixi](https://etherscan.io/address/0xe82719202e5965Cf5D9B6673B7503a3b92DE20be#code)
- See `Rubixi_source_code/Rubixi.sol`: `DynamicPyramid` instead of `Rubixi`
154 changes: 154 additions & 0 deletions missing_constructor/Rubixi_source_code/Rubixi.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// 0xe82719202e5965Cf5D9B6673B7503a3b92DE20be#code

contract Rubixi {

//Declare variables for storage critical to contract
uint private balance = 0;
uint private collectedFees = 0;
uint private feePercent = 10;
uint private pyramidMultiplier = 300;
uint private payoutOrder = 0;

address private creator;

//Sets creator
function DynamicPyramid() {
creator = msg.sender;
}

modifier onlyowner {
if (msg.sender == creator) _
}

struct Participant {
address etherAddress;
uint payout;
}

Participant[] private participants;

//Fallback function
function() {
init();
}

//init function run on fallback
function init() private {
//Ensures only tx with value of 1 ether or greater are processed and added to pyramid
if (msg.value < 1 ether) {
collectedFees += msg.value;
return;
}

uint _fee = feePercent;
//50% fee rebate on any ether value of 50 or greater
if (msg.value >= 50 ether) _fee /= 2;

addPayout(_fee);
}

//Function called for valid tx to the contract
function addPayout(uint _fee) private {
//Adds new address to participant array
participants.push(Participant(msg.sender, (msg.value * pyramidMultiplier) / 100));

//These statements ensure a quicker payout system to later pyramid entrants, so the pyramid has a longer lifespan
if (participants.length == 10) pyramidMultiplier = 200;
else if (participants.length == 25) pyramidMultiplier = 150;

// collect fees and update contract balance
balance += (msg.value * (100 - _fee)) / 100;
collectedFees += (msg.value * _fee) / 100;

//Pays earlier participiants if balance sufficient
while (balance > participants[payoutOrder].payout) {
uint payoutToSend = participants[payoutOrder].payout;
participants[payoutOrder].etherAddress.send(payoutToSend);

balance -= participants[payoutOrder].payout;
payoutOrder += 1;
}
}

//Fee functions for creator
function collectAllFees() onlyowner {
if (collectedFees == 0) throw;

creator.send(collectedFees);
collectedFees = 0;
}

function collectFeesInEther(uint _amt) onlyowner {
_amt *= 1 ether;
if (_amt > collectedFees) collectAllFees();

if (collectedFees == 0) throw;

creator.send(_amt);
collectedFees -= _amt;
}

function collectPercentOfFees(uint _pcent) onlyowner {
if (collectedFees == 0 || _pcent > 100) throw;

uint feesToCollect = collectedFees / 100 * _pcent;
creator.send(feesToCollect);
collectedFees -= feesToCollect;
}

//Functions for changing variables related to the contract
function changeOwner(address _owner) onlyowner {
creator = _owner;
}

function changeMultiplier(uint _mult) onlyowner {
if (_mult > 300 || _mult < 120) throw;

pyramidMultiplier = _mult;
}

function changeFeePercentage(uint _fee) onlyowner {
if (_fee > 10) throw;

feePercent = _fee;
}

//Functions to provide information to end-user using JSON interface or other interfaces
function currentMultiplier() constant returns(uint multiplier, string info) {
multiplier = pyramidMultiplier;
info = 'This multiplier applies to you as soon as transaction is received, may be lowered to hasten payouts or increased if payouts are fast enough. Due to no float or decimals, multiplier is x100 for a fractional multiplier e.g. 250 is actually a 2.5x multiplier. Capped at 3x max and 1.2x min.';
}

function currentFeePercentage() constant returns(uint fee, string info) {
fee = feePercent;
info = 'Shown in % form. Fee is halved(50%) for amounts equal or greater than 50 ethers. (Fee may change, but is capped to a maximum of 10%)';
}

function currentPyramidBalanceApproximately() constant returns(uint pyramidBalance, string info) {
pyramidBalance = balance / 1 ether;
info = 'All balance values are measured in Ethers, note that due to no decimal placing, these values show up as integers only, within the contract itself you will get the exact decimal value you are supposed to';
}

function nextPayoutWhenPyramidBalanceTotalsApproximately() constant returns(uint balancePayout) {
balancePayout = participants[payoutOrder].payout / 1 ether;
}

function feesSeperateFromBalanceApproximately() constant returns(uint fees) {
fees = collectedFees / 1 ether;
}

function totalParticipants() constant returns(uint count) {
count = participants.length;
}

function numberOfParticipantsWaitingForPayout() constant returns(uint count) {
count = participants.length - payoutOrder;
}

function participantDetails(uint orderInPyramid) constant returns(address Address, uint Payout) {
if (orderInPyramid <= participants.length) {
Address = participants[orderInPyramid].etherAddress;
Payout = participants[orderInPyramid].payout / 1 ether;
}
}
}
1,238 changes: 1,238 additions & 0 deletions reentrancy/DAO_source_code/DAO.sol

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions unprotected_function/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Unprotected function

## Principle:
- Missing modifier on a sensitive function


## Known exploit
[Parity Wallet](https://blog.zeppelin.solutions/on-the-parity-wallet-multisig-hack-405a8c12e8f7)
- See `initWallet` in `WalletLibrary_source_code/WalletLibrary.sol`

30 changes: 30 additions & 0 deletions unprotected_function/Unprotected.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
pragma solidity ^0.4.15;

contract Unprotected{
address private owner;

modifier onlyowner {
require(msg.sender==owner);
_;
}

function Unprotected()
public
{
owner = msg.sender;
}

// This function should be protected
function changeOwner(address _newOwner)
public
{
owner = _newOwner;
}

function changeOwner_fixed(address _newOwner)
public
onlyowner
{
owner = _newOwner;
}
}
462 changes: 462 additions & 0 deletions unprotected_function/WalletLibrary_source_code/WalletLibrary.sol

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions wrong_interface/Alice.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

pragma solidity ^0.4.15;

contract Alice {
int public val;

function set(int new_val){
val = new_val;
}

function set_fixed(int new_val){
val = new_val;
}

function(){
val = 1;
}
}
17 changes: 17 additions & 0 deletions wrong_interface/Bob.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

pragma solidity ^0.4.15;

contract Alice {
function set(uint);
function set_fixed(int);
}

contract Bob {
function set(Alice c){
c.set(42);
}

function set_fixed(Alice c){
c.set_fixed(42);
}
}
76 changes: 76 additions & 0 deletions wrong_interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Wrong interface

## Principle:
- Wrong contract interface

## Detail
The interface is wrongly define. `Alice.set(uint)` takes an `uint` in `Bob.sol` and `Alice.set(int)` in `Alice.sol`.
The id of the method from bob will be wrong, as a result bob will call the fallback function of alice.

## Running example
First, get the bytecode and the abi of the contracts:
```̀bash
$ solc --bin Alice.sol
6060604052341561000f57600080fd5b5b6101158061001f6000396000f300606060405236156051576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633c6bb436146067578063a5d5e46514608d578063e5c19b2d1460ad575b3415605b57600080fd5b5b60016000819055505b005b3415607157600080fd5b607760cd565b6040518082815260200191505060405180910390f35b3415609757600080fd5b60ab600480803590602001909190505060d3565b005b341560b757600080fd5b60cb600480803590602001909190505060de565b005b60005481565b806000819055505b50565b806000819055505b505600a165627a7a723058207d0ad6d1ce356adf9fa0284c9f887bb4b912204886b731c37c2ae5d16aef19a20029
$ solc --abi Alice.sol
[{"constant":true,"inputs":[],"name":"val","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"new_val","type":"int256"}],"name":"set_fixed","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"new_val","type":"int256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"payable":false,"type":"fallback"}]
$ solc --bin Bob.sol
6060604052341561000f57600080fd5b5b6101f58061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632801617e1461004957806390b2290e14610082575b600080fd5b341561005457600080fd5b610080600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100bb565b005b341561008d57600080fd5b6100b9600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610142565b005b8073ffffffffffffffffffffffffffffffffffffffff166360fe47b1602a6040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b151561012a57600080fd5b6102c65a03f1151561013b57600080fd5b5050505b50565b8073ffffffffffffffffffffffffffffffffffffffff1663a5d5e465602a6040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15156101b157600080fd5b6102c65a03f115156101c257600080fd5b5050505b505600a165627a7a72305820f8c9dcade78d92097c18627223a8583507e9331ef1e5de02640ffc2e731111320029
$ solc --abi Bob.sol
[{"constant":false,"inputs":[{"name":"c","type":"address"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"c","type":"address"}],"name":"set_fixed","outputs":[],"payable":false,"type":"function"}]
```

The following commands were tested on a private blockchain

```javascript
$ get attach

// this unlock the account for a limited amount of time
// if you have an error:
// Error: authentication needed: password or unlock
// you can to call unlockAccount again
personal.unlockAccount(eth.accounts[0], "apasswordtochange")

var bytecodeAlice = '0x6060604052341561000f57600080fd5b5b6101158061001f6000396000f300606060405236156051576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633c6bb436146067578063a5d5e46514608d578063e5c19b2d1460ad575b3415605b57600080fd5b5b60016000819055505b005b3415607157600080fd5b607760cd565b6040518082815260200191505060405180910390f35b3415609757600080fd5b60ab600480803590602001909190505060d3565b005b341560b757600080fd5b60cb600480803590602001909190505060de565b005b60005481565b806000819055505b50565b806000819055505b505600a165627a7a723058207d0ad6d1ce356adf9fa0284c9f887bb4b912204886b731c37c2ae5d16aef19a20029'
var abiAlice = [{"constant":true,"inputs":[],"name":"val","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"new_val","type":"int256"}],"name":"set_fixed","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"new_val","type":"int256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"payable":false,"type":"fallback"}]

var bytecodeBob = '0x6060604052341561000f57600080fd5b5b6101f58061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632801617e1461004957806390b2290e14610082575b600080fd5b341561005457600080fd5b610080600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100bb565b005b341561008d57600080fd5b6100b9600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610142565b005b8073ffffffffffffffffffffffffffffffffffffffff166360fe47b1602a6040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b151561012a57600080fd5b6102c65a03f1151561013b57600080fd5b5050505b50565b8073ffffffffffffffffffffffffffffffffffffffff1663a5d5e465602a6040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15156101b157600080fd5b6102c65a03f115156101c257600080fd5b5050505b505600a165627a7a72305820f8c9dcade78d92097c18627223a8583507e9331ef1e5de02640ffc2e731111320029'
var abiBob = [{"constant":false,"inputs":[{"name":"c","type":"address"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"c","type":"address"}],"name":"set_fixed","outputs":[],"payable":false,"type":"function"}]

var contractAlice = eth.contract(abiAlice);
var txDeployAlice = {from:eth.coinbase, data: bytecodeAlice, gas: 1000000};
var contractPartialInstanceAlice = contractAlice.new(txDeployAlice);

// Wait to mine the block containing the transaction

var alice = contractAlice.at(contractPartialInstanceAlice.address);

var contractBob = eth.contract(abiBob);
var txDeployBob = {from:eth.coinbase, data: bytecodeBob, gas: 1000000};
var contractPartialInstanceBob = contractBob.new(txDeployBob);

// Wait to mine the block containing the transaction

var bob = contractBob.at(contractPartialInstanceBob.address);

// From now, wait for each transaction to be mined before calling
// the others transactions

// print the default value of val: 0
alice.val()

// call bob.set, as the interface is wrong, it will call
// the fallback function of alice
bob.set(alice.address, {from: eth.accounts[0]} )
// print val: 1
alice.val()

// call the fixed version of the interface
bob.set_fixed(alice.address, {from: eth.accounts[0]} )
// print val: 42
alice.val()
```