Bauer
medium
Attacker can steal funds via reorg attack if bid is accepted within a few blocks of being created
When creating a bid for a loan without collateral, the protocol generates bid id in a cumulative manner and store bid information into the mapping. An attacker can abuse this to steal funds via reorg. Assume borrower create a bid for a loan without collateral and accepted by a lender in the next block. This now allows an attacker to steal the funds via a reorg attack.They would maliciously insert their own transaction which they would use to create a bid with their own malicious parameters. This id is the same as the id created by the borrower before the reorg attack. Next, lender accept the bid and transfer tokens to malicious borrower.
function _submitBid(
address _lendingToken,
uint256 _marketplaceId,
uint256 _principal,
uint32 _duration,
uint16 _APR,
string calldata _metadataURI,
address _receiver
) internal virtual returns (uint256 bidId_) {
address sender = _msgSenderForMarket(_marketplaceId);
(bool isVerified, ) = marketRegistry.isVerifiedBorrower(
_marketplaceId,
sender
);
require(isVerified, "Not verified borrower");
require(
!marketRegistry.isMarketClosed(_marketplaceId),
"Market is closed"
);
// Set response bid ID.
bidId_ = bidId;
// Create and store our bid into the mapping
Bid storage bid = bids[bidId];
bid.borrower = sender;
bid.receiver = _receiver != address(0) ? _receiver : bid.borrower;
bid.marketplaceId = _marketplaceId;
bid.loanDetails.lendingToken = ERC20(_lendingToken);
bid.loanDetails.principal = _principal;
bid.loanDetails.loanDuration = _duration;
bid.loanDetails.timestamp = uint32(block.timestamp);
// Set payment cycle type based on market setting (custom or monthly)
(bid.terms.paymentCycle, bidPaymentCycleType[bidId]) = marketRegistry
.getPaymentCycle(_marketplaceId);
bid.terms.APR = _APR;
bidDefaultDuration[bidId] = marketRegistry.getPaymentDefaultDuration(
_marketplaceId
);
bidExpirationTime[bidId] = marketRegistry.getBidExpirationTime(
_marketplaceId
);
bid.paymentType = marketRegistry.getPaymentType(_marketplaceId);
bid.terms.paymentCycleAmount = V2Calculations
.calculatePaymentCycleAmount(
bid.paymentType,
bidPaymentCycleType[bidId],
_principal,
_duration,
bid.terms.paymentCycle,
_APR
);
uris[bidId] = _metadataURI;
bid.state = BidState.PENDING;
emit SubmittedBid(
bidId,
bid.borrower,
bid.receiver,
keccak256(abi.encodePacked(_metadataURI))
);
// Store bid inside borrower bids mapping
borrowerBids[bid.borrower].push(bidId);
// Increment bid id counter
bidId++;
}
Attacker can steal funds via reorg.
Manual Review