sherlock-2023-08-cooler-h01
[H-01] Can steal gOhm by calling Clearinghouse.claimDefaulted on loans not made by Clearinghouse
Summary
Clearinghouse.claimDefaulted() 는 모든 대출이 Clearinghouse 컨트랙트에서 시작된 것으로 가정한다. 하지만 실제로 그러한지 확인하지 않는다. 공격자가 Clearinghouse 에서 만든 대출과 외부에서 만든 대출을 혼합해 호출하면 Clearinghouse 시스템 밖의 대출을 이용해 키퍼 리워드를 받아낼 수 있고, 이로써 담보 토큰을 훔칠 수 있다.
Keyword
theft, lending protocol, lack of input validation
Vulnerability
Clearinghouse.claimDefaulted() 함수는 채무 불이행 상태의, Clearinghouse 에게 받은 대출을 신고하는 기능이다. 신고자에게는 최대 0.1 gOHM 또는 청산하여 받은 담보 토큰의 5%를 리워드로 준다.
이 때 Clearinghouse.claimDefaulted() 는 파라미터로 넘어온 모든 대출이 Clearinghouse 컨트랙트에게 받은 대출이라고 가정하고 이를 확인하지 않는다. Clearinghouse의 대출이 아닌 대출을 청산한다면 청산된 담보 토큰은 Clearinghouse 컨트랙트에게 가지 않고, 실제 대여자에게 간다. 하지만 여전히 신고자에게는 리워드가 주어지며, 리워드는 청산된 담보가 아니라 Clearinghouse 소유의 토큰에서 나오게 된다.
다음은 claimDefaulted 함수이다. Cooler가 팩토리에서 생성되었는지만 확인하며, 대여자가 Clearinghouse인지는 확인하지 않는다.
function claimDefaulted(address[] calldata coolers_, uint256[] calldata loans_) external {
uint256 loans = loans_.length;
@> if (loans != coolers_.length) revert LengthDiscrepancy();
uint256 totalDebt;
uint256 totalInterest;
uint256 totalCollateral;
uint256 keeperRewards;
for (uint256 i=0; i < loans;) {
// Validate that cooler was deployed by the trusted factory.
@> if (!factory.created(coolers_[i])) revert OnlyFromFactory();
// Claim defaults and update cached metrics.
(uint256 debt, uint256 collateral, uint256 elapsed) = Cooler(coolers_[i]).claimDefaulted(loans_[i]);즉, 공격자가 자신의 계정을 이용해 P2P 대출을 하고, 채무 불이행 상태로 만든 후 Clearinghouse.claimDefaulted() 를 호출하면 Clearinghouse 에게서 리워드 토큰을 탈취할 수 있다.
- Alice가 Cooler를 생성한다. Clearinghouse를 거치지 않고
requestLoan를 직접 호출하여 2 gOHM을 담보로 0.000001 DAI를 대출해달라고 요청한다.(P2P이므로 비율을 마음대로 설정 가능) 각 대출마다clearLoan을 호출하여 0.000001 DAI를 자신에게 대출한다. - 일주일 뒤, Bob이
Clearinghouse.lendToCooler를 호출하여 1 gOHM을 담보로 3000 DAI를 대출받는다. - Alice가 Alice 스스로에게 대출한 채무를 불이행하고 7일 기다린다.
- Bob이 체납한다.
- Alice가
Clearinghouse.claimDefaulted를 호출하여 자신이 한 대출과 Clearinghouse으로부터 대출받은 Bob의 대출을 모두 파라미터로 전달한다. - Alice는 각 대출에 대해 키퍼 보상을 받는다. Alice의 대출 당 최대 보상인 0.1 gOHM 만큼 받는다. Bob의 대출의 경우, Bob의 대출이 체납된 후 경과한 시간에 따라 키퍼 보상이 0~0.05 gOHM 정도 증가한다.
- 키퍼 보상은 Alice에게 전달된다. Alice는 0.9
0.95 gOHM 을 보상으로 받지만, 원래는 00.05 gOHM 을 받아야 한다. 컨트랙트는 0.050.1 gOHM 을 받지만, 원래는 0.951 gOHM 을 받아야 한다. 즉, Alice는 0.9 gOHM을 훔쳤다.
Bob을 시나리오에 포함하여 Clearinghouse에 훔쳐갈 담보 토큰을 끌어온다. 리워드 크기에 제한이 있기 때문에, 이 공격은 담보의 5%(또는 채무 당 최대 0.1 gOHM)까지만 훔칠 수 있다.
Impact
Clearinghouse의 담보 토큰을 훔칠 수 있다.
Mitigation
Clearinghouse.claimDefaulted 함수에 전달된 모든 대출이 Clearinghouse 를 통해 생성된 것인지 확인한다.
tags: bughunting, olympus dao, smart contract, solidity, lending protocol, crypto theft, lack-of-input-validation-vul, severity high