33520 - [BC - Insight] Inconsistent consensus issue for BlakeF precomp...

Submitted on Jul 22nd 2024 at 14:02:09 UTC by @ret2happy for Boost | Shardeum: Core

Report ID: #33520

Report type: Blockchain/DLT

Report severity: Insight

Target: https://github.com/shardeum/shardeum/tree/dev

Impacts:

  • Unintended chain split (network partition)

Description

Brief/Intro

There exists a wrong implementation in the precompile 0x9. When calculate the output involved with non-zero aligned inputs, the output and the gas result is totally wrong. This would lead to the wrong gas usage and further wrong result with the correct EVM-consensus implemented validator, resulting the network partition.

Vulnerability Details

In the precompile function of [1], it decodes the rounds and the following parameters hRaw,mRaw,tRaw using the beginning buffer of the data. However, the data buffer could have offset and when the offset is non-zero, the decoded parameter is wrong. The official definition of the Blake precompile could be found in [2].


export function precompile09(opts: PrecompileInput): ExecResult {
  ...

  const rounds = new DataView(data.subarray(0, 4).buffer).getUint32(0) // [1] wrong align for the 
  const hRaw = new DataView(data.buffer, 4, 64)
  const mRaw = new DataView(data.buffer, 68, 128)
  const tRaw = new DataView(data.buffer, 196, 16)

Take [1] for example, it decode the first 4 bytes of the data array buffer. However, the byteOffset of the data could be non-zero. This result in the wrong memory access (we read the wrong & stale memory data) when calculate the parameter. This further leads to the wrong output result and the gas result.

Hence we need to take byteOffset into consideration. Following parameter calculation is an suggested fix:

Impact Details

By loading the malicious PoC tx we provided, the contract call result is totally wrong. What's more, the gas calculation is wrong as well. This could lead to the consensus issue within the execution layer. Furthermore, if any dApp using this precompile under this wrong implementation, it could get the wrong & unexpected result, leading to the DoS or fund lock/stolen issue in that case.

References

[1] https://github.com/shardeum/shardeum/blob/c7b10c2370028f7c7cbd2a01839e50eb50faa904/src/evm_v2/precompiles/09-blake2f.ts#L184-L187

[2] https://eips.ethereum.org/EIPS/eip-152#12-rounds

Proof of Concept

PoC

I write a PoC which runs transaction on the shardeum network under the test/testCases directory:

Executing this unit test using jest test/testCases/transactionsPoC.test.ts, we would get:

We observe that the return value is 0x08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d1487c967f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b. However, the correct return value should be 0x772acbd3f30b0c3f5f53e8b836ab406f7d8d46fd4b27e2ce2ecd67dbf18c958741e2c49d1f1b1a463907a484f970c057dab9684062b82fda69e8a0057e14766f. (We will later explain how we get the correct value/ ground truth.)

By applying the suggested fix in the bug description, we get the correct execution result.

Ground truth

We use a foundry test to get the correct result since it uses ethereum mainnet as the execution backend. Following is the test file:

Install foundry and run:

You would get the return from running this code:

Also note that the 0x0 address code (i.e., 0x366000602037600080366020600060095AF1593D6000593E3D90F3) is doing the following things:

The asm code of that bytecode is:

You can also verify the result by the RPC call:

Running it you would get:

Last updated

Was this helpful?