DatingDapp

First Flight #33
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Invalid

Front-running vulnerability in `LikeRegistry::likeUser` enables match manipulation

Summary

The LikeRegistry::likeUser function is vulnerable to front-running attacks where an attacker can intercept intended matches by observing pending transactions and submitting their own transaction with a higher gas price, allowing them to manipulate the matching system with minimal ETH commitment.

Vulnerability Details

The likeUser function in LikeRegistry.sol only enforces a minimum ETH requirement without any protection against transaction ordering manipulation:

function likeUser(address liked) external payable {
require(msg.value >= 1 ether, "Must send at least 1 ETH");
require(!likes[msg.sender][liked], "Already liked");
require(msg.sender != liked, "Cannot like yourself");
require(profileNFT.profileToToken(msg.sender) != 0, "Must have a profile NFT");
require(profileNFT.profileToToken(liked) != 0, "Liked user must have a profile NFT");
likes[msg.sender][liked] = true;
emit Liked(msg.sender, liked);
if (likes[liked][msg.sender]) {
matches[msg.sender].push(liked);
matches[liked].push(msg.sender);
emit Matched(msg.sender, liked);
matchRewards(liked, msg.sender);
}
}

Proof Of Concept

contract LikeRegistryTest is Test {
LikeRegistry registry;
SoulboundProfileNFT nft;
address alice = address(0x1);
address bob = address(0x2);
address attacker = address(0x3);
function setUp() public {
nft = new SoulboundProfileNFT();
registry = new LikeRegistry(address(nft));
vm.startPrank(alice);
nft.mintProfile("Alice", 25, "ipfs://alice");
vm.stopPrank();
vm.startPrank(bob);
nft.mintProfile("Bob", 30, "ipfs://bob");
vm.stopPrank();
vm.startPrank(attacker);
nft.mintProfile("Attacker", 28, "ipfs://attacker");
vm.stopPrank();
}
function testFrontRunningAttack() public {
uint256 initialBalance = address(attacker).balance;
vm.startPrank(bob);
vm.deal(bob, 1 ether);
registry.likeUser{value: 1 ether}(alice);
vm.stopPrank();
vm.startPrank(attacker);
vm.deal(attacker, 1 ether);
registry.likeUser{value: 1 ether}(alice);
vm.stopPrank();
vm.startPrank(alice);
vm.deal(alice, 5 ether);
registry.likeUser{value: 5 ether}(attacker);
address[] memory aliceMatches = registry.getMatches();
assertEq(aliceMatches[0], attacker, "Front-running succeeded");
vm.stopPrank();
vm.startPrank(attacker);
address[] memory attackerMatches = registry.getMatches();
assertEq(attackerMatches[0], alice, "Attacker matched with Alice");
uint256 expectedValue = ((5 ether + 1 ether) * 90) / 100;
assertTrue(expectedValue > 1 ether, "Attacker gained access to more ETH than invested");
vm.stopPrank();
}
}

Impact

High severity because:

  • Attackers can intercept legitimate matches with minimal ETH commitment

  • Users lose intended matches to attackers

  • Platform's matching mechanism can be manipulated

  • Users may lose significant ETH when matched with attackers

  • Trust in the platform's matching system is compromised

Tools Used

  • Manual review

  • Foundry testing framework

Recommendations

  1. Implement a commit-reveal scheme:

mapping(address => bytes32) public commitments;
mapping(address => uint256) public commitmentTimes;
function commitLike(bytes32 hashedLike) external {
commitments[msg.sender] = hashedLike;
commitmentTimes[msg.sender] = block.timestamp;
}
function revealLike(address liked, bytes32 salt) external payable {
require(block.timestamp >= commitmentTimes[msg.sender] + 1 hours, "Too early");
require(commitments[msg.sender] == keccak256(abi.encodePacked(liked, salt)), "Invalid reveal");
}
  1. Implement a timelock between likes:

mapping(address => uint256) public lastLikeTime;
function likeUser(address liked) external payable {
require(block.timestamp >= lastLikeTime[msg.sender] + 1 hours, "Like too soon");
lastLikeTime[msg.sender] = block.timestamp;
}
Updates

Appeal created

n0kto Lead Judge 4 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.