Rigor Protocol contest - 0x52's results

Community lending and instant payments for new home construction.

General Information

Platform: Code4rena

Start Date: 01/08/2022

Pot Size: $50,000 USDC

Total HM: 26

Participants: 133

Period: 5 days

Judge: Jack the Pug

Total Solo HM: 6

Id: 151

League: ETH

Rigor Protocol

Findings Distribution

Researcher Performance

Rank: 4/133

Findings: 6

Award: $2,623.57

🌟 Selected for report: 1

πŸš€ Solo Findings: 1

Findings Information

🌟 Selected for report: Lambda

Also found by: 0x1f8b, 0x52, horsefacts, vlad_bochok, wastewa

Labels

bug
duplicate
3 (High Risk)
disagree with severity
sponsor disputed
edited-by-warden
valid

Awards

514.2536 USDC - $514.25

External Links

Lines of code

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Project.sol#L791-L816

Vulnerability details

Impact

Transaction replays on unrelated functions

Proof of Concept

abi.decode has a very high level of ambiguity when decoding abi.encode data. Variable types do not follow many of the conversion rules that apply to regular setting. All abi.decode does is take the bytes given to it and unpacks it and stores is at the requested variables. This means that an encoded uint256 could be unpacked as nearly any other type of data. The output is dictated by the abi.decode call not the abi.encode call. To make matters worse, the length of the input data does not need to be exactly the same as the output for abi.decode.

event BroadcastUint256 (uint256 indexed myUint); event BroadcastAddress (address indexed myAddress); function DecodeWrongLengthAndVarType () public { uint256 first = uint256(1); uint256 second = uint256(2); uint256 third = uint256(3); bytes memory data = abi.encode(first, second, third); (address firstDecoded, uint256 secondDecoded) = abi.decode(data, (address, uint256)); emit BroadcastAddress(firstDecoded); emit BroadcastUint256(secondDecoded); }

I've included some sample code above, feel free to plug it into remix and test yourself. Let's use the above decode line as an example. On the right side of the function, abi.decode is decoding data and storing it as an address and uint256. As long as data is at least 64 bytes long (address=32 bytes, uint256=32 bytes) then it will successfully store a value for both firstDecoded and secondDecoded. In the above example data is 96 bytes, abi.decode will read the first 64 bytes, store them as firstDecoded and secondDecoded, then skip the last 32 bytes. This introduces a huge amount of data malleability, because input data does not have to match the outputs of abi.decode in either length or type, the only requirement is that the input data length is at least as long as the output. Given how loose the requirements are, data from a majority of functions in Project.sol and Community.sol can be submit interchangeably and the data will be processed by abi.decode without issue.

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Project.sol#L858

checkSignatureValidity(builder, _hash, _signature, 0);

The other point of concern is the method by which signatures are specified. The issue is that the builder is always uses the 0 index. This means that the signature will be valid for the data for every function it is submit with, due to it always being signed with the same signatureIndex.

When a transaction is mined or even added the mempool, all the input data becomes public. This would give malicious actors access to all data and signatures. It would also allow them to find and test every possible replay opportunity. It isn't just limited to project.sol because community.sol uses the same signature/data scheme and a majority of functions that use this pattern can be called by anyone. It's clear that Rigor is a very ambitious product and will continue to expand to become more feature rich. With this expansion will come an even larger number of functions and signed data, which will continually widen the opening for data replay.

This is open ended and there are very large number of potential vectors and combinations to consider. I was not able to find a specific example but given the scope I think it is worth including, so I am submitting this as medium risk.

Tools Used

I strongly recommend implementing a stricter form of permissioning. I believe to fully remove the risk of replay, a majority of functions should be permissioned to the appropriate builder/contractor/subcontractor though the use of modifiers.

#0 - zgorizzo69

2022-08-11T08:08:13Z

  • data is validated by the different parties involved so they are aware of what kind of data is sent. It would have been nice to see how exactly an abi.decode data malleability can be used to fool an actor of the transaction
  • for signatures nonce is and will be present in all data to mitigate replay attacks

#1 - jack-the-pug

2022-08-27T13:45:43Z

Duplicate of #75

Findings Information

Labels

bug
duplicate
3 (High Risk)
sponsor confirmed
valid

Awards

165.6336 USDC - $165.63

External Links

Lines of code

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L824-L859

Vulnerability details

Impact

Builder reduces their interest by half

Proof of Concept

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L685-L686

uint256 _noOfDays = (block.timestamp - _communityProject.lastTimestamp) / 86400; // 24*60*60

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L847

_communityProject.lastTimestamp = block.timestamp;

In Community.sol#returnToLender, the number of days for accumulating interest is rounded down to the nearest day. Then in Community.sol#claimInterest _communityProject.lastTimestamp is updated to block.timestamp. If a builder were to make a small repayment every 1.99 days, only 1 day worth of interest would accumulate every ~2 days. This would allow them to reduce their total paid interest by half.

