How Was NGFS Token Exploited?

TL;DR

On April 25, 2024, FENGSHOU or the NGFS token, was exploited on the BNB chain due to a smart contract vulnerability, which resulted in a loss of assets worth approximately $191,000.

Introduction to NGFS Token

FENGSHOU or NGFS is a token on the BNB chain.

Vulnerability Assessment

The root cause of the exploit is a lack of stringent access control.

Steps

Step 1:

We attempt to analyze the attack transaction executed by the exploiter.

Step 2:

It can be seen on the vulnerable NGFSToken contract that the delegateCallReserves function was set to a public visibility specifier and lacked proper access control, allowing for an arbitrary configuration of the _uniswapV2Proxy variable.

function delegateCallReserves() public {
  require(!uniswapV2Dele, "ERC20: delegateCall launch");

  _uniswapV2Proxy = _msgSender();
  uniswapV2Dele = !uniswapV2Dele;
}

Step 3:

With this privilege in hand, the attacker was able to set the attack contract as a UniSwapV2 proxy via invoking a call to the setProxySync function. Notably, the attack was carried out roughly 1.5 hours after the deployment of the NGFS token contract.

function setProxySync(address _addr) external {
  require(_addr != ZERO, "ERC20: library to the zero address");
  require(_addr != DEAD, "ERC20: library to the dead address");
  require(msg.sender == _uniswapV2Proxy, "ERC20: uniswapPrivileges");

  _uniswapV2Library = IPancakeLibrary(_addr);
  _isExcludedFromFee[_addr] = true;
}

Step 4:

As a result, the attacker is able to manipulate the _uniswapV2Library variable to set an arbitrary high balance for their attack contract by invoking a call to the reserveMultiSync function, and then swap tokens for the underlying tokens.

function reserveMultiSync(address syncAddr, uint256 syncAmount) public {
  require(_msgSender() == address(_uniswapV2Library), "ERC20: uniswapPrivileges");
  require(syncAddr != address(0), "ERC20: multiSync address is zero");
  require(syncAmount > 0, "ERC20: multiSync amount equal to zero");
  _balances[syncAddr] = _balances[syncAddr].air(syncAmount);
  _isExcludedFromFee[syncAddr] = true;
}

Solution

To prevent the exploit seen in the NGFS token incident, several robust security measures can be implemented to safeguard smart contracts against unauthorized access and manipulation. If the Uniswap proxy variables are intended to be set once and not modified, they should be configured in the contract’s constructor and declared as immutable. This design pattern ensures that the value is written only once during contract creation and cannot be altered thereafter, providing a high level of security against unauthorized changes.

Integrating access control mechanisms is also crucial. The use of an onlyOwner modifier, or similar access control modifiers, should be applied to sensitive functions to ensure that only authorized addresses can execute them. This can be managed through OpenZeppelin’s Ownable contract, which provides a robust ownership control mechanism. These modifiers ensure that only the designated owner of the contract can call critical functions, thereby preventing unauthorized users from reassigning critical components like the UniSwapV2 proxy.

Furthermore, conducting comprehensive security audits and thorough testing regimes, including both static analysis and dynamic testing, is essential to identifying vulnerabilities before deployment. Utilizing automated tools along with expert manual review will help uncover issues that might be overlooked by one approach alone. Additionally, increasing the rigor of validation checks within functions that handle critical logic is advisable.

This article was originally published by Pukar Acharya elsewhere.




    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • Unlocking the Power of Uniswap V4 Hooks
  • Preprocessing Unstructured Data for LLM Applications
  • Fine-Tuning Large Language Models
  • Guide to LangChain for LLM Development
  • The Art of ChatGPT Prompt Engineering