Skip to content

Commit

Permalink
Add syntax highlighting
Browse files Browse the repository at this point in the history
  • Loading branch information
kadenzipfel committed Oct 29, 2024
1 parent 97b995e commit 1fca039
Show file tree
Hide file tree
Showing 13 changed files with 28 additions and 28 deletions.
2 changes: 1 addition & 1 deletion vulnerabilities/asserting-contract-from-code-size.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A common method for asserting whether a sender is a contract or EOA has been to check the code size of the sender. This check asserts that if the sender has a code size > 0 that it must be a contract and if not then it must be an EOA. For example:

```
```solidity
function mint(uint256 amount) public {
if (msg.sender.code.length != 0) revert CallerNotEOA();
}
Expand Down
4 changes: 2 additions & 2 deletions vulnerabilities/authorization-txorigin.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

`tx.origin` is a global variable in Solidity which returns the address that sent a transaction. It's important that you never use `tx.origin` for authorization since another contract can use a fallback function to call your contract and gain authorization since the authorized address is stored in `tx.origin`. Consider this example:

```
```solidity
pragma solidity >=0.5.0 <0.7.0;
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
Expand All @@ -22,7 +22,7 @@ contract TxUserWallet {

Here we can see that the `TxUserWallet` contract authorizes the `transferTo()` function with `tx.origin`.

```
```solidity
pragma solidity >=0.5.0 <0.7.0;
interface TxUserWallet {
Expand Down
2 changes: 1 addition & 1 deletion vulnerabilities/dos-gas-limit.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ An effective solution to this problem would be to use a pull payment system over

If, for some reason, you really need to loop through an array of unspecified length, at least expect it to potentially take multiple blocks, and allow it to be performed in multiple transactions - as seen in this example:

```
```solidity
struct Payee {
address addr;
uint256 value;
Expand Down
6 changes: 3 additions & 3 deletions vulnerabilities/dos-revert.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This can be problematic in the case that the funds are sent to a smart contract

For example:

```
```solidity
// INSECURE
contract Auction {
address currentLeader;
Expand All @@ -31,7 +31,7 @@ As you can see in this example, if an attacker bids from a smart contract with a

This can also be problematic without an attacker present. For example, you may want to pay an array of users by iterating through the array, and of course you would want to make sure each user is properly paid. The problem here is that if one payment fails, the function is reverted and no one is paid.

```
```solidity
address[] private refundAddresses;
mapping (address => uint) public refunds;
Expand All @@ -45,7 +45,7 @@ function refundAll() public {

An effective solution to this problem would be to use a pull payment system over the above push payment system. To do this, separate each payment into its own transaction, and have the recipient call the function.

```
```solidity
contract auction {
address highestBidder;
uint highestBid;
Expand Down
10 changes: 5 additions & 5 deletions vulnerabilities/hash-collision.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ When `abi.encodePacked()` is used with multiple variable-length arguments (such

For example, consider the following two calls to `abi.encodePacked()`:

```
```solidity
abi.encodePacked(["a", "b"], ["c", "d"])
```

```
```solidity
abi.encodePacked(["a"], ["b", "c", "d"])
```

Both calls could potentially produce the same packed encoding because `abi.encodePacked()` simply concatenates the elements without any delimiters!

Consider the below example for strings:

```
```solidity
abi.encodePacked("foo", "bar") == abi.encodePacked("fo", "obar")
```

Expand Down Expand Up @@ -71,12 +71,12 @@ function verifyMessage(string calldata message1, string calldata message2, bytes

The above function `verifyMessage()` could easily be exploited as below:-

```
```solidity
verifyMessage("hello", "world", signature);
```
or

```
```solidity
verifyMessage("hell", "oworld", signature);
```

Expand Down
4 changes: 2 additions & 2 deletions vulnerabilities/insufficient-gas-griefing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Insufficient gas griefing can be done on contracts which accept data and use it

Let's consider a simple relayer contract as an example. As shown below, the relayer contract allows someone to make and sign a transaction, without having to execute the transaction. Often this is used when a user can't pay for the gas associated with the transaction.

```
```solidity
contract Relayer {
mapping (bytes => bool) executed;
Expand All @@ -21,7 +21,7 @@ The user who executes the transaction, the 'forwarder', can effectively censor t

There are two ways this could be prevented. The first solution would be to only allow trusted users to relay transactions. The other solution is to require that the forwarder provides enough gas, as seen below.

```
```solidity
// contract called by Relayer
contract Executor {
function execute(bytes _data, uint _gasLimit) {
Expand Down
2 changes: 1 addition & 1 deletion vulnerabilities/lack-of-precision.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ In Solidity, there are a limited variety of number types. Differently from many

Since division often results in a remainder, performing division with integers generally requires a lack of precision to some degree. To see how a lack of precision may cause a serious flaw, consider the following example in which we charge a fee for early withdrawals denominated in the number of days early that the withdrawal is made:

```
```solidity
uint256 daysEarly = withdrawalsOpenTimestamp - block.timestamp / 1 days
uint256 fee = amount / daysEarly * dailyFee
```
Expand Down
4 changes: 2 additions & 2 deletions vulnerabilities/off-by-one.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Properly determining intended array lengths is a common source of off-by-one err

Consider for example a function intended to loop over a list of recipients to transfer funds to each user, but the loop length is incorrectly set.

```
```solidity
// Incorrectly sets upper bound to users.length - 1
// Final user in array doesn't receive token transfer
for (uint256 i; i < users.length - 1; ++i) {
Expand All @@ -22,7 +22,7 @@ It's common for comparison operators to be off by one when, e.g. `>` should be u

Consider for example a Defi protocol with liquidation logic documented to liquidate a user only if their collateralization ratio is *below* 1e18.

```
```solidity
// Incorrectly liquidates if collateralizationRatio is == 1 ether
if (collateralizationRatio > 1 ether) {
...
Expand Down
10 changes: 5 additions & 5 deletions vulnerabilities/reentrancy.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ When Ether is transferred to a contract address, it will trigger the `receive` o

One example of a hard to spot external call is OpenZeppelin's [`ERC721._safeMint`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/3f610ebc25480bf6145e519c96e2f809996db8ed/contracts/token/ERC721/ERC721.sol#L244) & [`ERC721._safeTransfer`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/3f610ebc25480bf6145e519c96e2f809996db8ed/contracts/token/ERC721/ERC721.sol#L190) functions.

```
```solidity
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
Expand All @@ -38,7 +38,7 @@ The function is titled `_safeMint` because it prevents tokens from being uninten

A single function reentrancy attack occurs when a vulnerable function is the same function that an attacker is trying to recursively call.

```
```solidity
// UNSECURE
function withdraw() external {
uint256 amount = balances[msg.sender];
Expand All @@ -54,7 +54,7 @@ Here we can see that the balance is only modified after the funds have been tran

A cross-function reentrancy attack is a more complex version of the same process. Cross-function reentrancy occurs when a vulnerable function shares state with a function that an attacker can exploit.

```
```solidity
// UNSECURE
function transfer(address to, uint amount) external {
if (balances[msg.sender] >= amount) {
Expand All @@ -77,7 +77,7 @@ In this example, a hacker can exploit this contract by having a fallback functio

Read-only reentrancy is a novel attack vector in which instead of reentering into the same contract in which state changes have yet to be made, an attacker reenters into another contract which reads from the state of the original contract.

```
```solidity
// UNSECURE
contract A {
// Has a reentrancy guard to prevent reentrancy
Expand Down Expand Up @@ -116,7 +116,7 @@ Finally, we can perform *interactions* with other smart contracts, e.g. external

This structure is effective against reentrancy because when an attacker reenters the function, the state changes have already been made. For example:

```
```solidity
function withdraw() external {
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0;
Expand Down
2 changes: 1 addition & 1 deletion vulnerabilities/shadowing-state-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

It is possible to use the same variable twice in Solidity, but it can lead to unintended side effects. This is especially difficult regarding working with multiple contracts. Take the following example:

```
```solidity
contract SuperContract {
uint a = 1;
}
Expand Down
2 changes: 1 addition & 1 deletion vulnerabilities/signature-malleability.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

It's generally assumed that a valid signature cannot be modified without the private key and remain valid. However, it is possible to modify and signature and maintain validity. One example of a system which is vulnerable to signature malleability is one in which validation as to whether an action can be executed is determined based on whether the signature has been previously used.

```
```solidity
// UNSECURE
require(!signatureUsed[signature]);
Expand Down
4 changes: 2 additions & 2 deletions vulnerabilities/unexpected-ecrecover-null-address.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This can be manipulated by attackers to make it seem like a valid message has be

> **NOTE:** The default value for addresses in solidity is `address(0)`. As such, in case important storage variables, e.g. owner/admin, are unset, it's possible to spoof a signature from one of these unset addresses, executing authorized-only logic.
```
```solidity
// UNSECURE
function validateSigner(address signer, bytes32 message, uint8 v, bytes32 r, bytes32 s) internal pure returns (bool) {
address recoveredSigner = ecrecover(message, v, r, s);
Expand All @@ -20,7 +20,7 @@ The above method is intended to only return true if a valid signature is provide

We can mitigate this issue by reverting if the `recoveredSigner` address is null, e.g.:

```
```solidity
function validateSigner(address signer, bytes32 message, uint8 v, bytes32 r, bytes32 s) internal pure returns (bool) {
address recoveredSigner = ecrecover(message, v, r, s);
require(recoveredSigner != address(0));
Expand Down
4 changes: 2 additions & 2 deletions vulnerabilities/unsafe-low-level-call.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Low-level calls will never throw an exception, instead they will return `false`

In the case that you use low-level calls, be sure to check the return value to handle possible failed calls, e.g.:

```
```solidity
// Simple transfer of 1 ether
(bool success,) = to.call{value: 1 ether}("");
// Revert if unsuccessful
Expand All @@ -25,7 +25,7 @@ Note that this check is not performed in case of [low-level calls](https://doc

It's imperative that we do not simply assume that a contract to be called via a low-level call actually exists, since if it doesn't our logic will proceed even though our external call effectively failed. This can lead to loss of funds and/or an invalid contract state. Instead, we must verify that the contract being called exists, either immediately before being called with an `extcodesize` check, or by verifying during contract deployment and using a `constant`/`immutable` value if the contract can be fully trusted.

```
```solidity
// Verify address is a contract
require(to.code.length > 0);
// Simple transfer of 1 ether
Expand Down

0 comments on commit 1fca039

Please sign in to comment.