The Most Common Smart Contract Hacks & Prevention Tips for Developers
Welcome to the world of Decentralized Finance (DeFi) where trust is replaced with code and billions of dollars are at stake. But just like with any other financial system, DeFi is not immune to cyber-attacks and hacking attempts. In this article, we’ll uncover the most common ways hackers use to attack smart contracts in the DeFi space and the necessary steps that smart contract developers can take to prevent them.
Reentrancy Attacks
One of the most common hacking methods is reentrancy. Where a malicious contract calls the target contract multiple times before the first call is completed. Leading to an overflow of the contract’s balance. Example: Fei Protocol lost $80 million
Consider the following contract:
pragma solidity ^0.8.0;
contract ReentrancyAttack {
uint public balance;
function deposit() public payable {
balance += msg.value;
}
function withdraw(uint amount) public {
require(balance >= amount, "Insufficient balance");
msg.sender.transfer(amount);
balance -= amount;
}
}
In this contract, the withdraw
function is vulnerable to a reentrancy attack. A malicious contract can call deposit
multiple times before the first call to deposit
is completed. Leading to an overflow of the contract’s balance. To prevent this, a mutex (a lock) should be added to the contract:
pragma solidity ^0.8.0;
contract ReentrancyAttack {
uint public balance;
bool public locked;
function deposit() public payable {
balance += msg.value;
}
function withdraw(uint amount) public {
require(balance >= amount, "Insufficient balance");
require(!locked, "Contract is locked");
locked = true;
msg.sender.transfer(amount);
balance -= amount;
locked = false;
}
}
Integer Overflow and Underflow
Another common attack is the manipulation of integer values. Leading to an overflow or underflow, which results in unintended behavior of the contract.
An integer overflow occurs when an arithmetic operation results in a value that is too large to be stored in an integer variable. This can cause the value to wrap around to a negative number or to zero, leading to incorrect results. In smart contracts, an integer overflow can have serious consequences. As allowing an attacker to steal funds or manipulate the contract in unintended ways.
An integer underflow occurs when an arithmetic operation results in a negative number that is too small to be represented by an unsigned integer. This can cause the value to wrap around to a very large positive number, also leading to incorrect results.
Consider the following contract:
pragma solidity ^0.8.0;
contract IntegerOverflowUnderflow {
uint public balance;
function deposit() public payable {
balance += msg.value;
}
function withdraw(uint amount) public {
require(balance >= amount, "Insufficient balance");
balance -= amount;
msg.sender.transfer(amount);
}
function add(uint a, uint b) public pure returns (uint) {
return a + b;
}
}
In the add
function, if the result of a + b
is larger than the maximum value of a uint
, an overflow occurs, leading to unintended behavior.
To prevent integer overflow and underflow attacks, smart contract developers need to be aware of the limitations of integers and take steps to ensure that their contracts handle arithmetic operations in a secure manner. This can include using libraries that provide safe arithmetic functions, or implementing custom logic. To check for overflow and underflow conditions and prevent them from occurring. The SafeMath
library can be used:
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
contract IntegerOverflowUnderflow {
uint public balance;
using SafeMath for uint;
function deposit() public payable {
balance = balance.add(msg.value);
}
function withdraw(uint amount) public {
require(balance >= amount, "Insufficient balance");
balance = balance.sub(amount);
msg.sender.transfer(amount);
}
function add(uint a, uint b) public pure returns (uint) {
return a.add(b);
}
}
Unchecked Approval
An unchecked approval attack is a security vulnerability that can occur in smart contracts. That uses the ERC-20 token standard in the Ethereum network. This type of attack takes advantage of the way the “approve” function is implemented in the ERC-20 standard.
The “approve” function allows one smart contract to authorize another smart contract to transfer a certain amount of tokens on its behalf. If this function is not properly checked, an attacker can transfer more tokens than they were authorized to. Leading to a loss of funds for the owner of the token contract.
This type of attack occurs when the smart contract that is being approved does not properly check the balance of the approved contract before transferring tokens. If the approved contract has a bug that allows it to transfer more tokens than it was authorized, the owner of the token contract could lose their funds.
To prevent this vulnerability, it’s important for smart contract developers to carefully check and verify the code of any contract. That they plan to approve and to ensure that the “approve” function is implemented in a secure manner.
Lack of Updater Contract
In absence of an updater contract, a malicious party can exploit a vulnerability in the smart contract and replace it with a malicious version.
To prevent attack, smart contract developers can follow these best practices:
Implement proper access control: The contract’s update mechanism should only be accessible to authorized parties. Such as the contract owner or a designated administrator. Access control can be implemented using access control mechanisms like Role-Based Access Control (RBAC) or Access Control Lists (ACLs).
Use a separate updater contract: Instead of directly updating the contract’s code. A separate updater contract can be used to make updates. This allows for better control over the update process and reduces the risk of malicious code being introduced.
Replay Attacks
A replay attack is one of the most common types of attacks and involves an attacker replaying a transaction multiple times. This exploits the immutable nature of smart contracts. Allowing the attacker to drain funds from unsuspecting users. To defend against this type of attack, developers should use nonce values when sending a transaction. This ensures that the transaction can only be broadcast once and not replayed. For example, Solidity code to implement a nonce might look like this:
uint nonce = 0;
function sendTransaction(uint amount) {
require(nonce < 2**63 - 1);
nonce++;
// Send transaction with amount and nonce
}
Front Running
Front running is another type of attack that takes advantage of the speed of the blockchain to get ahead of other users. When a user places a transaction, a malicious actor notices it before it is added to the blockchain and quickly places their own transaction to get ahead of the original user. To protect against this type of attack, developers should use a decentralized exchange. That makes it harder for malicious actors to observe transactions. For example, a decentralized exchange might use the 0x protocol to prevent front-running:
pragma solidity ^0.8.0;
import "0x.sol";
contract Exchange {
using 0x for *;
// Exchange code
}
Private Key Theft
Private key theft is another type of attack that is becoming more prevalent in the DeFi space. Hackers can use a variety of methods to steal user keys, such as phishing and social engineering. To counteract this, developers should use secure wallets and store their private keys in a secure location.
Race Condition Vulnerability
A race condition vulnerability occurs when a smart contract allows multiple transactions to be processed at the same time, resulting in unexpected behavior. To prevent this type of attack, developers need to make sure their smart contracts are carefully written and tested to prevent race conditions from occurring.
To avoid these and other potential security threats, it is crucial that smart contract developers follow industry-standard security best practices, such as:
- Regular code reviews and testing
- Proper use of secure libraries
- Implementation of access controls
- Adequate gas usage for contract operations
However, even with the best intentions, mistakes can happen. That’s where Novvr and Scrutify come in. Novvr is a leading smart contract auditing firm that provides comprehensive security assessments for DeFi projects. Our team of experts uses the latest tools and techniques to identify vulnerabilities and provide actionable recommendations for improvement. Visit Smart Contract Audit to learn more about our process and how we can help secure your smart contracts.
Conclusion
In conclusion, the DeFi space is still in its infancy, and security threats will continue to evolve. It’s essential that smart contract developers stay vigilant and implement best practices to ensure the safety of funds in their contracts. Novvr and Scrutify are here to help. Don’t let the dark side of DeFi catch you by surprise – take action today. Schedule a Free Consultation.