Update 2023. 3. 30.

2022년 10월 25일, 사후

strapi-blog-api-image

발견

10월 13일, ToshiDiscord#sdk-js 채널에서 예기치 않은 동작을 신고했습니다. 그는 더 이상 사용되지 않는 TypeScript SDK의 출력과 최신 JavaScript SDK의 출력을 비교하던 중 총 트랜잭션 해시가 다르게 계산되는 것을 발견했습니다. 이는... 예상치 못한 행동이었습니다. 더욱 우려스러운 것은 두 트랜잭션이 모두 네트워크에서 승인(및 확인)되고 있다는 사실입니다.

근본 원인

팀은 신속하게 두 가지 중요한 버그를 찾아냈습니다. 하나는 Catapult(Symbol 클라이언트)에서, 다른 하나는 TypeScript 및 Java SDK에서 발견되었습니다.

클라이언트

Catapult의 "후시초 2"(2019년 11월 8일) 릴리스에서, 완료 및 본딩된 집계 트랜잭션의 트랜잭션 해시 필드를 검증하기 위해 집계 트랜잭션 플러그인에 새로운 유효성 검사기가 추가되었습니다. 안타깝게도 감독 소홀로 인해 집계 트랜잭션 플러그인에 등록되지 않았으며, 따라서 호출되지 않았습니다.
manager.addStatelessValidatorHook([config](auto& builder) {
        // 다음 줄이 있어야 하지만 그렇지 않았습니다.
    builder.add(validators::CreateAggregateTransactionsHashValidator()); 

    builder.add(validators::CreateBasicAggregateCosignaturesValidator(
			config.MaxTransactionsPerAggregate,
			config.MaxCosignaturesPerAggregate)));
	if (config.EnableStrictCosignatureCheck)
		builder.add(validators::CreateStrictAggregateCosignaturesValidator());
});
출시를 서두르다 보니 Failure_Aggregate_Transactions_Hash_Mismatch 오류 코드를 트리거하는 E2E 테스트를 추가하는 것을 잊어버렸습니다. 결과적으로 심볼은 집계 트랜잭션 해시에 대한 적절한 검증 없이 출시되었습니다.
또한, 더 이상 사용되지 않는 타입스크립트 SDK, 자바 SDK, 파이썬 및 자바스크립트 SDK 간에 집계 트랜잭션의 해시 계산에 불일치가 있었습니다.
Python 및 JavaScript SDK는 집계 트랜잭션 해시를 올바르게 계산하는 반면, 올해 초 더 이상 사용되지 않는 TypeScript SDK와 Java SDK는 그렇지 않았습니다.

SDK

(더 이상 사용되지 않는) TypeScript SDK에는 계산에 두 가지 버그가 있습니다.
집계 트랜잭션은 임베디드 트랜잭션의 컨테이너라는 점을 기억하세요. 각 임베디드 트랜잭션은 8바이트 바운데이에서 시작하도록 보장됩니다. 이를 위해 필요할 때마다 트랜잭션 사이에 0으로 설정된 패드 바이트가 삽입됩니다. 임베디드 트랜잭션 해시를 계산할 때는 트랜잭션의 의미 있는 데이터만 해시하고 0 패드 바이트는 제외해야 합니다. 안타깝게도 TypeScript SDK는 해시 계산에 이러한 제로 패드 바이트를 포함하고 있었습니다. 실망스럽기는 하지만 보안이 저하되지는 않습니다.
실제 버그는 머클 해시 계산에서 발견되었습니다. 스플라이스를 잘못 사용하여 요소를 대체하는 대신 스플라이스를 삽입했습니다. 대체하려면 두 번째 매개변수가 1이어야 했습니다.
hashes.splice(i / 2, 0, this.hash([hashes[i], hashes[i + 1]]));
테스트 커버리지가 부족하고 표준 테스트 벡터를 활용하지 않았기 때문에 이 두 가지 간과 사항은 모두 눈에 띄지 않았습니다.
(더 이상 사용되지 않는) Java SDK에도 TypeScript SDK와 동일한 머클 해시 계산 버그가 있었습니다. 놀랍게도 내장된 트랜잭션 해시는 (패딩 없이) 올바르게 계산되었습니다.
이 두 SDK가 수년 동안 서로 다른 총 트랜잭션 해시를 계산해왔는데도 지금까지 아무도 눈치채지 못했다는 사실이 다소 우습습니다. 최소한 공통된 테스트 벡터를 사용했어야 합니다.
공격의 작동 방식 ### 집계 트랜잭션 해시 확인이 누락되었기 때문에 공동 서명자는 집계 트랜잭션 헤더에만 서명하게 됩니다. 따라서 유일한 제약 조건은 포함된 총 트랜잭션 크기가 헤더의 payload_size와 일치해야 한다는 것입니다.
TypeScript 및 Java SDK에서 사용하는 잘못된 머클 해시 알고리즘의 버그로 인해, 손상된 머클 해시는 트랜잭션의 하위 집합만 수정으로부터 보호합니다. 예를 들어, 3개의 트랜잭션이 있는 경우 처음 두 개의 트랜잭션만 수정으로부터 보호되고 세 번째 트랜잭션은 보호되지 않습니다. 세 번째 트랜잭션의 크기가 변하지 않는 한, 무엇이든 대체할 수 있습니다.

공격 예시: 집계 완료

모자이크를 교환하려는 앨리스, 밥, 찰리라는 세 명의 참가자가 있다고 가정해봅시다.
앨리스는 밥과 찰리에게 각각 알파 모자이크 10개를 지불하려고 합니다. 그 대가로 찰리는 100 XYM을 지불하기로 동의했습니다.
앨리스는 세 부분으로 구성된 전체 트랜잭션을 생성합니다:
  1. 앨리스가 밥에게 알파카 10개를 보냅니다.
  2. 앨리스가 찰리에게 알파카 10개를 보냅니다.
  3. 찰리가 앨리스에게 100 XYM을 보냅니다.
트랜잭션을 생성한 후 앨리스는 트랜잭션에 서명합니다. 앨리스는 이를 공동 서명자인 밥에게 전달합니다. 밥은 앨리스를 속이려는 찰리에게 이를 전달합니다.
찰리는 이 사실을 알고 세 번째 트랜잭션을 대체하여 앨리스에게 0 XYM을 대신 보냅니다. 또는 더 나쁜 경우, 앨리스를 속이기 위해 앨리스가
Translated with www.DeepL.com/Translator (free version)

News
Community
Docs
Contact