Attackathon _ Fuel Network 33451 - [Smart Contract - Medium] Incorrect code size estimation can bypa
Submitted on Sat Jul 20 2024 18:01:41 GMT-0400 (Atlantic Standard Time) by @Solosync6 for Attackathon | Fuel Network
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
Description
Brief/Intro
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.
Vulnerability Details
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:
I was unsure if this is by design or if its a bug, until I looked at the other values also part of Call Frames Documentation, 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)
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.
Impact Details
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
References
https://github.com/FuelLabs/sway/blob/e1b1c2bee73e0ba825e07736cefa6c0abd079595/sway-lib-std/src/call_frames.sw#L68
Proof of concept
Proof of Concept
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
Last updated