Prover can cheat in felt_to_bytes_little due to value underflow #118
Labels
3 (High Risk)
Assets can be stolen/lost/compromised directly
bug
Something isn't working
edited-by-warden
H-02
primary issue
Highest quality submission among a set of duplicates
🤖_03_group
AI based duplicate group recommendation
selected for report
This submission will be included/highlighted in the audit report
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
sufficient quality report
This report is of sufficient quality
Lines of code
https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/utils/bytes.cairo#L43-L138
Vulnerability details
The function
felt_to_bytes_little()
inbytes.cairo
converts a felt into an array of bytes.The prover can cheat by returning a near arbitrary string that does not correspond to the input felt whereby the spoofed output bytes and
bytes_len
bytes must fulfill some specific conditions (but, if carefully crafted, can contain almost arbitrary sequences of bytes).This issue affects the function `felt_to_bytes_little() as well as other functions that depend on it:
Those functions are used throughout the code, notably in
get_create_address()
andget_create2_address()
, which an attacker could exploit to deploy L2 smart contracts from a spoofed sender address (e.g. to steal funds from wallets that use account abstraction).Details
First, note the following loop:
We observe that:
Value can underflow if the byte returned in the last iteration is greater than the value remaining in the felt. For example, if the remaining value is 1 but the prover/hint returns 2, value will underflow into
STARKNET_PRIME - 1
. Consequently, the loop will continue running asvalue != 0
.In the following iterations of the loop, the prover can return arbitrary values, they just need to ensure that value eventually becomes 0. They can use as many iterations as required, but it is important that
bytes_len
ends up at a specific value (see 3).Finally, at the end of the function, there is a check that
bytes_len
is the minimal one possible to represent the value. The lower bound and upper bound for this check is read frompow256_table
at the offsetbytes_len
. The malicious prover must ensure that the value at this memory address (somewhere into the code segment) is lower thaninitial_value
. Any nearby position where the Cairo bytecode contains a zero works.Proof of Concept
To pass the bounds check, we need a code offset that contains the value
0
. Fortunately (from a malicious prover's perspective) there are many zero-value locations in the code segment. When we dump the memory segment with a hint we find multiple zeroes:When creating the proof-of-concept I had some problems calculating the offsets reliably, but I got it to work with a few brute-force tries. The following test worked reliably for me:
Running the test shows the prover's manipulated output vs. the expected output:
A skilled attacker who invests enough time can almost arbitrary output strings, as long as they make sure that
value
reaches zero at some point andpow256_table + bytes_len] == 0
.P.s. whether the same offset works may depend on the Cairo compiler version. I used the following test to find a valid offset:
Recommended Mitigation Steps
The easiest fix is to do range checks on
value
to prevent underflows.Assessed type
Math
The text was updated successfully, but these errors were encountered: