sherlock-audit / 2024-05-aleo-judging

0 stars 0 forks source link

haxatron - Using `self.signer` in `bond_validator` can allow external malicious programs to drain the signers microcredits #5

Closed sherlock-admin4 closed 1 week ago

sherlock-admin4 commented 2 weeks ago

haxatron

High

Using self.signer in bond_validator can allow external malicious programs to drain the signers microcredits

Summary

Using self.signer in bond_validator can allow external malicious programs to drain the signers microcredits

Vulnerability Detail

self.signer is equivalent to tx.origin, where the self.signer is the originator of the transaction.

This creates a huge risk when used in bond_validator because a malicious Aleo program in the call flow can make an external call to bond_validator in credits.aleo which can cause an eligible validator with 10M total delegated credits that originated the transaction to bond without their consent.

https://github.com/sherlock-audit/2024-05-aleo/blob/main/snarkVM/synthesizer/program/src/resources/credits.aleo#L159-L182

// The `transfer_public_as_signer` function sends the specified amount
// from the signer's `account` to the receiver's `account`.
function bond_validator:
    // Input the withdrawal address.
    input r0 as address.public;
    // Input the amount of microcredits to bond.
    input r1 as u64.public;
    // Input the commission percentage.
    input r2 as u8.public;

    // Ensure the withdrawal address is not the validator address.
    assert.neq self.signer r0;

    // Determine if the amount is at least 1 credit.
    gte r1 1_000_000u64 into r3;
    // Enforce the amount is at least 1 credit.
    assert.eq r3 true;

    // Ensure the commission percentage does not exceed 100%.
    gt r2 100u8 into r4;
    assert.neq r4 true;

    // Bond the specified amount of microcredits to the specified validator.
    async bond_validator self.signer r0 r1 r2 into r5;
    // Output the finalize future.
    output r5 as credits.aleo/bond_validator.future;

Impact

An attack path to fully drain all the credits is as follows:

  1. Victim first calls a malicious Aleo program in the call flow, which first delegates 10M credits to the user.
  2. The Aleo program calls bond_validator with an attacker-controlled address as the withdrawal address, all the microcredits of the victim and an arbitrary commission percentage
  3. Then the attacker-controlled withdrawal address will call unbond_public on the victim
  4. Once the unbonding process is done (after 360 blocks passed), the attacker-controlled withdrawal address can then claim back the victim's microcredits, thus having fully drained them.

Code Snippet

https://github.com/sherlock-audit/2024-05-aleo/blob/main/snarkVM/synthesizer/program/src/resources/credits.aleo#L159-L182

Tool used

Manual Review

Recommendation

Use self.caller which is equivalent to msg.sender

Duplicate of #15