🇰🇷 한글 설명
Payy의 Ethereum L2 zk-rollup 시스템에서 agg_agg 함수가 메시지 스트림의 잔여 공간을 검증하지 않는 결함이 발견되었습니다. 등록된 증명자(Prover)가 유효한 블록 증명에 허위 소각 메시지를 삽입하여 롤업 계약으로부터 USDC를 탈취할 수 있는 심각한 회로 건전성 오류입니다.
💡 쉬운 설명
시스템이 데이터의 끝부분이 비어있는지 확인하지 않는 허점을 이용해, 가짜 거래 내역을 끼워 넣어 돈을 빼가는 공격입니다. 공격자는 정식 증명자로 등록된 후 기존 블록에 가짜 메시지를 덧붙여 자금을 탈취할 수 있습니다.
🇺🇸 원문 (English)
Payy is an Ethereum L2 zk-rollup for privacy preserving and regulatory compliant transactions. Prior to version 1.3.0, agg_agg forwards the compacted message stream from its inner proofs into a public messages: [Field; 1000] array, but it never checks that the unused tail of the outer array is zero. A registered prover can build a valid agg_final proof for an approved rollup block while inserting an extra burn message after the real messages. RollupV1.verifyRollup() then parses that public input as a normal burn and transfers USDC from the rollup contract to the attacker. This is a severe circuit soundness failure: the proof system accepts a public statement whose messages array is not fully derived from the verified inner proofs. On the current deployment, verifyRollup() is restricted to the existing allowlisted prover, so a fresh public caller cannot submit the invalid proof directly. That gate limits who can reach L1 today; it does not make the circuit statement sound. The issue becomes permissionless under the prover model described in the Payy whitepaper. Section 3.3.2 states: "To join as a prover, the prover is required to submit a small stake", and Section 3.3.1 states that if a prover fails to submit, "other nodes can submit the block proof instead." In that model, an attacker only needs to become a registered prover and use public validator approval data for an already approved block. This issue has been patched in version 1.3.0.
📍 영향받는 시스템
Payy zk-rollup version 1.3.0 미만 버전
⚔️ 공격 벡터
등록된 증명자가 유효한 블록 증명 내에 허위 소각(burn) 메시지를 삽입하여 전송
🛡️ 대응 방법
Payy 시스템을 버전 1.3.0 이상으로 업데이트하여 메시지 배열 검증 로직 적용
🔧 검증 코드
출력 결과에 1.3.0 이상의 버전 번호가 포함되어 있으면 안전하며, 그렇지 않으면 취약한 상태입니다.
curl -s https://api.payy.network/version | grep -E '1.3.0'