From caadba8a0a512669cbe33c373a8140315e29c631 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 23 May 2025 05:11:29 +0200 Subject: [PATCH 1/3] contract extcodesize loop for 7907 --- packages/evm/test/contractSpammer.spec.ts | 78 +++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 packages/evm/test/contractSpammer.spec.ts diff --git a/packages/evm/test/contractSpammer.spec.ts b/packages/evm/test/contractSpammer.spec.ts new file mode 100644 index 0000000000..b90b3f8da1 --- /dev/null +++ b/packages/evm/test/contractSpammer.spec.ts @@ -0,0 +1,78 @@ +import { Common, Hardfork, Mainnet, createCommonFromGethGenesis } from '@ethereumjs/common' +import { eip4844GethGenesis } from '@ethereumjs/testdata' +import { + Account, + Address, + MAX_UINT64, + bytesToBigInt, + bytesToHex, + bytesToUnprefixedHex, + concatBytes, + createAddressFromPrivateKey, + createAddressFromString, + createZeroAddress, + hexToBytes, + intToBytes, + padToEven, + setLengthLeft, + setLengthRight, + unpadBytes, +} from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { assert, describe, it } from 'vitest' + +import { EVMError } from '../src/errors.ts' +import { defaultBlock } from '../src/evm.ts' +import { createEVM } from '../src/index.ts' + +import type { EVMRunCallOpts } from '../src/types.ts' + +// Non-protected Create2Address generator. Does not check if Uint8Arrays have the right padding. +function create2address(sourceAddress: Address, codeHash: Uint8Array, salt: Uint8Array): Address { + const rlp_proc_bytes = hexToBytes('0xff') + const hashBytes = concatBytes(rlp_proc_bytes, sourceAddress.bytes, salt, codeHash) + return new Address(keccak256(hashBytes).slice(12)) +} + +describe('RunCall tests', () => { + it('Create where FROM account nonce is 0', async () => { + const common = new Common({ chain: Mainnet, hardfork: Hardfork.Prague }) + const evm = await createEVM({ common }) + + const deployMentSizeHex = `62${bytesToUnprefixedHex(setLengthLeft(hexToBytes('0x6000'), 3))}` + const deployContractCode = hexToBytes(`0x305F52${deployMentSizeHex}5FF3`) + + const depSize = deployContractCode.length + const pushCodeSize = bytesToUnprefixedHex(new Uint8Array([0x60, depSize])) + + const deploymentCodeMSTORE = `7F${bytesToUnprefixedHex(setLengthRight(deployContractCode, 32))}5F52` + + // calldataload [0] is salt + const code = hexToBytes( + `0x00${deploymentCodeMSTORE}${'5F355B'}${'60010180'}${pushCodeSize}5F5F${'F5602557'}`, + ) + + const res = await evm.runCall({ to: undefined, gasLimit: BigInt(30_000_000), data: code }) + + const addr = res.createdAddress!.bytes + const deploymentHash = keccak256(deployContractCode) + + // CREATE2 layout for the hasher + // [0-0] 0xff + // [1-21] address [20bytes] + // [22-54] salt [32 bytes] + // [55-85] codeHash [32 bytes] + + const attackCode = `0x7FFF${bytesToUnprefixedHex(setLengthRight(addr, 31))}5F527F${bytesToUnprefixedHex(deploymentHash)}6037525F5B6001018060165260555F203B5060480056` + + const ctrAddress = createAddressFromString('0x' + '20'.repeat(20)) + await evm.stateManager.putCode(ctrAddress, hexToBytes(attackCode)) + + evm.events.on('step', (e) => { + console.log(e.opcode.name, e.stack) + }) + + const res2 = await evm.runCall({ to: ctrAddress, gasLimit: BigInt(30_000_000), data: code }) + console.log(res2) + }, 10000000) +}) From ae5ffa15a6bc3b98db4b5635c469a1941224095f Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 23 May 2025 05:34:04 +0200 Subject: [PATCH 2/3] update contract to retrieve correct addrs --- packages/evm/test/contractSpammer.spec.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/evm/test/contractSpammer.spec.ts b/packages/evm/test/contractSpammer.spec.ts index b90b3f8da1..bf73f61610 100644 --- a/packages/evm/test/contractSpammer.spec.ts +++ b/packages/evm/test/contractSpammer.spec.ts @@ -49,9 +49,11 @@ describe('RunCall tests', () => { // calldataload [0] is salt const code = hexToBytes( - `0x00${deploymentCodeMSTORE}${'5F355B'}${'60010180'}${pushCodeSize}5F5F${'F5602557'}`, + `0x${deploymentCodeMSTORE}${'5F355B'}${'60010180'}${pushCodeSize}5F5F${'F5602557'}`, ) + // evm.events.on('step', (e) => {console.log(e.opcode.name)}) + const res = await evm.runCall({ to: undefined, gasLimit: BigInt(30_000_000), data: code }) const addr = res.createdAddress!.bytes @@ -63,16 +65,11 @@ describe('RunCall tests', () => { // [22-54] salt [32 bytes] // [55-85] codeHash [32 bytes] - const attackCode = `0x7FFF${bytesToUnprefixedHex(setLengthRight(addr, 31))}5F527F${bytesToUnprefixedHex(deploymentHash)}6037525F5B6001018060165260555F203B5060480056` + const attackCode = `0x7FFF${bytesToUnprefixedHex(setLengthRight(addr, 31))}5F527F${bytesToUnprefixedHex(deploymentHash)}6035525F5B6001018060155260555F203B50604856` const ctrAddress = createAddressFromString('0x' + '20'.repeat(20)) await evm.stateManager.putCode(ctrAddress, hexToBytes(attackCode)) - evm.events.on('step', (e) => { - console.log(e.opcode.name, e.stack) - }) - const res2 = await evm.runCall({ to: ctrAddress, gasLimit: BigInt(30_000_000), data: code }) - console.log(res2) }, 10000000) }) From 6d849e29bf73612bcec7aba70b6d6d3db1ae4640 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 23 May 2025 06:20:25 +0200 Subject: [PATCH 3/3] add extcodecopy attack contract --- packages/evm/test/contractSpammer.spec.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/evm/test/contractSpammer.spec.ts b/packages/evm/test/contractSpammer.spec.ts index bf73f61610..6cbc4bd4e8 100644 --- a/packages/evm/test/contractSpammer.spec.ts +++ b/packages/evm/test/contractSpammer.spec.ts @@ -56,6 +56,8 @@ describe('RunCall tests', () => { const res = await evm.runCall({ to: undefined, gasLimit: BigInt(30_000_000), data: code }) + console.log(res) + const addr = res.createdAddress!.bytes const deploymentHash = keccak256(deployContractCode) @@ -65,11 +67,26 @@ describe('RunCall tests', () => { // [22-54] salt [32 bytes] // [55-85] codeHash [32 bytes] - const attackCode = `0x7FFF${bytesToUnprefixedHex(setLengthRight(addr, 31))}5F527F${bytesToUnprefixedHex(deploymentHash)}6035525F5B6001018060155260555F203B50604856` + const attackCodeEXTCODESIZE = `0x7FFF${bytesToUnprefixedHex(setLengthRight(addr, 31))}5F527F${bytesToUnprefixedHex(deploymentHash)}6035525F5B6001018060155260555F203B50604856` const ctrAddress = createAddressFromString('0x' + '20'.repeat(20)) - await evm.stateManager.putCode(ctrAddress, hexToBytes(attackCode)) + await evm.stateManager.putCode(ctrAddress, hexToBytes(attackCodeEXTCODESIZE)) + + const res2 = await evm.runCall({ + to: ctrAddress, + gasLimit: BigInt(30_000_000 - 21_000), + data: code, + }) + + const attackCodeEXTCODECOPY = `0x7FFF${bytesToUnprefixedHex(setLengthRight(addr, 31))}5F527F${bytesToUnprefixedHex(deploymentHash)}6035525F5B6001018060155260015F605F60555F203C604856` + + const ctrAddress2 = createAddressFromString('0x' + '40'.repeat(20)) + await evm.stateManager.putCode(ctrAddress2, hexToBytes(attackCodeEXTCODECOPY)) - const res2 = await evm.runCall({ to: ctrAddress, gasLimit: BigInt(30_000_000), data: code }) + const res3 = await evm.runCall({ + to: ctrAddress2, + gasLimit: BigInt(30_000_000 - 21_000), + data: code, + }) }, 10000000) })