code4rena-2024-03-dittoeth-h02
[H-02] An attacker can cancel other people’s short orders
Summary
데이터를 삭제하지 않고, 기존 데이터가 여전히 유효한지 확인하지 않아 다른 유저의 숏 주문을 취소시킬 수 있다.
Keyword
logic flaw, dos
Vulnerability
숏을 NFT화 하여 이동할 때, 만약 해당 숏이 부분 매칭된 숏이라면 숏 주문을 닫는다. 이 때, 이 숏의 주인이 해당 숏 주문의 주인인지를 확인하지 않는다. 또한 NFT 화 한 뒤 숏을 닫아도 해당 NFT는 삭제되지 않는다. NFT에 등록된 숏 데이터 역시 남아있는다. 즉, 숏을 닫아도 nft.shortRecordId 와 nft.shortOrderId 데이터는 유지된다.
숏 주문은 주문이 취소 또는 완료되면 shortOrderId 가 재활용된다. 만약 숏 NFT를 만들고, 부분매칭 상태였던 nft.shortOrderId 숏주문을 닫는다면 추후 다른 유저가 nft.shortOrderId 와 동일한 ID로 숏 주문을 만들 것이다. 이후 NFT를 이동하면 여전히 nft.shortOrderId 가 해당 숏의 숏 주문이라고 인식, 숏 주문을 닫으려고 시도한다. 숏 주문의 주인을 확인하지 않으므로 남의 숏 주문을 닫아버릴 수 있다.
function transferShortRecord(address from, address to, uint40 tokenId) internal {
AppStorage storage s = appStorage();
STypes.NFT storage nft = s.nftMapping[tokenId];
address asset = s.assetMapping[nft.assetId];
STypes.ShortRecord storage short = s.shortRecords[asset][from][nft.shortRecordId];
if (short.status == SR.Closed) revert Errors.OriginalShortRecordCancelled();
if (short.ercDebt == 0) revert Errors.OriginalShortRecordRedeemed();
// @dev shortOrderId is already validated in mintNFT
if (short.status == SR.PartialFill) {
@> LibOrders.cancelShort(asset, nft.shortOrderId);
}
...
}- 공격자가 부분 매칭된 숏을 만든다
- shortRecordId = 2
- shortOrderId = 100, 숏 주문 ID 리스트: HEAD ←> HEAD ←> 100 ←> TAIL
- 부분 매칭 숏을 NFT화 한다.
- shortRecordId = 2 인 NFT가 생성된다.
- 숏 주문을 취소한다.
- 숏 주문 ID 리스트: HEAD ←> 100 ←> HEAD ←> TAIL (주문 ID 재활용 리스트로 이동)
- 숏 종료하기 (예를 들어
exitShortErcEscrowed호출한다. 민팅된 만큼 dUSD를 지불하면 담보를 돌려받고 숏 레코드를 닫을 수 있다) - 피해자가 숏 주문 생성
- 숏 주문 ID로 100 을 재활용 리스트에서 꺼내서 사용
- 숏 주문 ID 리스트: HEAD ←> HEAD ←> 100 ←> TAIL
- 공격자가 다시 부분 매칭된 숏 생성
- shortRecordId = 2, (shortRecordId 는 유저별로 할당되고, 재활용되므로 기존 삭제된 ID를 재활용 한다)
- 기존에 만들어둔 숏 NFT 를
transferFrom을 호출해 이동한다. - 기존에 만든 NFT는 기존 숏 정보를
nftMapping[tokenId]에 담고 있다. 즉, 기존 숏을 닫았어도 기존 숏 데이터가 남아있다. 즉nft.shortOrderId는 여전히 100으로 저장되어 있다. - 공격자는 shortRecordId = 2 인 숏을 새로 생성했기 때문에, 기존 NFT에 매핑된 2번 숏이 있다고 취급되어 각종 상태 확인을 우회할 수 있다. (실제로는 다른 숏이지만 ID 재활용으로 인해 정상으로 판단)
transferShortRecord에서 부분 매칭 숏 주문 100번을 닫으려고 한다. 이로 인해 피해자의 숏 주문이 닫히게 된다.
공격의 심각성을 강화하기 위해 공격자가 여러개의 숏 주문 ID에 동일한 방법으로 함정을 만들어두고 유저의 주문을 취소시킬 수 있다.
Impact
공격자는 숏 주문을 완전히 제어할 수 있으며, 주문을 검열하고 낮은 가격의 주문을 취소하여 가격을 조작할 수 있다. 리워드 지급 직전에 숏 주문을 취소시켜 다른 사람의 보상을 줄일 수 있다. (리워드는 오더북에서 일정 시간이 지난 후에 지급됨) 유저를 방해할 수 있다. (DoS)
Mitigation
transferShortRecord에서 부분 매칭된 숏 주문을 닫으려고 할 때, 실제로 해당 숏 주문의 주인인지를 확인한다. (재활용된, 다른 이의 주문이 아닌지 확인)- 숏 레코드를 삭제할 때 연동된 NFT도 소각하도록 한다.
Memo
shortOrderId의 주인을 확인하지 않았다는 점 뿐만 아니라 기존 숏의 NFT가 새로 만든(ID를 재활용한) 숏에 매핑이 되는 점도 원인이다. NFT 재활용은 따로 이슈가 있는 것으로 아는데, 재활용을 통해 남의 숏 주문을 닫는다는 활용을 하여 좀 더 심각도를 올렸다.
ID 여러개에 걸쳐 함정을 파둔다는 아이디어가 괜찮다.
tags: bughunting, dittoeth, smart contract, solidity, logic flaw, dos, nft, solo issue, severity high