Attackathon _ Fuel Network 32269 - [Smart Contract - High] Incorrect fuel dce optimization register
Submitted on Mon Jun 17 2024 08:24:27 GMT-0400 (Atlantic Standard Time) by @anatomist for Attackathon | Fuel Network
Report ID: #32269
Report type: Smart Contract
Report severity: High
Target: https://github.com/FuelLabs/sway/tree/7b56ec734d4a4fda550313d448f7f20dba818b59
Impacts:
Incorrect sway optimization leading to incorrect bytecode
Description
Brief/Intro
The sway compiler wrongly modeled register usage of WQAM
instruction in def_registers
, which makes the compiler emit wrong code during optimization pass. An attacker may leverage that vulnerability to manipulate pointer to point to an arbitrary address and potentially gain arbitrary write in callee memory.
Vulnerability Details
The def_registers
function and use_registers
function are used to define which registers in the argument will be written to or read from by the given instruction. This information is be used by dce
optimization to decide which instructions only write to "dead" registers and could be removed. In the extracted snippet shown below, we can see that WQAM
is incorrectly thought to modify r1
while not relying on its value, while the actual behavior using r1
as a memory pointer.
During dce
optimization pass, sway compiler eliminates any instructions that write to registers without any dependency. The incorrect WQAM
modelling creates a chance where the compiler may mistake an actual useful instruction for an useless one.
To show what this means, we look at the test case below. We write a simple script that emits the WQAM
instruction. The script is then translates into abstract instructions before going through the optimization passes. Notably, the abstract instructions before dce includes an addi $r6 $$locbase i400
instruction before WQAM
, which is responsible for setting up the pointer for WQAM
output buffer.
However, after dce optimization, this instruction is removed, since the compiler thinks that wqam
writes to $r6
, and thus the result of addi
is never used and could be considered "dead". This results in the compiled program never initializing the wqam output buffer ptr, and ends up using the left-over value within the register as the output destination.
Source Script
Abstract instructions before dce
Abstract instructions after dce
Impact Details
In the best case scenario, this would lead to unexpected failures of the script / contract due to an illegal write. It the worst case scenario, the uninitialized register and wqam
calculation result could be controllable by caller, which would lead to a 32 byte arbitrary write within the memory space of callee contract.
References
https://github.com/FuelLabs/sway/blob/7b56ec734d4a4fda550313d448f7f20dba818b59/sway-core/src/asm_lang/virtual_ops.rs#L580
https://github.com/FuelLabs/sway/blob/7b56ec734d4a4fda550313d448f7f20dba818b59/sway-core/src/asm_lang/virtual_ops.rs#L697
https://github.com/FuelLabs/sway/blob/7b56ec734d4a4fda550313d448f7f20dba818b59/sway-core/src/asm_generation/fuel/optimizations.rs#L247
Proof of concept
Proof of Concept
This minimal sway test panics. We don't further demonstrate arbitrary write since that should be obvious from the details above. We can provide a PoC later is necessary.
Last updated