Home

Awesome

Audit GPT

Fine-tuning GPT-3/4 for Smart Contract Auditing.

Running

Get an API key from OpenAI and set it to environment. Fine-tuning costs around $16.

export OPENAI_API_KEY=sk_XXXXXXXX

Files

Example

Let fine-tuned GPT audit Kairos Auction Faucet from Sherlock competition:

False Positives9
False Negatives1

Details

contract AuctionFacet is IAuctionFacet, SafeMint {
    using RayMath for Ray;
    using RayMath for uint256;
    using Erc20CheckedTransfer for IERC20;

    /// @notice buy one or multiple NFTs in liquidation

🤖️ the method buy() doesn’t check that the sender of the transaction is the winner of the auction. This allows the attacker to steal all of the funds in the contract.

    /// @param args arguments on what and how to buy
    function buy(BuyArg[] memory args) external {
        for (uint256 i = 0; i < args.length; i++) {
            useLoan(args[i]);
        }
    }

🤖️ malicious user can buy tokens for the lender multiple times, draining their stake.


    /// @notice gets the price to buy the underlying collateral of the loan
    /// @param loanId identifier of the loan
    /// @return price computed price
    function price(uint256 loanId) public view returns (uint256) {
        Loan storage loan = protocolStorage().loan[loanId];

🤖️ the method price() returns the wrong price of the collateral ETH when the loan is in the Erased state. This allows a malicious lender to buy back the collateral ETH at a low price before it is erased from their wallet.

        uint256 loanEndDate = loan.endDate;
        uint256 timeSinceLiquidable = block.timestamp - loanEndDate;

        checkLoanStatus(loanId);

        /* the decreasing factor controls the evolution of the price from its initial value to 0 (and staying at 0)

🤖️ Liquidable loans can be stuck in the endDate state, so that timeSinceLiquidable will always be greater than block.timestamp, and the borrower will not be able to pay back the loan before its end date. This can cause the borrower’s collateral to be stuck in the EndDate state forever, and can lead to a loss of principal for the lender.

        over the course of the auction duration */
        Ray decreasingFactor = timeSinceLiquidable >= loan.auction.duration
            ? ZERO
            : ONE.sub(timeSinceLiquidable.div(loan.auction.duration));

        /* the estimated value arises from the mean of the loan offer loanToValues used in the loan regarding their

🤖️ the estimated liquidable stake is wrong, which can be used to frontrun the bidders.

        share in the collateral usage. This must stay consitent even if less than the full value of the NFT has been
        used as collateral */
        uint256 estimatedValue = loan.lent.mul(ONE.div(loan.shareLent));

        /* by mutliplying the estimated price by some factor and slowly decreasing this price over time we aim to
        make sure a liquidator will buy the NFT at fair market price. */

🤖️ the liquidator may not buy the collateral token for its fair value, but instead buy it for much less than its fair value, allowing the borrower to profit from the liquidator’s mistake.

        return estimatedValue.mul(loan.auction.priceFactor).mul(decreasingFactor);
    }

    /// @notice handles buying one NFT
    /// @param arg arguments on what and how to buy
    function useLoan(BuyArg memory arg) internal {

🤖️ the user can lose their NFTs when they buy a loan.

        Loan storage loan = protocolStorage().loan[arg.loanId];

        checkLoanStatus(arg.loanId);
        uint256 toPay = price(arg.loanId);

        /* store as liquidated and paid before transfers to avoid malicious reentrency, following

🤖️ Users can take the ETH in the collateral of a loan without paying back the loan.

        checks-effects-interaction pattern */
        loan.payment.liquidated = true;
        loan.payment.paid = toPay;
        loan.assetLent.checkedTransferFrom(msg.sender, address(this), toPay);
        loan.collateral.implem.safeTransferFrom(address(this), arg.to, loan.collateral.id);

🤖️ the user’s NFT could be stuck in the contract when the user tries to liquidate the loan.

        emit Buy(arg.loanId, abi.encode(arg));
    }

    /// @notice checks that loan is liquidable, revert if not
    /// @param loanId identifier of the loan
    function checkLoanStatus(uint256 loanId) internal view {

🤖️ Liquidation can be bypassed by calling buy on a loan that has already been liquidated. This allows users to purchase a loan that they should not be able to purchase, allowing them to steal funds from the borrower.

        Loan storage loan = protocolStorage().loan[loanId];

        if (block.timestamp < loan.endDate) {
            revert CollateralIsNotLiquidableYet(loan.endDate, loanId);
        }
        if (loan.payment.paid != 0 || loan.payment.liquidated) {

🤖️ Liquidation of collateral does not update endDate, so users can take out a new loan in the old endDate, allowing them to take out multiple loans with the same collateral.

            revert LoanAlreadyRepaid(loanId);
        }
    }

🤖️ User’s loan can be maliciously reported as already repaid so that the user can’t repay the loan again. This allows users to take NFTs out of their account multiple times.