Add SB Curated (copied from the smartbugs repository).

This commit is contained in:
Joao F. Ferreira
2022-11-23 09:07:09 +00:00
parent 03da27c72a
commit 254a3b20c1
156 changed files with 17228 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
# Denial Of Service
Including gas limit reached, unexpected throw, unexpected kill, access control breached.
Denial of service is deadly in the world of Ethereum: while other types of applications can eventually recover, smart contracts can be taken offline forever by just one of these attacks. Many ways lead to denials of service, including maliciously behaving when being the recipient of a transaction, artificially increasing the gas necessary to compute a function, abusing access controls to access private components of smart contracts, taking advantage of mixups and negligence, etc. This class of attack includes many different variants and will probably see a lot of development in the years to come.
Loss: estimated at 514,874 ETH (~300M USD at the time)
## Attack Scenario
An auction contract allows its users to bid on different assets.
To bid, a user must call a bid(uint object) function with the desired amount of ether. The auction contract will store the ether in escrow until the object's owner accepts the bid or the initial bidder cancels it. This means that the auction contract must hold the full value of any unresolved bid in its balance.
The auction contract also contains a withdraw(uint amount) function which allows admins to retrieve funds from the contract. As the function sends the amount to a hardcoded address, the developers have decided to make the function public.
An attacker sees a potential attack and calls the function, directing all the contract's funds to its admins. This destroys the promise of escrow and blocks all the pending bids.
While the admins might return the escrowed money to the contract, the attacker can continue the attack by simply withdrawing the funds again.
## Examples
In the following example (inspired by King of the Ether) a function of a game contract allows you to become the president if you publicly bribe the previous one. Unfortunately, if the previous president is a smart contract and causes reversion on payment, the transfer of power will fail and the malicious smart contract will remain president forever. Sounds like a dictatorship to me:
```
function becomePresident() payable {
require(msg.value >= price); // must pay the price to become president
president.transfer(price); // we pay the previous president
president = msg.sender; // we crown the new president
price = price * 2; // we double the price to become president
}
```
In this second example, a caller can decide who the next function call will reward. Because of the expensive instructions in the for loop, an attacker can introduce a number too large to iterate on (due to gas block limitations in Ethereum) which will effectively block the function from functioning.
```
function selectNextWinners(uint256 _largestWinner) {
for(uint256 i = 0; i < largestWinner, i++) {
// heavy code
}
largestWinner = _largestWinner;
}
```
## References
Taken from [DASP TOP10](https://dasp.co/)

View File

@@ -0,0 +1,29 @@
/*
* @source: https://github.com/trailofbits/not-so-smart-contracts/blob/master/denial_of_service/auction.sol
* @author: -
* @vulnerable_at_lines: 23
*/
pragma solidity ^0.4.15;
//Auction susceptible to DoS attack
contract DosAuction {
address currentFrontrunner;
uint currentBid;
//Takes in bid, refunding the frontrunner if they are outbid
function bid() payable {
require(msg.value > currentBid);
//If the refund fails, the entire transaction reverts.
//Therefore a frontrunner who always fails will win
if (currentFrontrunner != 0) {
//E.g. if recipients fallback function is just revert()
// <yes> <report> DENIAL_OF_SERVICE
require(currentFrontrunner.send(currentBid));
}
currentFrontrunner = msg.sender;
currentBid = msg.value;
}
}

View File

@@ -0,0 +1,36 @@
/*
* @source: https://github.com/SmartContractSecurity/SWC-registry/blob/master/test_cases/dos_gas_limit/dos_address.sol
* @author: -
* @vulnerable_at_lines: 16,17,18
*/
pragma solidity ^0.4.25;
contract DosGas {
address[] creditorAddresses;
bool win = false;
function emptyCreditors() public {
// <yes> <report> DENIAL_OF_SERVICE
if(creditorAddresses.length>1500) {
creditorAddresses = new address[](0);
win = true;
}
}
function addCreditors() public returns (bool) {
for(uint i=0;i<350;i++) {
creditorAddresses.push(msg.sender);
}
return true;
}
function iWin() public view returns (bool) {
return win;
}
function numberCreditors() public view returns (uint) {
return creditorAddresses.length;
}
}

View File

@@ -0,0 +1,47 @@
/*
* @source: https://github.com/SmartContractSecurity/SWC-registry/blob/master/test_cases/dos_gas_limit/dos_number.sol
* @author: -
* @vulnerable_at_lines: 18,19,20,21,22
*/
pragma solidity ^0.4.25;
contract DosNumber {
uint numElements = 0;
uint[] array;
function insertNnumbers(uint value,uint numbers) public {
// Gas DOS if number > 382 more or less, it depends on actual gas limit
// <yes> <report> DENIAL_OF_SERVICE
for(uint i=0;i<numbers;i++) {
if(numElements == array.length) {
array.length += 1;
}
array[numElements++] = value;
}
}
function clear() public {
require(numElements>1500);
numElements = 0;
}
// Gas DOS clear
function clearDOS() public {
// number depends on actual gas limit
require(numElements>1500);
array = new uint[](0);
numElements = 0;
}
function getLengthArray() public view returns(uint) {
return numElements;
}
function getRealLengthArray() public view returns(uint) {
return array.length;
}
}

View File

@@ -0,0 +1,27 @@
/*
* @source: https://github.com/SmartContractSecurity/SWC-registry/blob/master/test_cases/dos_gas_limit/dos_simple.sol
* @author: -
* @vulnerable_at_lines: 17,18
*/
pragma solidity ^0.4.25;
contract DosOneFunc {
address[] listAddresses;
function ifillArray() public returns (bool){
if(listAddresses.length<1500) {
// <yes> <report> DENIAL_OF_SERVICE
for(uint i=0;i<350;i++) {
listAddresses.push(msg.sender);
}
return true;
} else {
listAddresses = new address[](0);
return false;
}
}
}

View File

@@ -0,0 +1,124 @@
/*
* @source: https://etherscan.io/address/0xf45717552f12ef7cb65e95476f217ea008167ae3#code
* @author: -
* @vulnerable_at_lines: 46,48
*/
//added pragma version
pragma solidity ^0.4.0;
contract Government {
// Global Variables
uint32 public lastCreditorPayedOut;
uint public lastTimeOfNewCredit;
uint public profitFromCrash;
address[] public creditorAddresses;
uint[] public creditorAmounts;
address public corruptElite;
mapping (address => uint) buddies;
uint constant TWELVE_HOURS = 43200;
uint8 public round;
function Government() {
// The corrupt elite establishes a new government
// this is the commitment of the corrupt Elite - everything that can not be saved from a crash
profitFromCrash = msg.value;
corruptElite = msg.sender;
lastTimeOfNewCredit = block.timestamp;
}
function lendGovernmentMoney(address buddy) returns (bool) {
uint amount = msg.value;
// check if the system already broke down. If for 12h no new creditor gives new credit to the system it will brake down.
// 12h are on average = 60*60*12/12.5 = 3456
if (lastTimeOfNewCredit + TWELVE_HOURS < block.timestamp) {
// Return money to sender
msg.sender.send(amount);
// Sends all contract money to the last creditor
creditorAddresses[creditorAddresses.length - 1].send(profitFromCrash);
corruptElite.send(this.balance);
// Reset contract state
lastCreditorPayedOut = 0;
lastTimeOfNewCredit = block.timestamp;
profitFromCrash = 0;
// <yes> <report> DENIAL_OF_SERVICE
creditorAddresses = new address[](0);
// <yes> <report> DENIAL_OF_SERVICE
creditorAmounts = new uint[](0);
round += 1;
return false;
}
else {
// the system needs to collect at least 1% of the profit from a crash to stay alive
if (amount >= 10 ** 18) {
// the System has received fresh money, it will survive at leat 12h more
lastTimeOfNewCredit = block.timestamp;
// register the new creditor and his amount with 10% interest rate
creditorAddresses.push(msg.sender);
creditorAmounts.push(amount * 110 / 100);
// now the money is distributed
// first the corrupt elite grabs 5% - thieves!
corruptElite.send(amount * 5/100);
// 5% are going into the economy (they will increase the value for the person seeing the crash comming)
if (profitFromCrash < 10000 * 10**18) {
profitFromCrash += amount * 5/100;
}
// if you have a buddy in the government (and he is in the creditor list) he can get 5% of your credits.
// Make a deal with him.
if(buddies[buddy] >= amount) {
buddy.send(amount * 5/100);
}
buddies[msg.sender] += amount * 110 / 100;
// 90% of the money will be used to pay out old creditors
if (creditorAmounts[lastCreditorPayedOut] <= address(this).balance - profitFromCrash) {
creditorAddresses[lastCreditorPayedOut].send(creditorAmounts[lastCreditorPayedOut]);
buddies[creditorAddresses[lastCreditorPayedOut]] -= creditorAmounts[lastCreditorPayedOut];
lastCreditorPayedOut += 1;
}
return true;
}
else {
msg.sender.send(amount);
return false;
}
}
}
// fallback function
function() {
lendGovernmentMoney(0);
}
function totalDebt() returns (uint debt) {
for(uint i=lastCreditorPayedOut; i<creditorAmounts.length; i++){
debt += creditorAmounts[i];
}
}
function totalPayedOut() returns (uint payout) {
for(uint i=0; i<lastCreditorPayedOut; i++){
payout += creditorAmounts[i];
}
}
// better don't do it (unless you are the corrupt elite and you want to establish trust in the system)
function investInTheSystem() {
profitFromCrash += msg.value;
}
// From time to time the corrupt elite inherits it's power to the next generation
function inheritToNextGeneration(address nextGeneration) {
if (msg.sender == corruptElite) {
corruptElite = nextGeneration;
}
}
function getCreditorAddresses() returns (address[]) {
return creditorAddresses;
}
function getCreditorAmounts() returns (uint[]) {
return creditorAmounts;
}
}

View File

@@ -0,0 +1,28 @@
/*
* @source: https://consensys.github.io/smart-contract-best-practices/known_attacks/#dos-with-unexpected-revert
* @author: ConsenSys Diligence
* @vulnerable_at_lines: 24
* Modified by Bernhard Mueller
*/
pragma solidity 0.4.24;
contract Refunder {
address[] private refundAddresses;
mapping (address => uint) public refunds;
constructor() {
refundAddresses.push(0x79B483371E87d664cd39491b5F06250165e4b184);
refundAddresses.push(0x79B483371E87d664cd39491b5F06250165e4b185);
}
// bad
function refundAll() public {
for(uint x; x < refundAddresses.length; x++) { // arbitrary length iteration based on how many addresses participated
// <yes> <report> DENIAL_OF_SERVICE
require(refundAddresses[x].send(refunds[refundAddresses[x]])); // doubly bad, now a single failure on send will hold up all funds
}
}
}