[9] SubtleCrypto RSA import OOB read from missing ASN.1 bounds check
Severity: Medium | Component: WebCore SubtleCrypto | bbec6de
Medium으로 평가한 이유는 실제로 관측되는 영향이 안정적으로 재현 가능한 renderer crash에 한정되기 때문입니다. Hardened 빌드에서는 crypto.subtle.importKey()를 통해 어느 웹 페이지에서나 매번 동일하게 SIGTRAP을 발생시킬 수 있습니다. Information disclosure에 관해서는 commit에서 뒷받침하는 근거가 없습니다. Non-hardened 빌드에서 OOB heap read로 확장될 가능성은 존재하지만, 읽힌 데이터는 CCRSACryptorImport로만 전달됩니다. 이 함수가 해당 데이터를 거부하기 때문에 info-leak 가능성은 제한적입니다.
CryptoKeyRSA::importSpki()와 CryptoKeyRSA::importPkcs8()는 이제 ASN.1 헤더 크기 계산이 완료된 후, keyData.subspan(headerSize) 호출 이전에 keyData.size() < headerSize 검사를 수행합니다.
Source/WebCore/crypto/cocoa/CryptoKeyRSAMac.cpp
if (keyData.size() < headerSize + 1)
return nullptr;
headerSize += bytesUsedToEncodedLength(keyData[headerSize]) + sizeof(InitialOctet);
+ if (keyData.size() < headerSize)
+ return nullptr;
CCRSACryptorRef ccPublicKey = nullptr;
auto dataAfterHeader = keyData.subspan(headerSize);
if (keyData.size() < headerSize + 1)
return nullptr;
headerSize += bytesUsedToEncodedLength(keyData[headerSize]);
+ if (keyData.size() < headerSize)
+ return nullptr;
CCRSACryptorRef ccPrivateKey = nullptr;
auto dataAfterHeader = keyData.subspan(headerSize);
LayoutTests/crypto/subtle/rsa-import-pkcs8-truncated-key.html
+ // headerSize 계산 과정:
+ // 5. headerSize = 21 + 3 = 24
+ // 6. 22바이트 버퍼에서 subspan(24) 호출 → CRASH (수정 전)
+ var truncatedPkcs8Key = hexStringToUint8Array("30000000000000000000000000000000000000000082");
+ shouldReject('crypto.subtle.importKey("pkcs8", truncatedPkcs8Key, {name: "RSA-OAEP", hash: "sha-256"}, extractable, ["decrypt"])');
Patch Details
importSpki()와 importPkcs8() 각각에 동일한 한 줄이 추가되었습니다. 두 곳 모두 마지막 headerSize += bytesUsedToEncodedLength(...) 연산 이후, keyData.subspan(headerSize) 호출 이전에 if (keyData.size() < headerSize) return nullptr; 검사가 삽입되었습니다. 기존의 중간 단계 bounds check는 변경 없이 유지됩니다.
Background
ASN.1은 태그-길이-값(TLV) 인코딩 방식을 사용합니다. 길이 필드는 "short form"(단일 바이트, ≤ 127)과 "long form"(첫 바이트 = 0x80 + N, 이후 N바이트로 실제 길이를 인코딩) 두 가지 형태로 구분됩니다. bytesUsedToEncodedLength()는 길이 필드가 소비하는 총 바이트 수를 반환하며, long form의 경우 이 값은 (firstByte - 128) + 1에 해당합니다. WebKit의 RSA key import 함수는 headerSize 오프셋을 점진적으로 계산하면서 ASN.1 헤더를 수동으로 제거한 뒤, 해당 오프셋을 기준으로 std::span::subspan()을 호출해 입력 버퍼를 분리합니다.
Hardened C++ 표준 라이브러리 빌드에서는 오프셋이 범위를 벗어날 경우 subspan()이 매번 동일하게 trap(EXC_BREAKPOINT / SIGTRAP)을 발생시킵니다. Non-hardened 빌드에서는 동일한 상황에서 subspan()이 버퍼 끝을 넘어선 지점에서 시작하는 span을 조용히 생성합니다.
Analysis
증분 방식의 ASN.1 header size 계산 완료 후, span slicing 이전의 최종 bounds check가 누락된 패턴.
importSpki()와 importPkcs8() 두 함수 모두 ASN.1 길이 필드를 기반으로 headerSize를 누적 계산하는 과정에서 중간 단계마다 bounds check를 수행했습니다. 그러나 마지막 증분 연산인 bytesUsedToEncodedLength(keyData[headerSize]) 추가 이후에는 후속 검사가 없었습니다. 마지막 위치에 큰 length-of-length 지시자가 포함된 조작된 키를 사용하면 headerSize를 keyData.size() 너머로 밀어낼 수 있었습니다. 예를 들어 0x82는 추가 길이 바이트 2개를 의미하므로 함수가 3을 반환합니다. 테스트 케이스는 이를 직접 보여줍니다. 22바이트 PKCS#8 키에서 마지막 0x82 바이트로 인해 headerSize가 24에 도달하고, 22바이트 버퍼에서 subspan(24)를 호출하면 crash가 발생합니다.
Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaa Aa Aa a Aaaaaa Aa Aaaaaa Aaaa Aa Aaaaaaaaaaaaa Aaaaa Aaaaaaaaaa Aaaaaaaa Aaaaa Aaaaaaa a Aa Aaa Aaa Aaa Aaaaa Aaa a Aaaaa Aaaaaaaa Aaaaa Aaaaaa Aa Aaaa Aaaaaa Aaaaaaaaaaaa Aaaaa Aaa Aaaaaaaa Aaa Aaa Aaaa Aaaa Aaa Aaaa Aaaaa Aa Aa Aaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaa a Aaa Aa Aaaa Aaa Aaaaaaaaaaaaaaaa Aaaa Aaaaaa
a Aaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaa Aaaa Aaaaaaa Aaa Aaaa Aaaa Aaaaaaaaaaaaaaaa Aaa Aaa Aa Aaa Aa a Aaaaaa Aa Aa Aaaaa Aa Aaaa Aaaaa Aaaa Aa Aa Aaaaaaaaaaaaa Aaaaaa Aaaa Aaaa Aaaaaa Aaaa Aaaa Aa Aaaa Aaaaa
Aa Aaaa Aaa Aaaaa Aaaa Aaaaa Aaaa Aaaaaa Aa Aaa Aaaaaa Aaaaaa Aa Aaaaaa Aa Aaa Aa Aa Aaaa Aa Aaa Aa Aa Aaaa Aa Aaaa Aaaaaa
🔒Explores the exploitability boundaries of this OOB access and what limits escalation beyond a crash
더 확인하려면 구독해 주세요
Audit directions
a Aaa Aa Aaaaaaaaa Aaaa a Aaa Aaa Aa Aaaa Aaa Aaaa Aaaaaa Aaaaa Aaa Aa Aa Aa Aaaa Aa Aa Aaaaaa Aaaaaaa Aa Aaaaaa Aaaaaa Aaaaaa Aa a Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aa Aaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aa Aaaaa Aaaa Aaaaaaaaa Aaaaaaa Aaaaaaaaaa Aaa Aaaaaa Aaaaaa Aaaaa Aaaaaa
a Aaa Aaa a Aa Aaaa Aaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaa Aaaaaa Aaaaaa Aaaaa Aaa Aa a Aaa Aa Aaaaa Aaaa Aaaa Aa Aa Aa Aa Aaaaaa Aaaaa Aa Aaaa Aaa Aaaaaa
a Aaaaaaaaaaaaaaaaaaaaa a Aa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaa Aaa Aaa Aa Aaaa Aaaaaa Aa a Aaa Aaa Aaaaaa Aa Aaaaa Aaa Aaaa Aaaa Aaaaaa Aaa Aaaaaaaaaaaa Aaaa Aa Aaaaaa Aaaaaa Aaa Aaaa Aaa Aaa Aaaaaa
🔒Multiple audit patterns identified for ASN.1 parsing across WebKit's crypto subsystem, with concrete search targets
더 확인하려면 구독해 주세요