Hawk High

First Flight #39
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: high
Likelihood: medium
Invalid

USDC Blacklist Compliance Mechanism Creates Unrecoverable Contract State

Description: The LevelOne contract contains a critical vulnerability where USDC compliance mechanisms can permanently disable core contract functionality. The contract's graduateAndUpgrade() function distributes USDC to all teachers and the principal using direct transfers within a single transaction. However, USDC (like many compliant stablecoins) implements an address blacklisting feature, which prevents transfers to or from certain addresses. If any recipient (teacher or principal) is blacklisted, the corresponding safeTransfer call will revert, causing the entire upgrade process to fail with no recovery mechanism.

Vulnerable Code:

using SafeERC20 for IERC20;
IERC20 usdc;
function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
// [...validation logic...]
uint256 totalTeachers = listOfTeachers.length;
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
_authorizeUpgrade(_levelTwo);
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher); // Vulnerable point 1
}
usdc.safeTransfer(principal, principalPay); // Vulnerable point 2
}

Detailed Impact Analysis: This vulnerability creates several severe consequences:

  1. Unrecoverable System State: If any teacher becomes blacklisted by USDC, the entire contract becomes frozen in its current implementation with no way to upgrade or distribute funds.

  2. Single Point of Failure: Even a single blacklisted address among potentially many teachers will brick the entire system.

  3. No Contingency Mechanism: The contract lacks any method to skip or remove blacklisted recipients after an upgrade attempt has begun.

  4. External Dependency Risk: The contract's core functionality becomes dependent on external compliance decisions made by the USDC issuer.

  5. Vector for Malicious Exploitation: A malicious actor could intentionally get themselves added as a teacher and then self-report for blacklisting to permanently disable the system.

Technical Details: USDC and many other regulated stablecoins implement a blacklisting mechanism to comply with legal requirements. When an address is blacklisted:

  1. Any transfer to or from that address will revert

  2. This reversal happens at the token contract level, not within the SafeERC20 library

  3. SafeERC20's safeTransfer will propagate this revert, causing the entire transaction to fail

This creates a situation where external factors entirely outside the control of the contract or even the principal can permanently disable core contract functionality.

Proof of Concept:

  1. A teacher's address is blacklisted in the USDC contract for compliance reasons

  2. The principal calls graduateAndUpgrade()

  3. When the loop reaches the blacklisted teacher, the usdc.safeTransfer() call reverts

  4. The entire transaction fails, preventing the upgrade

  5. No matter how many times the principal attempts the upgrade, it will always fail at the same point

Recommended Mitigation: Implement a decoupled payment system that separates token transfers from the upgrade process:

// Add state variables
mapping(address => uint256) public pendingPayments;
// Function to prepare payments without transferring tokens
function prepareGraduation() public onlyPrincipal {
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
for (uint256 n = 0; n < listOfTeachers.length; n++) {
pendingPayments[listOfTeachers[n]] = payPerTeacher;
}
pendingPayments[principal] = principalPay;
}
// Allow payments to be claimed individually
function claimPayment() public {
uint256 amount = pendingPayments[msg.sender];
require(amount > 0, "No pending payment");
pendingPayments[msg.sender] = 0;
// If this reverts, it only affects the individual claim, not the entire system
usdc.safeTransfer(msg.sender, amount);
}
// Separate function for upgrade
function upgradeImplementation(address _levelTwo) public onlyPrincipal {
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
_authorizeUpgrade(_levelTwo);
}

This pattern ensures that token compliance issues can't block the upgrade process, and blacklisted addresses only affect their own payment claims rather than the entire system.

Updates

Lead Judging Commences

yeahchibyke Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.