Attackathon _ Fuel Network 33451 - [Smart Contract - Medium] Incorrect code size estimation can bypa
Last updated
Was this helpful?
Last updated
Was this helpful?
Submitted on Sat Jul 20 2024 18:01:41 GMT-0400 (Atlantic Standard Time) by @Solosync6 for
Report ID: #33451
Report type: Smart Contract
Report severity: Medium
Target: https://github.com/FuelLabs/sway/tree/v0.61.2
Impacts:
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
The call_frames::code_size()
function in the standard library incorrectly returns a memory address instead of the actual code size.
This vulnerability can bypass security checks in contracts that verify contract code size, potentially resulting in unauthorized access and direct theft of user funds.
The code_size()
function is intended to return the size of the code in the current call frame. Current in-line documentation clearly suggests that function is expected to return code size of current call frame in bytes and NOT its memory location.
Code snipped below:
This makes me conclude that code_size is implemented incorrectly by dev team. The current implementation erroneously returns the memory address where the code size is stored, rather than the actual size value. This discrepancy can cause critical security issues in contracts that use this function for access control or other security-critical operations.
Consider a DEX built on Fuel that implements a security feature to protect against malicious contracts by checking their code size. The DEX assumes that very small contracts ( proxy contracts) or extremely large contracts might be suspicious.
Here is a sample code where I inserted a require condition based on code size of calling contract:
An attacker could exploit this vulnerability by:
Creating a malicious contract with a very small code size that would normally be rejected.
Bypassing the size check because code_size()
returns a memory address that likely falls within the "safe" range.
Gaining unauthorized access to perform swaps, potentially draining liquidity pools or performing unauthorized trades.
Consistently exploiting this vulnerability, as the returned "size" (actually a memory address) is likely to be consistent across calls.
This could result in direct theft of user funds locked in liquidity pools or actively being traded
https://github.com/FuelLabs/sway/blob/e1b1c2bee73e0ba825e07736cefa6c0abd079595/sway-lib-std/src/call_frames.sw#L68
Copy the following in call_frames.sw and run the following command forc test --logs --filter-exact test_code_size
Note:
I mocked the code_size function by passing the frame pointer as input. (code_size_mock)
I wrote a corrected function code_size_mock_corrected
to show correction
I was unsure if this is by design or if its a bug, until I looked at the other values also part of , namely, first param and second param. Although they are byte[8], they are read as u64 inside the code and their implementation clearly reads the value at the memory offset location (instead of returning the location itself)