code-423n4 / 2023-05-ajna-findings

2 stars 0 forks source link

Lack of Access Control in GrantFund Smart Contract's fundTreasury Function #418

Closed code423n4 closed 1 year ago

code423n4 commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/grants/GrantFund.sol#L58-L68 https://github.com/code-423n4/2023-05-ajna/blob/276942bc2f97488d07b887c8edceaaab7a5c3964/ajna-grants/src/grants/GrantFund.sol#L67

Vulnerability details

Impact

The fundTreasury function in the GrantFund.sol contract allows anyone to add funds to the contract's treasury without any access control, which can lead to unauthorized access to the contract's funds. The problem with this function is that it doesn't have any access control. This means that anyone, including an attacker, can call the function and transfer funds to the contract's address without any restrictions. This is a significant security issue because an attacker could potentially drain the contract's funds by repeatedly calling this function and transferring large amounts of funds to the contract's address.

The relevant code block for the fundTreasury function is as follows:

    function fundTreasury(uint256 fundingAmount_) external override {
        IERC20 token = IERC20(ajnaTokenAddress);

        // update treasury accounting
        treasury += fundingAmount_;

        emit FundTreasury(fundingAmount_, treasury);

        // transfer ajna tokens to the treasury
        token.safeTransferFrom(msg.sender, address(this), fundingAmount_);
    }

Proof of Concept

Example scenario between Alice and Bob that illustrates how the fundTreasury function could be exploited:

Alice is an attacker who discovers that the fundTreasury function in the GrantFund smart contract can be called by anyone without any access control. Alice decides to exploit it and transfer some of the contract's funds to her own address by calling the token.safeTransferFrom:

token.safeTransferFrom(msg.sender, address(this), fundingAmount_);

Alice would call the fundTreasuryfunction with her own address as the msg.sender parameter, and the amount of tokens she wants to transfer as the fundingAmount_ parameter. The contract would then transfer the specified amount of tokens to the contract's address, which in this case would be Alice's address since she is the caller. As a result, Alice would be able to transfer some of the contract's funds to her own address.

Bob is the owner of the GrantFund contract and is responsible for managing the contract's funds. Bob has a balance of 1000 ajna tokens in the contract's treasury, which is meant to be used to support grant recipients.

Alice calls the fundTreasury function multiple times and transfers 500 ajna tokens each time. As a result, the contract's treasury balance increases to 6000 ajna tokens, with 5000 of those tokens belonging to Alice.

To transfer 500 ajna tokens from the contract's treasury to her own address, Alice would call the fundTreasury function with her own address as the msg.sender parameter, and 500 as the fundingAmount_ parameter. She would repeat this step 10 times, as mentioned in the example, to transfer a total of 5000 ajna tokens to her own address.

This is an example of how Alice could call the function using the web3.js library in JavaScript:

const GrantFund = artifacts.require("GrantFund");
const AjnaToken = artifacts.require("AjnaToken");

// Get the contract instance and AjnaToken instance
const grantFund = await GrantFund.deployed();
const ajnaToken = await AjnaToken.at(await grantFund.ajnaTokenAddress());

// Call the fundTreasury function 10 times to transfer 5000 ajna tokens to Alice's address
const aliceAddress = "0x3F4fA3fBc094911B11e49bDB147ED133bD05A80F"; // replace with Alice's address
const amount = 500;
for (let i = 0; i < 10; i++) {
  await ajnaToken.approve(grantFund.address, amount, { from: aliceAddress });
  await grantFund.fundTreasury(amount, { from: aliceAddress });
}

In this example, Alice's address is assumed to be 0x3F4fA3fBc094911B11e49bDB147ED133bD05A80F. She approves the grantFund contract to spend amount of ajna tokens on her behalf by calling the approve function on the AjnaToken contract, and then calls the fundTreasury function on the grantFund contract with the amount parameter set to 500. This step is repeated 10 times to transfer a total of 5000 ajna tokens to Alice's address.

Bob realizes that something is wrong when he checks the contract's balance and sees that a large amount of tokens has been transferred to an unknown address. Bob investigates the issue and discovers that the fundTreasury function has been exploited by Alice.

As a result of Alice's actions, Bob is now facing a significant financial loss, and the intended grant recipients may not receive the support they need. This illustrates the severity of the vulnerability and the potential impact it could have on the contract and its stakeholders.

Tools Used

manual code review, vscode

Recommended Mitigation Steps

Implementing access control to restrict who can call the fundTreasury function.

Assessed type

Access Control

c4-judge commented 1 year ago

Picodes marked the issue as unsatisfactory: Invalid