출처: 제로 아워 테크놀로지
사건의 배경
2023-11-13 18:51:57 (UTC) OKC 토큰에 대한 공격이 체인에서 발생했으며, 해커는 결함이 있는 마이너풀을 통해 적시에 이득을 얻었습니다
해킹된 트랜잭션:
0xd85c603f71bb84437bc69b21d785f982f7630355573566fa365dbee4cd236f08
Hacking Contract 1:
0xD5d8c2fd8A743A89BC497B2F180C52d719a007B9
해킹 컨트랙트 2:
0x617432Fc98c1fFaAB62B8cB94Cef6D75ABD95598
해킹 컨트랙트 3:
0x28e7c8337373C81bAF0A4FE88ee6E33d3C23E974
공격자 주소:
0xbbcc139933D1580e7c40442E09263e90E6F1D66D
취약점 컨트랙트:
0x36016C4F0E0177861E6377f73C380c70138E13EE (MinerPool)
공격 분석
온체인 트랜잭션을 분석하여 데이터 분석을 통해 트랜잭션을 수집하고 요약합니다.
해커들은 먼저 여러 차례의 플래시 대출을 통해 공격 자금을 조달했으며, 총 2,753,399 USDT 토큰을 빌렸습니다.
이후 즉시 130,000 USDT를 전환했습니다. 토큰은 즉시 최소 단위인 USDT 토큰과 27,264 OKC 토큰으로 전환되었습니다.
팬케이크 스왑v2는 AMM(상수 함수 시장 메이커)을 사용하기 때문에 풀에 있는 두 토큰 수의 곱은 모델에서 상수입니다. 이는 다음 방정식으로 나타낼 수 있습니다:
이것은:
- x는 트레이딩 풀의 첫 번째 토큰 수입니다.
- y는 거래 풀에 있는 두 번째 토큰의 수입니다.
- k는 풀에 있는 두 토큰 수의 곱을 나타내는 상수입니다.
트레이더는 한 토큰을 다른 토큰으로 교환하고자 할 때 풀에 있는 한 토큰의 개수를 늘리고(dx), 다른 토큰의 개수를 줄여 상수를 유지합니다(dy). 이 과정에서 토큰의 상대적 가격이 변경됩니다.
상수 제품 공식은 거래 전후에 모두 적용되어야 합니다.
상수 제품 공식은 거래 전후에 모두 적용되어야 합니다.
k가 상수이므로 거래 전후에 x-y가 동일하다는 뜻입니다. 그러나 트레이더가 토큰 A의 양을 늘리고 토큰 B의 양을 줄이면(dx), 토큰 B의 가격이 상승하게 됩니다(dy).
토큰의 순간 가격은 풀에 있는 두 가지 유형의 토큰 수 비율을 계산하여 도출할 수 있습니다. 토큰 A가 x이고 토큰 B가 y인 경우, 토큰 A에 대한 토큰 B의 가격은 y/x입니다. 거래 후 토큰 B의 토큰 수는 감소하고 토큰 A의 토큰 수는 증가하므로 새로운 가격은 (y-dy)/(x+dy)가 됩니다. 분자가 감소하고 분모가 증가하면 이 비율은 작아지고, 이는 토큰 B의 가격이 상승한다는 것을 의미합니다.
대규모 트랜잭션의 경우 dx와 dy가 매우 커질 수 있으며, 이로 인해 가격 변동이 크게 발생할 수 있습니다. 이는 k를 일정하게 유지하기 위해 풀에서 많은 양의 dy를 제거하여 dx의 증가를 보상해야 하기 때문입니다. 이처럼 대규모 거래가 풀의 토큰 수에 미치는 영향이 크면 가격이 크게 변동합니다.
따라서 공격자는 대량의 라이트닝 대출 자금을 사용하여 OKC를 매수할 수 있었고, 그 결과 OKC의 양이 감소하여 OKC 토큰의 가격이 상승하여 1OKC = 0.3 USDT의 가격이 1OKC = 68.9 USDT로 증가했습니다.
그 후 공격자는 두 개의 컨트랙트 주소를 생성하고 각 주소에 0.01 OKC와 0.0001 USDT를 보냈습니다. 각 주소에 OKC와 0.0001 USDT 및 최소 단위의 OKC를 전송했습니다.
해커들은 1차 공격 컨트랙트를 사용하여 팬케이크페어_USDT_OKC 풀에 유동성 작업을 추가하여 약 225,705 LP 토큰을 획득했습니다.
그런 다음 LP 토큰은 공격자가 생성한 0x28e7c8337373C81bAF0A4FE88ee6E33d3C23E974 공격 컨트랙트로 전송되고, 취약한 컨트랙트의 processLPReward 함수가 즉시 호출되어 컨트랙트에 저장된 lpHolder 주소에 대한 프로세스를 수행합니다. 그런 다음 공격자는 즉시 취약한 컨트랙트의 processLPReward 함수를 호출하여 보상을 컨트랙트에 저장된 lpHolder 주소에 할당합니다. 공격자는 마이너풀 컨트랙트로 돈을 이체하기만 하지만, 컨트랙트는 이체를 수락하는 콜백 함수에서 processLPReward 함수를 호출합니다.
다음 다이어그램에 따르면 공격 계약은 다음과 같습니다. 0x28e7c8337373C81bAF0A4FE88ee6E33d3C23E974는 보상 컬렉션에서 77,890 OKC 토큰을 받았습니다.
이후 공격자는 공격 계약 0x28e7..E974의 LP 토큰을 전송하고 이를 소멸하여 OKC 토큰을 제거하였습니다. E974를 전송하고 이를 소멸시켜 유동성을 제거하여 1,884,223 USDT 토큰과 27,264 OKC 토큰을 획득했습니다.
그리고 다른 두 공격 컨트랙트의 모든 토큰을 메인 공격 컨트랙트로 전송했습니다
0xD5d8c2fd8A743A89BC497B2F180C52d719a007B9 , 각각 272개와 77,890개의 OKC 토큰.
해커들은 약 104,610 개의 약 104,610 OKC 토큰을 약 136,518 USDT 토큰으로 옮겼으며, 현재 해커는 총 약 2,759,918 USDT 토큰을 보유하고 있습니다.
최종적으로 해커는 라이트닝 대출의 원금과 이자를 모두 반환하고 약 6,268 USDT 토큰의 최종 잔액을 남겨 공격자의 주소로 모두 이체했습니다
. p>
0xbbcc139933D1580e7c40442E09263e90E6F1D66D.
취약점 분석
공격 분석 결과 해커의 주요 수익은 MinerPool 컨트랙트의 프로세스LPReward 함수를 통해 이루어지며, 이 함수의 로직은 lpHolder를 획득하여 포함된 LP 수에 따라 직접적으로 비례하여 보상하는 것입니다.
공격자가 생성한 세 번째 공격 컨트랙트의 실행 로직을 살펴보면 다음과 같습니다.
이러한 로직에서 addHolder 함수가 extcodesize()를 사용하여 주소의 현재 크기를 계산하여 공격자가 공격에 성공했는지 여부를 판단하는 것을 볼 수 있습니다. 그러나 공격자가 CREATE2를 통해 컨트랙트를 생성하면 컨트랙트가 초기화될 때 주소 크기가 0으로 유지되므로 공격자는 생성자에서 이 함수를 호출하여 컨트랙트 호출 제한을 우회할 수 있습니다.
이체 시점에 추가홀더 함수를 호출하여 컨트랙트 주소를 lpHolder 목록에 추가하여 OKC 보상 시점에 맞춰 보상을 수령하는 데 컨트랙트를 사용할 수 있도록 했습니다.
프로세스LPReward 함수의 코드 로직을 분석한 결과, 아래 그림에서 보상을 받기 위한 락타임은 설정되어 있지만 LP 보유시간은 확인하거나 제한하도록 설정되어 있지 않아 해커가 라이트닝 대출을 통해 대량의 임시 자금을 확보하고 이를 LP로 교환한 후 보상 수령 후 바로 소멸시키는 것을 알 수 있습니다. 보상 수령 후 즉시 소멸됩니다.
요약
요약하자면, 해커는 라이트닝 대출을 통해 대량의 USDT를 빌린 후 이를 대량의 OKC로 교환하여 OKC의 가격을 상승시켰습니다. 또한 OKC 프로젝트에서 LP 보상 지급에 대한 락아웃 요건을 설정하지 않았기 때문에 해커는 보상을 받은 직후 유동성을 인출하여 프로젝트 측에서 발행한 유동성 공급자의 보상을 획득했습니다. 해커는 OKC 프로젝트에서 보상으로 지급한 OKC 토큰을 판매했습니다. 최종 수익은 6,268 USDT였습니다.