This vulnerability cannot be used to avoid interest completely because if the accumulated interest == 0 then Community.sol#claimInterest won't update the timestamp.

Tools Used

Change L847 to:

_communityProject.lastTimestamp = (block.timestamp / 86400) * 86400;

#0 - horsefacts

2022-08-06T22:09:45Z

Findings Information

🌟 Selected for report: MiloTruck

Also found by: 0x52, 8olidity, Ruhum, __141345__, cccz, codexploder, cryptonue, hansfriese, sseefried

Labels

bug
duplicate
2 (Med Risk)
sponsor confirmed
valid

Awards

49.6901 USDC - $49.69

External Links

Lines of code

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/HomeFi.sol#L185-L197

Vulnerability details

Impact

All projects created while fee > 1000 can never receive funding

Proof of Concept

HomeFi.sol#replaceLenderFee allows fees > 1000 (100%). If set > 1000 all projects created after would never be able to receive funds due to lenderFee causing an underflow error in Community.sol#lendToProject.

Tools Used

Implement a max fee:

require(lenderFee >= 30);

#0 - horsefacts

2022-08-06T21:58:15Z

Duplicate of https://github.com/code-423n4/2022-08-rigor-findings/issues/400 (lender can set a greater than 100% fee that underflows)

Findings Information

🌟 Selected for report: hansfriese

Also found by: 0x52, 0xNazgul, rbserver

Labels

bug
duplicate
2 (Med Risk)
disagree with severity
sponsor acknowledged
valid

Awards

285.6964 USDC - $285.70

External Links

Lines of code

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L455-L481 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L484-L506 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L509-L552

Vulnerability details

Impact

Builder forced to pay higher interest

Proof of Concept

Contract is pausable and functions that reduce debt do not function when contract is paused. Interest continues to accrue during that period of time, forcing the builder to pay more interest because they are unable to repay while it's paused.

Tools Used

Remove whenNotPaused modifer from functions that reduce user debt.

#0 - horsefacts

2022-08-06T22:14:01Z

Findings Information

🌟 Selected for report: 0x52

Labels

bug
2 (Med Risk)
sponsor disputed
valid

Awards

1567.6074 USDC - $1,567.61

External Links

Lines of code

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L501-L506 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Project.sol#L108-L115

Vulnerability details

Impact

User is unable to revoke previously approved hash

Proof of Concept

If user reconsiders or notices something malicious about the hash after signing, they should be able to revoke the hash. For example the user approves a hash only to find out later that the hash has been spoofed and they weren't approving what they thought they were. To protect themselves the user should be able to revoke approval, otherwise it may lead to loss of funds or access.

Tools Used

Add the following function:

function revokeHash(bytes32 _hash) external virtual { approvedHashes[_msgSender()][_hash] = false; }

#0 - parv3213

2022-08-18T09:58:43Z

Duplicate of #86

#1 - parv3213

2022-09-12T06:39:56Z

Duplicate of #86

This is a separate issue and not a duplicate. I do not find it essential to revoke a hash. As off-chain signatures can never be marked as invalid, adding this feature for on-chain signatures makes no sense.

QA Report

Low Risk Findings

[L-01] HomeFi.sol implements a secondary non-EIP1967 admin address
Description

The underlying proxy for HomeFi.sol stores the admin address in the EIP1967 compliant location at:

bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)

HomeFi.sol implements a secondary non-EIP1967 admin address in L52, which is set in L113 of initialize.

Lines of Code

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/HomeFi.sol#L52

Mitigation

admin should be renamed to something else such as owner to avoid any confusion

[L-02] Proxies implement a payable fallback and receive
Description

Contracts never deal directly with eth, but implement a payable fallback and receive

Lines of Code

N/A

Mitigation

Fallback should not be payable and receive should revert.

[L-03] HomeFi.sol mints users an NFT with no function when they call createProject
Description

HomeFi.sol mints an NFT when calling createProject, but never uses any feature associated with it. It consumes a lot of gas to mint an NFT then never use it for anything. Implementing the ERC721 standard increases gas cost to deploy the contract because of the extra code required.

Lines of Code

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/HomeFi.sol#L210-L232

Mitigation

Don't mint NFTs when calling createProject and don't implement ERC721

[L-04] Current method for storing acceptable currency is limited
Description

Current method of storing acceptable currency can only ever support 3 currencies. In order to add more, a new implementation would have to be deployed. If currencies were stored in a mapping it would be easy and convenient to add new currencies without having to deploy a new contract.

Lines of Code

https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/HomeFi.sol#L253-L261

Mitigation

Store the approved currencies in a mapping(address => bool) to approve a currency set the bool for that currency address to true. To validate a currency simply check if the mapping returns true for its address.

Non-Critical Findings

[NC-01] Community.sol has no way to remove members
Description

There is no way to remove a member from a community after they've been added. It could help with bloat if there was a way to remove users.

Lines of Code

N/A

Mitigation

Add a function that allows the owner to remove users. Additionally add a nonce to addMember so the transaction data can't be replayed after the user is removed

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter