Add SB Curated (copied from the smartbugs repository).
This commit is contained in:
62
dataset/access_control/FibonacciBalance.sol
Normal file
62
dataset/access_control/FibonacciBalance.sol
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* @source: https://github.com/sigp/solidity-security-blog
|
||||
* @author: Suhabe Bugrara
|
||||
* @vulnerable_at_lines: 31,38
|
||||
*/
|
||||
|
||||
//added pragma version
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract FibonacciBalance {
|
||||
|
||||
address public fibonacciLibrary;
|
||||
// the current fibonacci number to withdraw
|
||||
uint public calculatedFibNumber;
|
||||
// the starting fibonacci sequence number
|
||||
uint public start = 3;
|
||||
uint public withdrawalCounter;
|
||||
// the fibonancci function selector
|
||||
bytes4 constant fibSig = bytes4(sha3("setFibonacci(uint256)"));
|
||||
|
||||
// constructor - loads the contract with ether
|
||||
constructor(address _fibonacciLibrary) public payable {
|
||||
fibonacciLibrary = _fibonacciLibrary;
|
||||
}
|
||||
|
||||
function withdraw() {
|
||||
withdrawalCounter += 1;
|
||||
// calculate the fibonacci number for the current withdrawal user
|
||||
// this sets calculatedFibNumber
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
require(fibonacciLibrary.delegatecall(fibSig, withdrawalCounter));
|
||||
msg.sender.transfer(calculatedFibNumber * 1 ether);
|
||||
}
|
||||
|
||||
// allow users to call fibonacci library functions
|
||||
function() public {
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
require(fibonacciLibrary.delegatecall(msg.data));
|
||||
}
|
||||
}
|
||||
|
||||
// library contract - calculates fibonacci-like numbers;
|
||||
contract FibonacciLib {
|
||||
// initializing the standard fibonacci sequence;
|
||||
uint public start;
|
||||
uint public calculatedFibNumber;
|
||||
|
||||
// modify the zeroth number in the sequence
|
||||
function setStart(uint _start) public {
|
||||
start = _start;
|
||||
}
|
||||
|
||||
function setFibonacci(uint n) public {
|
||||
calculatedFibNumber = fibonacci(n);
|
||||
}
|
||||
|
||||
function fibonacci(uint n) internal returns (uint) {
|
||||
if (n == 0) return start;
|
||||
else if (n == 1) return start + 1;
|
||||
else return fibonacci(n - 1) + fibonacci(n - 2);
|
||||
}
|
||||
}
|
||||
20
dataset/access_control/README.md
Executable file
20
dataset/access_control/README.md
Executable file
@@ -0,0 +1,20 @@
|
||||
# Access Control
|
||||
Access Control issues are common in all programs, not just smart contracts. In fact, it's number 5 on the OWASP top 10. One usually accesses a contract's functionality through its public or external functions. While insecure visibility settings give attackers straightforward ways to access a contract's private values or logic, access control bypasses are sometimes more subtle. These vulnerabilities can occur when contracts use the deprecated tx.origin to validate callers, handle large authorization logic with lengthy require and make reckless use of delegatecall in proxy libraries or proxy contracts.
|
||||
|
||||
Loss: estimated at 150,000 ETH (~30M USD at the time)
|
||||
|
||||
## Attack Scenario
|
||||
A smart contract designates the address which initializes it as the contract's owner. This is a common pattern for granting special privileges such as the ability to withdraw the contract's funds.
|
||||
Unfortunately, the initialization function can be called by anyone — even after it has already been called. Allowing anyone to become the owner of the contract and take its funds.
|
||||
|
||||
## Examples
|
||||
In the following example, the contract's initialization function sets the caller of the function as its owner. However, the logic is detached from the contract's constructor, and it does not keep track of the fact that it has already been called.
|
||||
```
|
||||
function initContract() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
```
|
||||
In the Parity multi-sig wallet, this initialization function was detached from the wallets themselves and defined in a "library" contract. Users were expected to initialize their own wallet by calling the library's function via a delegateCall. Unfortunately, as in our example, the function did not check if the wallet had already been initialized. Worse, since the library was a smart contract, anyone could initialize the library itself and call for its destruction.
|
||||
|
||||
## References
|
||||
Taken from [DASP TOP10](https://dasp.co/)
|
||||
40
dataset/access_control/arbitrary_location_write_simple.sol
Normal file
40
dataset/access_control/arbitrary_location_write_simple.sol
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* @source: https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-124#arbitrary-location-write-simplesol
|
||||
* @author: Suhabe Bugrara
|
||||
* @vulnerable_at_lines: 27
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.25;
|
||||
|
||||
contract Wallet {
|
||||
uint[] private bonusCodes;
|
||||
address private owner;
|
||||
|
||||
constructor() public {
|
||||
bonusCodes = new uint[](0);
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function () public payable {
|
||||
}
|
||||
|
||||
function PushBonusCode(uint c) public {
|
||||
bonusCodes.push(c);
|
||||
}
|
||||
|
||||
function PopBonusCode() public {
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
require(0 <= bonusCodes.length); // this condition is always true since array lengths are unsigned
|
||||
bonusCodes.length--; // an underflow can be caused here
|
||||
}
|
||||
|
||||
function UpdateBonusCodeAt(uint idx, uint c) public {
|
||||
require(idx < bonusCodes.length);
|
||||
bonusCodes[idx] = c; // write to any index less than bonusCodes.length
|
||||
}
|
||||
|
||||
function Destroy() public {
|
||||
require(msg.sender == owner);
|
||||
selfdestruct(msg.sender);
|
||||
}
|
||||
}
|
||||
34
dataset/access_control/incorrect_constructor_name1.sol
Normal file
34
dataset/access_control/incorrect_constructor_name1.sol
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* @source: https://github.com/trailofbits/not-so-smart-contracts/blob/master/wrong_constructor_name/incorrect_constructor.sol
|
||||
* @author: Ben Perez
|
||||
* @vulnerable_at_lines: 20
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
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
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function IamMissing()
|
||||
public
|
||||
{
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function () payable {}
|
||||
|
||||
function withdraw()
|
||||
public
|
||||
onlyowner
|
||||
{
|
||||
owner.transfer(this.balance);
|
||||
}
|
||||
}
|
||||
32
dataset/access_control/incorrect_constructor_name2.sol
Normal file
32
dataset/access_control/incorrect_constructor_name2.sol
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* @source: https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-118#incorrect-constructor-name1sol
|
||||
* @author: Ben Perez
|
||||
* @vulnerable_at_lines: 18
|
||||
*/
|
||||
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
contract Missing{
|
||||
address private owner;
|
||||
|
||||
modifier onlyowner {
|
||||
require(msg.sender==owner);
|
||||
_;
|
||||
}
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function missing()
|
||||
public
|
||||
{
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function () payable {}
|
||||
|
||||
function withdraw()
|
||||
public
|
||||
onlyowner
|
||||
{
|
||||
owner.transfer(this.balance);
|
||||
}
|
||||
}
|
||||
32
dataset/access_control/incorrect_constructor_name3.sol
Normal file
32
dataset/access_control/incorrect_constructor_name3.sol
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* @source: https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-118#incorrect-constructor-name2sol
|
||||
* @author: Ben Perez
|
||||
* @vulnerable_at_lines: 17
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
contract Missing{
|
||||
address private owner;
|
||||
|
||||
modifier onlyowner {
|
||||
require(msg.sender==owner);
|
||||
_;
|
||||
}
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function Constructor()
|
||||
public
|
||||
{
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function () payable {}
|
||||
|
||||
function withdraw()
|
||||
public
|
||||
onlyowner
|
||||
{
|
||||
owner.transfer(this.balance);
|
||||
}
|
||||
|
||||
}
|
||||
30
dataset/access_control/mapping_write.sol
Normal file
30
dataset/access_control/mapping_write.sol
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* @source: https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-124#mapping-writesol
|
||||
* @author: Suhabe Bugrara
|
||||
* @vulnerable_at_lines: 20
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
//This code is derived from the Capture the Ether https://capturetheether.com/challenges/math/mapping/
|
||||
|
||||
contract Map {
|
||||
address public owner;
|
||||
uint256[] map;
|
||||
|
||||
function set(uint256 key, uint256 value) public {
|
||||
if (map.length <= key) {
|
||||
map.length = key + 1;
|
||||
}
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
map[key] = value;
|
||||
}
|
||||
|
||||
function get(uint256 key) public view returns (uint256) {
|
||||
return map[key];
|
||||
}
|
||||
function withdraw() public{
|
||||
require(msg.sender == owner);
|
||||
msg.sender.transfer(address(this).balance);
|
||||
}
|
||||
}
|
||||
63
dataset/access_control/multiowned_vulnerable.sol
Normal file
63
dataset/access_control/multiowned_vulnerable.sol
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* @source: https://github.com/SmartContractSecurity/SWC-registry/blob/master/test_cases/solidity/unprotected_critical_functions/multiowned_vulnerable/multiowned_vulnerable.sol
|
||||
* @author: -
|
||||
* @vulnerable_at_lines: 38
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.23;
|
||||
|
||||
/**
|
||||
* @title MultiOwnable
|
||||
*/
|
||||
contract MultiOwnable {
|
||||
address public root;
|
||||
mapping (address => address) public owners; // owner => parent of owner
|
||||
|
||||
/**
|
||||
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
|
||||
* account.
|
||||
*/
|
||||
constructor() public {
|
||||
root = msg.sender;
|
||||
owners[root] = root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Throws if called by any account other than the owner.
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
require(owners[msg.sender] != 0);
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Adding new owners
|
||||
* Note that the "onlyOwner" modifier is missing here.
|
||||
*/
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function newOwner(address _owner) external returns (bool) {
|
||||
require(_owner != 0);
|
||||
owners[_owner] = msg.sender;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Deleting owners
|
||||
*/
|
||||
function deleteOwner(address _owner) onlyOwner external returns (bool) {
|
||||
require(owners[_owner] == msg.sender || (owners[_owner] != 0 && msg.sender == root));
|
||||
owners[_owner] = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
contract TestContract is MultiOwnable {
|
||||
|
||||
function withdrawAll() onlyOwner {
|
||||
msg.sender.transfer(this.balance);
|
||||
}
|
||||
|
||||
function() payable {
|
||||
}
|
||||
|
||||
}
|
||||
24
dataset/access_control/mycontract.sol
Normal file
24
dataset/access_control/mycontract.sol
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* @source: https://consensys.github.io/smart-contract-best-practices/recommendations/#avoid-using-txorigin
|
||||
* @author: Consensys Diligence
|
||||
* @vulnerable_at_lines: 20
|
||||
* Modified by Gerhard Wagner
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
contract MyContract {
|
||||
|
||||
address owner;
|
||||
|
||||
function MyContract() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function sendTo(address receiver, uint amount) public {
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
require(tx.origin == owner);
|
||||
receiver.transfer(amount);
|
||||
}
|
||||
|
||||
}
|
||||
469
dataset/access_control/parity_wallet_bug_1.sol
Normal file
469
dataset/access_control/parity_wallet_bug_1.sol
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* @source: https://github.com/paritytech/parity-ethereum/blob/4d08e7b0aec46443bf26547b17d10cb302672835/js/src/contracts/snippets/enhanced-wallet.sol#L216
|
||||
* @author: parity
|
||||
* @vulnerable_at_lines: 223,437
|
||||
*/
|
||||
|
||||
//sol Wallet
|
||||
// Multi-sig, daily-limited account proxy/wallet.
|
||||
// @authors:
|
||||
// Gav Wood <g@ethdev.com>
|
||||
// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
|
||||
// single, or, crucially, each of a number of, designated owners.
|
||||
// usage:
|
||||
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
|
||||
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
|
||||
// interior is executed.
|
||||
|
||||
pragma solidity ^0.4.9;
|
||||
|
||||
contract WalletEvents {
|
||||
// EVENTS
|
||||
|
||||
// this contract only has six types of events: it can accept a confirmation, in which case
|
||||
// we record owner and operation (hash) alongside it.
|
||||
event Confirmation(address owner, bytes32 operation);
|
||||
event Revoke(address owner, bytes32 operation);
|
||||
|
||||
// some others are in the case of an owner changing.
|
||||
event OwnerChanged(address oldOwner, address newOwner);
|
||||
event OwnerAdded(address newOwner);
|
||||
event OwnerRemoved(address oldOwner);
|
||||
|
||||
// the last one is emitted if the required signatures change
|
||||
event RequirementChanged(uint newRequirement);
|
||||
|
||||
// Funds has arrived into the wallet (record how much).
|
||||
event Deposit(address _from, uint value);
|
||||
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
||||
event SingleTransact(address owner, uint value, address to, bytes data, address created);
|
||||
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
||||
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data, address created);
|
||||
// Confirmation still needed for a transaction.
|
||||
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||
}
|
||||
|
||||
contract WalletAbi {
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation) external;
|
||||
|
||||
// Replaces an owner `_from` with another `_to`.
|
||||
function changeOwner(address _from, address _to) external;
|
||||
|
||||
function addOwner(address _owner) external;
|
||||
|
||||
function removeOwner(address _owner) external;
|
||||
|
||||
function changeRequirement(uint _newRequired) external;
|
||||
|
||||
function isOwner(address _addr) constant returns (bool);
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool);
|
||||
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) external;
|
||||
|
||||
function execute(address _to, uint _value, bytes _data) external returns (bytes32 o_hash);
|
||||
function confirm(bytes32 _h) returns (bool o_success);
|
||||
}
|
||||
|
||||
contract WalletLibrary is WalletEvents {
|
||||
// TYPES
|
||||
|
||||
// struct for the status of a pending operation.
|
||||
struct PendingState {
|
||||
uint yetNeeded;
|
||||
uint ownersDone;
|
||||
uint index;
|
||||
}
|
||||
|
||||
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
||||
struct Transaction {
|
||||
address to;
|
||||
uint value;
|
||||
bytes data;
|
||||
}
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple single-sig function modifier.
|
||||
modifier onlyowner {
|
||||
if (isOwner(msg.sender))
|
||||
_;
|
||||
}
|
||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||
// that later attempts can be realised as the same underlying operation and
|
||||
// thus count as confirmations.
|
||||
modifier onlymanyowners(bytes32 _operation) {
|
||||
if (confirmAndCheck(_operation))
|
||||
_;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// gets called when no other function matches
|
||||
function() payable {
|
||||
// just being sent some cash?
|
||||
if (msg.value > 0)
|
||||
Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
||||
// as well as the selection of addresses capable of confirming them.
|
||||
function initMultiowned(address[] _owners, uint _required) {
|
||||
m_numOwners = _owners.length + 1;
|
||||
m_owners[1] = uint(msg.sender);
|
||||
m_ownerIndex[uint(msg.sender)] = 1;
|
||||
for (uint i = 0; i < _owners.length; ++i)
|
||||
{
|
||||
m_owners[2 + i] = uint(_owners[i]);
|
||||
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
||||
}
|
||||
m_required = _required;
|
||||
}
|
||||
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
var pending = m_pending[_operation];
|
||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||
pending.yetNeeded++;
|
||||
pending.ownersDone -= ownerIndexBit;
|
||||
Revoke(msg.sender, _operation);
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces an owner `_from` with another `_to`.
|
||||
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_to)) return;
|
||||
uint ownerIndex = m_ownerIndex[uint(_from)];
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
clearPending();
|
||||
m_owners[ownerIndex] = uint(_to);
|
||||
m_ownerIndex[uint(_from)] = 0;
|
||||
m_ownerIndex[uint(_to)] = ownerIndex;
|
||||
OwnerChanged(_from, _to);
|
||||
}
|
||||
|
||||
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_owner)) return;
|
||||
|
||||
clearPending();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
reorganizeOwners();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
return;
|
||||
m_numOwners++;
|
||||
m_owners[m_numOwners] = uint(_owner);
|
||||
m_ownerIndex[uint(_owner)] = m_numOwners;
|
||||
OwnerAdded(_owner);
|
||||
}
|
||||
|
||||
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
if (ownerIndex == 0) return;
|
||||
if (m_required > m_numOwners - 1) return;
|
||||
|
||||
m_owners[ownerIndex] = 0;
|
||||
m_ownerIndex[uint(_owner)] = 0;
|
||||
clearPending();
|
||||
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
||||
OwnerRemoved(_owner);
|
||||
}
|
||||
|
||||
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
|
||||
if (_newRequired > m_numOwners) return;
|
||||
m_required = _newRequired;
|
||||
clearPending();
|
||||
RequirementChanged(_newRequired);
|
||||
}
|
||||
|
||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
function getOwner(uint ownerIndex) external constant returns (address) {
|
||||
return address(m_owners[ownerIndex + 1]);
|
||||
}
|
||||
|
||||
function isOwner(address _addr) constant returns (bool) {
|
||||
return m_ownerIndex[uint(_addr)] > 0;
|
||||
}
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) {
|
||||
var pending = m_pending[_operation];
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return false;
|
||||
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||
}
|
||||
|
||||
// constructor - stores initial daily limit and records the present day's index.
|
||||
function initDaylimit(uint _limit) {
|
||||
m_dailyLimit = _limit;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
|
||||
m_dailyLimit = _newLimit;
|
||||
}
|
||||
// resets the amount already spent today. needs many of the owners to confirm.
|
||||
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
|
||||
m_spentToday = 0;
|
||||
}
|
||||
|
||||
// constructor - just pass on the owner array to the multiowned and
|
||||
// the limit to daylimit
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function initWallet(address[] _owners, uint _required, uint _daylimit) {
|
||||
initDaylimit(_daylimit);
|
||||
initMultiowned(_owners, _required);
|
||||
}
|
||||
|
||||
// kills the contract sending everything to `_to`.
|
||||
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
suicide(_to);
|
||||
}
|
||||
|
||||
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
||||
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
||||
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
||||
// and _data arguments). They still get the option of using them if they want, anyways.
|
||||
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 o_hash) {
|
||||
// first, take the opportunity to check that we're under the daily limit.
|
||||
if ((_data.length == 0 && underLimit(_value)) || m_required == 1) {
|
||||
// yes - just execute the call.
|
||||
address created;
|
||||
if (_to == 0) {
|
||||
created = create(_value, _data);
|
||||
} else {
|
||||
if (!_to.call.value(_value)(_data))
|
||||
throw;
|
||||
}
|
||||
SingleTransact(msg.sender, _value, _to, _data, created);
|
||||
} else {
|
||||
// determine our operation hash.
|
||||
o_hash = sha3(msg.data, block.number);
|
||||
// store if it's new
|
||||
if (m_txs[o_hash].to == 0 && m_txs[o_hash].value == 0 && m_txs[o_hash].data.length == 0) {
|
||||
m_txs[o_hash].to = _to;
|
||||
m_txs[o_hash].value = _value;
|
||||
m_txs[o_hash].data = _data;
|
||||
}
|
||||
if (!confirm(o_hash)) {
|
||||
ConfirmationNeeded(o_hash, msg.sender, _value, _to, _data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function create(uint _value, bytes _code) internal returns (address o_addr) {
|
||||
assembly {
|
||||
o_addr := create(_value, add(_code, 0x20), mload(_code))
|
||||
jumpi(invalidJumpLabel, iszero(extcodesize(o_addr)))
|
||||
}
|
||||
}
|
||||
|
||||
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
||||
// to determine the body of the transaction from the hash provided.
|
||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool o_success) {
|
||||
if (m_txs[_h].to != 0 || m_txs[_h].value != 0 || m_txs[_h].data.length != 0) {
|
||||
address created;
|
||||
if (m_txs[_h].to == 0) {
|
||||
created = create(m_txs[_h].value, m_txs[_h].data);
|
||||
} else {
|
||||
if (!m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data))
|
||||
throw;
|
||||
}
|
||||
|
||||
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data, created);
|
||||
delete m_txs[_h];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||
// determine what index the present sender is:
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
var pending = m_pending[_operation];
|
||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||
if (pending.yetNeeded == 0) {
|
||||
// reset count of confirmations needed.
|
||||
pending.yetNeeded = m_required;
|
||||
// reset which owners have confirmed (none) - set our bitmap to 0.
|
||||
pending.ownersDone = 0;
|
||||
pending.index = m_pendingIndex.length++;
|
||||
m_pendingIndex[pending.index] = _operation;
|
||||
}
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
// make sure we (the message sender) haven't confirmed this operation previously.
|
||||
if (pending.ownersDone & ownerIndexBit == 0) {
|
||||
Confirmation(msg.sender, _operation);
|
||||
// ok - check if count is enough to go ahead.
|
||||
if (pending.yetNeeded <= 1) {
|
||||
// enough confirmations: reset and run interior.
|
||||
delete m_pendingIndex[m_pending[_operation].index];
|
||||
delete m_pending[_operation];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reorganizeOwners() private {
|
||||
uint free = 1;
|
||||
while (free < m_numOwners)
|
||||
{
|
||||
while (free < m_numOwners && m_owners[free] != 0) free++;
|
||||
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
||||
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
||||
{
|
||||
m_owners[free] = m_owners[m_numOwners];
|
||||
m_ownerIndex[m_owners[free]] = free;
|
||||
m_owners[m_numOwners] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
||||
// returns true. otherwise just returns false.
|
||||
function underLimit(uint _value) internal onlyowner returns (bool) {
|
||||
// reset the spend limit if we're on a different day to last time.
|
||||
if (today() > m_lastDay) {
|
||||
m_spentToday = 0;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// check to see if there's enough left - if so, subtract and return true.
|
||||
// overflow protection // dailyLimit check
|
||||
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
||||
m_spentToday += _value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// determines today's index.
|
||||
function today() private constant returns (uint) { return now / 1 days; }
|
||||
|
||||
function clearPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
|
||||
for (uint i = 0; i < length; ++i) {
|
||||
delete m_txs[m_pendingIndex[i]];
|
||||
|
||||
if (m_pendingIndex[i] != 0)
|
||||
delete m_pending[m_pendingIndex[i]];
|
||||
}
|
||||
|
||||
delete m_pendingIndex;
|
||||
}
|
||||
|
||||
// FIELDS
|
||||
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
||||
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public m_required;
|
||||
// pointer used to find a free slot in m_owners
|
||||
uint public m_numOwners;
|
||||
|
||||
uint public m_dailyLimit;
|
||||
uint public m_spentToday;
|
||||
uint public m_lastDay;
|
||||
|
||||
// list of owners
|
||||
uint[256] m_owners;
|
||||
|
||||
uint constant c_maxOwners = 250;
|
||||
// index on the list of owners to allow reverse lookup
|
||||
mapping(uint => uint) m_ownerIndex;
|
||||
// the ongoing operations.
|
||||
mapping(bytes32 => PendingState) m_pending;
|
||||
bytes32[] m_pendingIndex;
|
||||
|
||||
// pending transactions we have at present.
|
||||
mapping (bytes32 => Transaction) m_txs;
|
||||
}
|
||||
|
||||
contract Wallet is WalletEvents {
|
||||
|
||||
// WALLET CONSTRUCTOR
|
||||
// calls the `initWallet` method of the Library in this context
|
||||
function Wallet(address[] _owners, uint _required, uint _daylimit) {
|
||||
// Signature of the Wallet Library's init function
|
||||
bytes4 sig = bytes4(sha3("initWallet(address[],uint256,uint256)"));
|
||||
address target = _walletLibrary;
|
||||
|
||||
// Compute the size of the call data : arrays has 2
|
||||
// 32bytes for offset and length, plus 32bytes per element ;
|
||||
// plus 2 32bytes for each uint
|
||||
uint argarraysize = (2 + _owners.length);
|
||||
uint argsize = (2 + argarraysize) * 32;
|
||||
|
||||
assembly {
|
||||
// Add the signature first to memory
|
||||
mstore(0x0, sig)
|
||||
// Add the call data, which is at the end of the
|
||||
// code
|
||||
codecopy(0x4, sub(codesize, argsize), argsize)
|
||||
// Delegate call to the library
|
||||
delegatecall(sub(gas, 10000), target, 0x0, add(argsize, 0x4), 0x0, 0x0)
|
||||
}
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// gets called when no other function matches
|
||||
function() payable {
|
||||
// just being sent some cash?
|
||||
if (msg.value > 0)
|
||||
Deposit(msg.sender, msg.value);
|
||||
else if (msg.data.length > 0)
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
_walletLibrary.delegatecall(msg.data); //it should have whitelisted specific methods that the user is allowed to call
|
||||
}
|
||||
|
||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
function getOwner(uint ownerIndex) constant returns (address) {
|
||||
return address(m_owners[ownerIndex + 1]);
|
||||
}
|
||||
|
||||
// As return statement unavailable in fallback, explicit the method here
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) {
|
||||
return _walletLibrary.delegatecall(msg.data);
|
||||
}
|
||||
|
||||
function isOwner(address _addr) constant returns (bool) {
|
||||
return _walletLibrary.delegatecall(msg.data);
|
||||
}
|
||||
|
||||
// FIELDS
|
||||
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
||||
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public m_required;
|
||||
// pointer used to find a free slot in m_owners
|
||||
uint public m_numOwners;
|
||||
|
||||
uint public m_dailyLimit;
|
||||
uint public m_spentToday;
|
||||
uint public m_lastDay;
|
||||
|
||||
// list of owners
|
||||
uint[256] m_owners;
|
||||
}
|
||||
406
dataset/access_control/parity_wallet_bug_2.sol
Normal file
406
dataset/access_control/parity_wallet_bug_2.sol
Normal file
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* @source: https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-106#walletlibrarysol
|
||||
* @author: -
|
||||
* @vulnerable_at_lines: 226,233
|
||||
*/
|
||||
|
||||
//sol Wallet
|
||||
// Multi-sig, daily-limited account proxy/wallet.
|
||||
// @authors:
|
||||
// Gav Wood <g@ethdev.com>
|
||||
// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
|
||||
// single, or, crucially, each of a number of, designated owners.
|
||||
// usage:
|
||||
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
|
||||
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
|
||||
// interior is executed.
|
||||
|
||||
pragma solidity ^0.4.9;
|
||||
|
||||
contract WalletEvents {
|
||||
// EVENTS
|
||||
|
||||
// this contract only has six types of events: it can accept a confirmation, in which case
|
||||
// we record owner and operation (hash) alongside it.
|
||||
event Confirmation(address owner, bytes32 operation);
|
||||
event Revoke(address owner, bytes32 operation);
|
||||
|
||||
// some others are in the case of an owner changing.
|
||||
event OwnerChanged(address oldOwner, address newOwner);
|
||||
event OwnerAdded(address newOwner);
|
||||
event OwnerRemoved(address oldOwner);
|
||||
|
||||
// the last one is emitted if the required signatures change
|
||||
event RequirementChanged(uint newRequirement);
|
||||
|
||||
// Funds has arrived into the wallet (record how much).
|
||||
event Deposit(address _from, uint value);
|
||||
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
||||
event SingleTransact(address owner, uint value, address to, bytes data, address created);
|
||||
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
||||
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data, address created);
|
||||
// Confirmation still needed for a transaction.
|
||||
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||
}
|
||||
|
||||
contract WalletAbi {
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation) external;
|
||||
|
||||
// Replaces an owner `_from` with another `_to`.
|
||||
function changeOwner(address _from, address _to) external;
|
||||
|
||||
function addOwner(address _owner) external;
|
||||
|
||||
function removeOwner(address _owner) external;
|
||||
|
||||
function changeRequirement(uint _newRequired) external;
|
||||
|
||||
function isOwner(address _addr) constant returns (bool);
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool);
|
||||
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) external;
|
||||
|
||||
function execute(address _to, uint _value, bytes _data) external returns (bytes32 o_hash);
|
||||
function confirm(bytes32 _h) returns (bool o_success);
|
||||
}
|
||||
|
||||
contract WalletLibrary is WalletEvents {
|
||||
// TYPES
|
||||
|
||||
// struct for the status of a pending operation.
|
||||
struct PendingState {
|
||||
uint yetNeeded;
|
||||
uint ownersDone;
|
||||
uint index;
|
||||
}
|
||||
|
||||
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
||||
struct Transaction {
|
||||
address to;
|
||||
uint value;
|
||||
bytes data;
|
||||
}
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple single-sig function modifier.
|
||||
modifier onlyowner {
|
||||
if (isOwner(msg.sender))
|
||||
_;
|
||||
}
|
||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||
// that later attempts can be realised as the same underlying operation and
|
||||
// thus count as confirmations.
|
||||
modifier onlymanyowners(bytes32 _operation) {
|
||||
if (confirmAndCheck(_operation))
|
||||
_;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// gets called when no other function matches
|
||||
function() payable {
|
||||
// just being sent some cash?
|
||||
if (msg.value > 0)
|
||||
Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
||||
// as well as the selection of addresses capable of confirming them.
|
||||
function initMultiowned(address[] _owners, uint _required) only_uninitialized {
|
||||
m_numOwners = _owners.length + 1;
|
||||
m_owners[1] = uint(msg.sender);
|
||||
m_ownerIndex[uint(msg.sender)] = 1;
|
||||
for (uint i = 0; i < _owners.length; ++i)
|
||||
{
|
||||
m_owners[2 + i] = uint(_owners[i]);
|
||||
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
||||
}
|
||||
m_required = _required;
|
||||
}
|
||||
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
var pending = m_pending[_operation];
|
||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||
pending.yetNeeded++;
|
||||
pending.ownersDone -= ownerIndexBit;
|
||||
Revoke(msg.sender, _operation);
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces an owner `_from` with another `_to`.
|
||||
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_to)) return;
|
||||
uint ownerIndex = m_ownerIndex[uint(_from)];
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
clearPending();
|
||||
m_owners[ownerIndex] = uint(_to);
|
||||
m_ownerIndex[uint(_from)] = 0;
|
||||
m_ownerIndex[uint(_to)] = ownerIndex;
|
||||
OwnerChanged(_from, _to);
|
||||
}
|
||||
|
||||
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_owner)) return;
|
||||
|
||||
clearPending();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
reorganizeOwners();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
return;
|
||||
m_numOwners++;
|
||||
m_owners[m_numOwners] = uint(_owner);
|
||||
m_ownerIndex[uint(_owner)] = m_numOwners;
|
||||
OwnerAdded(_owner);
|
||||
}
|
||||
|
||||
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
if (ownerIndex == 0) return;
|
||||
if (m_required > m_numOwners - 1) return;
|
||||
|
||||
m_owners[ownerIndex] = 0;
|
||||
m_ownerIndex[uint(_owner)] = 0;
|
||||
clearPending();
|
||||
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
||||
OwnerRemoved(_owner);
|
||||
}
|
||||
|
||||
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
|
||||
if (_newRequired > m_numOwners) return;
|
||||
m_required = _newRequired;
|
||||
clearPending();
|
||||
RequirementChanged(_newRequired);
|
||||
}
|
||||
|
||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
function getOwner(uint ownerIndex) external constant returns (address) {
|
||||
return address(m_owners[ownerIndex + 1]);
|
||||
}
|
||||
|
||||
function isOwner(address _addr) constant returns (bool) {
|
||||
return m_ownerIndex[uint(_addr)] > 0;
|
||||
}
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) {
|
||||
var pending = m_pending[_operation];
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return false;
|
||||
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||
}
|
||||
|
||||
// constructor - stores initial daily limit and records the present day's index.
|
||||
function initDaylimit(uint _limit) only_uninitialized {
|
||||
m_dailyLimit = _limit;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
|
||||
m_dailyLimit = _newLimit;
|
||||
}
|
||||
// resets the amount already spent today. needs many of the owners to confirm.
|
||||
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
|
||||
m_spentToday = 0;
|
||||
}
|
||||
|
||||
// throw unless the contract is not yet initialized.
|
||||
modifier only_uninitialized { if (m_numOwners > 0) throw; _; }
|
||||
|
||||
// constructor - just pass on the owner array to the multiowned and
|
||||
// the limit to daylimit
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function initWallet(address[] _owners, uint _required, uint _daylimit) only_uninitialized {
|
||||
initDaylimit(_daylimit);
|
||||
initMultiowned(_owners, _required);
|
||||
}
|
||||
|
||||
// kills the contract sending everything to `_to`.
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
suicide(_to);
|
||||
}
|
||||
|
||||
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
||||
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
||||
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
||||
// and _data arguments). They still get the option of using them if they want, anyways.
|
||||
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 o_hash) {
|
||||
// first, take the opportunity to check that we're under the daily limit.
|
||||
if ((_data.length == 0 && underLimit(_value)) || m_required == 1) {
|
||||
// yes - just execute the call.
|
||||
address created;
|
||||
if (_to == 0) {
|
||||
created = create(_value, _data);
|
||||
} else {
|
||||
if (!_to.call.value(_value)(_data))
|
||||
throw;
|
||||
}
|
||||
SingleTransact(msg.sender, _value, _to, _data, created);
|
||||
} else {
|
||||
// determine our operation hash.
|
||||
o_hash = sha3(msg.data, block.number);
|
||||
// store if it's new
|
||||
if (m_txs[o_hash].to == 0 && m_txs[o_hash].value == 0 && m_txs[o_hash].data.length == 0) {
|
||||
m_txs[o_hash].to = _to;
|
||||
m_txs[o_hash].value = _value;
|
||||
m_txs[o_hash].data = _data;
|
||||
}
|
||||
if (!confirm(o_hash)) {
|
||||
ConfirmationNeeded(o_hash, msg.sender, _value, _to, _data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function create(uint _value, bytes _code) internal returns (address o_addr) {
|
||||
/*
|
||||
assembly {
|
||||
o_addr := create(_value, add(_code, 0x20), mload(_code))
|
||||
jumpi(invalidJumpLabel, iszero(extcodesize(o_addr)))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
||||
// to determine the body of the transaction from the hash provided.
|
||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool o_success) {
|
||||
if (m_txs[_h].to != 0 || m_txs[_h].value != 0 || m_txs[_h].data.length != 0) {
|
||||
address created;
|
||||
if (m_txs[_h].to == 0) {
|
||||
created = create(m_txs[_h].value, m_txs[_h].data);
|
||||
} else {
|
||||
if (!m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data))
|
||||
throw;
|
||||
}
|
||||
|
||||
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data, created);
|
||||
delete m_txs[_h];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||
// determine what index the present sender is:
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
var pending = m_pending[_operation];
|
||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||
if (pending.yetNeeded == 0) {
|
||||
// reset count of confirmations needed.
|
||||
pending.yetNeeded = m_required;
|
||||
// reset which owners have confirmed (none) - set our bitmap to 0.
|
||||
pending.ownersDone = 0;
|
||||
pending.index = m_pendingIndex.length++;
|
||||
m_pendingIndex[pending.index] = _operation;
|
||||
}
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
// make sure we (the message sender) haven't confirmed this operation previously.
|
||||
if (pending.ownersDone & ownerIndexBit == 0) {
|
||||
Confirmation(msg.sender, _operation);
|
||||
// ok - check if count is enough to go ahead.
|
||||
if (pending.yetNeeded <= 1) {
|
||||
// enough confirmations: reset and run interior.
|
||||
delete m_pendingIndex[m_pending[_operation].index];
|
||||
delete m_pending[_operation];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reorganizeOwners() private {
|
||||
uint free = 1;
|
||||
while (free < m_numOwners)
|
||||
{
|
||||
while (free < m_numOwners && m_owners[free] != 0) free++;
|
||||
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
||||
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
||||
{
|
||||
m_owners[free] = m_owners[m_numOwners];
|
||||
m_ownerIndex[m_owners[free]] = free;
|
||||
m_owners[m_numOwners] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
||||
// returns true. otherwise just returns false.
|
||||
function underLimit(uint _value) internal onlyowner returns (bool) {
|
||||
// reset the spend limit if we're on a different day to last time.
|
||||
if (today() > m_lastDay) {
|
||||
m_spentToday = 0;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// check to see if there's enough left - if so, subtract and return true.
|
||||
// overflow protection // dailyLimit check
|
||||
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
||||
m_spentToday += _value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// determines today's index.
|
||||
function today() private constant returns (uint) { return now / 1 days; }
|
||||
|
||||
function clearPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
|
||||
for (uint i = 0; i < length; ++i) {
|
||||
delete m_txs[m_pendingIndex[i]];
|
||||
|
||||
if (m_pendingIndex[i] != 0)
|
||||
delete m_pending[m_pendingIndex[i]];
|
||||
}
|
||||
|
||||
delete m_pendingIndex;
|
||||
}
|
||||
|
||||
// FIELDS
|
||||
address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
|
||||
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public m_required;
|
||||
// pointer used to find a free slot in m_owners
|
||||
uint public m_numOwners;
|
||||
|
||||
uint public m_dailyLimit;
|
||||
uint public m_spentToday;
|
||||
uint public m_lastDay;
|
||||
|
||||
// list of owners
|
||||
uint[256] m_owners;
|
||||
|
||||
uint constant c_maxOwners = 250;
|
||||
// index on the list of owners to allow reverse lookup
|
||||
mapping(uint => uint) m_ownerIndex;
|
||||
// the ongoing operations.
|
||||
mapping(bytes32 => PendingState) m_pending;
|
||||
bytes32[] m_pendingIndex;
|
||||
|
||||
// pending transactions we have at present.
|
||||
mapping (bytes32 => Transaction) m_txs;
|
||||
}
|
||||
23
dataset/access_control/phishable.sol
Normal file
23
dataset/access_control/phishable.sol
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* @source: https://github.com/sigp/solidity-security-blog
|
||||
* @author: -
|
||||
* @vulnerable_at_lines: 20
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.22;
|
||||
|
||||
contract Phishable {
|
||||
address public owner;
|
||||
|
||||
constructor (address _owner) {
|
||||
owner = _owner;
|
||||
}
|
||||
|
||||
function () public payable {} // collect ether
|
||||
|
||||
function withdrawAll(address _recipient) public {
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
require(tx.origin == owner);
|
||||
_recipient.transfer(this.balance);
|
||||
}
|
||||
}
|
||||
22
dataset/access_control/proxy.sol
Normal file
22
dataset/access_control/proxy.sol
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* @source: https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-112#proxysol
|
||||
* @author: -
|
||||
* @vulnerable_at_lines: 19
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
contract Proxy {
|
||||
|
||||
address owner;
|
||||
|
||||
constructor() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function forward(address callee, bytes _data) public {
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
require(callee.delegatecall(_data)); //Use delegatecall with caution and make sure to never call into untrusted contracts
|
||||
}
|
||||
|
||||
}
|
||||
162
dataset/access_control/rubixi.sol
Normal file
162
dataset/access_control/rubixi.sol
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* @source: https://github.com/trailofbits/not-so-smart-contracts/blob/master/wrong_constructor_name/Rubixi_source_code/Rubixi.sol
|
||||
* @author: -
|
||||
* @vulnerable_at_lines: 23,24
|
||||
*/
|
||||
|
||||
// 0xe82719202e5965Cf5D9B6673B7503a3b92DE20be#code
|
||||
pragma solidity ^0.4.15;
|
||||
|
||||
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
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function DynamicPyramid() {
|
||||
creator = msg.sender; //anyone can call this
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
dataset/access_control/simple_suicide.sol
Normal file
16
dataset/access_control/simple_suicide.sol
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* @source: https://github.com/SmartContractSecurity/SWC-registry/blob/master/test_cases/unprotected_critical_functions/simple_suicide.sol
|
||||
* @author: -
|
||||
* @vulnerable_at_lines: 12,13
|
||||
*/
|
||||
|
||||
//added prgma version
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract SimpleSuicide {
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function sudicideAnyone() {
|
||||
selfdestruct(msg.sender);
|
||||
}
|
||||
|
||||
}
|
||||
39
dataset/access_control/unprotected0.sol
Normal file
39
dataset/access_control/unprotected0.sol
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* @source: https://github.com/trailofbits/not-so-smart-contracts/blob/master/unprotected_function/Unprotected.sol
|
||||
* @author: -
|
||||
* @vulnerable_at_lines: 25
|
||||
*/
|
||||
|
||||
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
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function changeOwner(address _newOwner)
|
||||
public
|
||||
{
|
||||
owner = _newOwner;
|
||||
}
|
||||
|
||||
/*
|
||||
function changeOwner_fixed(address _newOwner)
|
||||
public
|
||||
onlyowner
|
||||
{
|
||||
owner = _newOwner;
|
||||
}
|
||||
*/
|
||||
}
|
||||
46
dataset/access_control/wallet_02_refund_nosub.sol
Normal file
46
dataset/access_control/wallet_02_refund_nosub.sol
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* @source: https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-105#wallet-02-refund-nosubsol
|
||||
* @author: -
|
||||
* @vulnerable_at_lines: 36
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
/* User can add pay in and withdraw Ether.
|
||||
Unfortunately the developer forgot set the user's balance to 0 when refund() is called.
|
||||
An attacker can pay in a small amount of Ether and call refund() repeatedly to empty the contract.
|
||||
*/
|
||||
|
||||
contract Wallet {
|
||||
address creator;
|
||||
|
||||
mapping(address => uint256) balances;
|
||||
|
||||
constructor() public {
|
||||
creator = msg.sender;
|
||||
}
|
||||
|
||||
function deposit() public payable {
|
||||
assert(balances[msg.sender] + msg.value > balances[msg.sender]);
|
||||
balances[msg.sender] += msg.value;
|
||||
}
|
||||
|
||||
function withdraw(uint256 amount) public {
|
||||
require(amount <= balances[msg.sender]);
|
||||
msg.sender.transfer(amount);
|
||||
balances[msg.sender] -= amount;
|
||||
}
|
||||
|
||||
function refund() public {
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
msg.sender.transfer(balances[msg.sender]);
|
||||
}
|
||||
|
||||
// In an emergency the owner can migrate allfunds to a different address.
|
||||
|
||||
function migrateTo(address to) public {
|
||||
require(creator == msg.sender);
|
||||
to.transfer(this.balance);
|
||||
}
|
||||
|
||||
}
|
||||
41
dataset/access_control/wallet_03_wrong_constructor.sol
Normal file
41
dataset/access_control/wallet_03_wrong_constructor.sol
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* @source: https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-105#wallet-03-wrong-constructorsol
|
||||
* @author: -
|
||||
* @vulnerable_at_lines: 19,20
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
/* User can add pay in and withdraw Ether.
|
||||
The constructor is wrongly named, so anyone can become 'creator' and withdraw all funds.
|
||||
*/
|
||||
|
||||
contract Wallet {
|
||||
address creator;
|
||||
|
||||
mapping(address => uint256) balances;
|
||||
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
function initWallet() public {
|
||||
creator = msg.sender;
|
||||
}
|
||||
|
||||
function deposit() public payable {
|
||||
assert(balances[msg.sender] + msg.value > balances[msg.sender]);
|
||||
balances[msg.sender] += msg.value;
|
||||
}
|
||||
|
||||
function withdraw(uint256 amount) public {
|
||||
require(amount <= balances[msg.sender]);
|
||||
msg.sender.transfer(amount);
|
||||
balances[msg.sender] -= amount;
|
||||
}
|
||||
|
||||
// In an emergency the owner can migrate allfunds to a different address.
|
||||
|
||||
function migrateTo(address to) public {
|
||||
require(creator == msg.sender);
|
||||
to.transfer(this.balance);
|
||||
}
|
||||
|
||||
}
|
||||
42
dataset/access_control/wallet_04_confused_sign.sol
Normal file
42
dataset/access_control/wallet_04_confused_sign.sol
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* @source: https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-105#wallet-04-confused-signsol
|
||||
* @author: -
|
||||
* @vulnerable_at_lines: 30
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
/* User can add pay in and withdraw Ether.
|
||||
Unfortunatelty, the developer was drunk and used the wrong comparison operator in "withdraw()"
|
||||
Anybody can withdraw arbitrary amounts of Ether :()
|
||||
*/
|
||||
|
||||
contract Wallet {
|
||||
address creator;
|
||||
|
||||
mapping(address => uint256) balances;
|
||||
|
||||
constructor() public {
|
||||
creator = msg.sender;
|
||||
}
|
||||
|
||||
function deposit() public payable {
|
||||
assert(balances[msg.sender] + msg.value > balances[msg.sender]);
|
||||
balances[msg.sender] += msg.value;
|
||||
}
|
||||
|
||||
function withdraw(uint256 amount) public {
|
||||
// <yes> <report> ACCESS_CONTROL
|
||||
require(amount >= balances[msg.sender]);
|
||||
msg.sender.transfer(amount);
|
||||
balances[msg.sender] -= amount;
|
||||
}
|
||||
|
||||
// In an emergency the owner can migrate allfunds to a different address.
|
||||
|
||||
function migrateTo(address to) public {
|
||||
require(creator == msg.sender);
|
||||
to.transfer(this.balance);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user