CVE-2023-28431 (published 2023-03-22T21:15Z)

Frontier is an Ethereum compatibility layer for Substrate. Frontier's `modexp` precompile uses `num-bigint` crate under the hood. In the implementation prior to pull request 1017, the cases for modulus being even and modulus being odd are treated separately. Odd modulus uses the fast Montgomery multiplication, and even modulus uses the slow plain power algorithm. This gas cost discrepancy was not accounted for in the `modexp` precompile, leading to possible denial of service attacks.\n\nNo fixes for `num-bigint` are currently available, and thus this issue is fixed in the short term by raising the gas costs for even modulus, and in the long term fixing it in `num-bigint` or switching to another modexp implementation. The short-term fix for Frontier is deployed at pull request 1017. There are no known workarounds aside from applying the fix.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-682
cwe_name: Incorrect Calculation
description: The product performs a calculation that generates incorrect or unintended results that are later used in security-critical decisions or resource management.
extended_description: When product performs a security-critical calculation incorrectly, it might lead to incorrect resource allocations, incorrect privilege assignments, or failed comparisons among other things. Many of the direct results of an incorrect calculation can lead to even larger problems such as failed protection mechanisms or even arbitrary code execution.
         

Fix information:
repo_url: https://github.com/paritytech/frontier
commit_details: repo_url: https://github.com/paritytech/frontier
author: Wei Tang
author_date: 2023-03-15 14:05:22+01:00
msg: Increase modexp gas cost when mod is even (#1017)

* Increase modexp gas cost when mod is even

* Fix tests
repository: repo_url: https://github.com/paritytech/frontier
repo_name: polkadot-evm/frontier
description: Ethereum compatibility layer for Substrate.
repo_language: Rust
owner: paritytech
file_changes: filename: lib.rs
change_type: ModificationType.MODIFY
diff: @@ -23,7 +23,7 @@ extern crate alloc;
 use alloc::{vec, vec::Vec};
 use core::cmp::max;
 
-use num::{BigUint, FromPrimitive, One, ToPrimitive, Zero};
+use num::{BigUint, FromPrimitive, Integer, One, ToPrimitive, Zero};
 
 use fp_evm::{
 	ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput,
@@ -41,6 +41,7 @@ fn calculate_gas_cost(
 	mod_length: u64,
 	exponent: &BigUint,
 	exponent_bytes: &[u8],
+	mod_is_even: bool,
 ) -> u64 {
 	fn calculate_multiplication_complexity(base_length: u64, mod_length: u64) -> u64 {
 		let max_length = max(base_length, mod_length);
@@ -91,6 +92,7 @@ fn calculate_gas_cost(
 		MIN_GAS_COST,
 		multiplication_complexity * iteration_count / 3,
 	)
+	.saturating_mul(if mod_is_even { 20 } else { 1 })
 }
 
 /// Copy bytes from input to target.
@@ -196,7 +198,13 @@ impl Precompile for Modexp {
 			let modulus = BigUint::from_bytes_be(&mod_buf);
 
 			// do our gas accounting
-			let gas_cost = calculate_gas_cost(base_len as u64, mod_len as u64, &exponent, &exp_buf);
+			let gas_cost = calculate_gas_cost(
+				base_len as u64,
+				mod_len as u64,
+				&exponent,
+				&exp_buf,
+				modulus.is_even(),
+			);
 
 			handle.record_cost(gas_cost)?;
 
@@ -510,6 +518,6 @@ mod tests {
 
 		let _ = Modexp::execute(&mut handle).expect("Modexp::execute() returned error");
 
-		assert_eq!(handle.gas_used, 7104); // gas used when ran in geth
+		assert_eq!(handle.gas_used, 7104 * 20); // gas used when ran in geth (x20)
 	}
 }

programming_language: Rust
filename: shell.nix
change_type: ModificationType.MODIFY
diff: @@ -2,7 +2,7 @@ let
   mozillaOverlay =
     import (builtins.fetchGit {
       url = "https://github.com/mozilla/nixpkgs-mozilla.git";
-      rev = "4a07484cf0e49047f82d83fd119acffbad3b235f";
+      rev = "78e723925daf5c9e8d0a1837ec27059e61649cb6";
     });
   nixpkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
   rust-nightly = with nixpkgs; ((rustChannelOf { date = "2022-11-16"; channel = "nightly"; }).rust.override {

programming_language: JavaScript