heap buffer overflow in nsStructuredCloneContainer::GetDataAsBase64 from integer overflow
Categories
(Core :: DOM: Core & HTML, defect, P1)
Tracking
()
People
(Reporter: bo13oy, Assigned: smaug)
References
Details
(Keywords: csectype-intoverflow, reporter-external, sec-high, Whiteboard: [reporter-external] [client-bounty-form][sec-survey][adv-main95+][adv-ESR91.4.0+])
Attachments
(4 files)
4.29 KB,
application/zip
|
Details | |
388 bytes,
text/html
|
Details | |
48 bytes,
text/x-phabricator-request
|
diannaS
:
approval-mozilla-beta+
RyanVM
:
approval-mozilla-esr91+
tjr
:
sec-approval+
|
Details | Review |
228 bytes,
text/plain
|
Details |
Tested Version: Windows 10 1909 x64 memory 16G + firfox 93.0 64-bit.
####Root cause:
In functionn sStructuredCloneContainer::GetDataAsBase64, at the position #1, v10 Force conversion from 64-bit to 32-bit, which leads to Integer Overflow vulnerability. This vulnerability can be easily exploited for Remote Code Execution Vulnerability.
The patch for this vulnerability is simple, you just need to determine if v10 overflows before calling SetLength.
####Code:
__int64 __fastcall nsStructuredCloneContainer::GetDataAsBase64(__int64 a1, __int64 a2)
{
__int64 v2; // rsi
__int64 v3; // rbx
__int64 v4; // rax
_QWORD *v5; // rcx
unsigned int v6; // ebp
_QWORD *v8; // rcx
__int64 v9; // rax
unsigned __int64 v10; // rdi
__int64 v11; // rax
signed __int64 v12; // r15
__int64 v13; // r12
__int64 v14; // rbx
size_t v15; // rbp
int v16; // eax
__int64 v17; // [rsp+0h] [rbp-D8h]
void *Src[2]; // [rsp+20h] [rbp-B8h]
__int128 v19; // [rsp+30h] [rbp-A8h]
char *v20; // [rsp+40h] [rbp-98h]
__int64 v21; // [rsp+48h] [rbp-90h]
int v22; // [rsp+50h] [rbp-88h]
char v23; // [rsp+54h] [rbp-84h]
unsigned __int64 v24; // [rsp+98h] [rbp-40h]
v2 = a2;
v3 = a1;
v24 = (unsigned __int64)&v17 ^ _security_cookie;
nsTSubstring_char16_t_::Truncate(a2);
v4 = *(_QWORD *)(v3 + 240);
v5 = (_QWORD *)(v4 + 64);
if ( !v4 )
v5 = (_QWORD *)(v3 + 168);
v6 = -2147467259;
if ( *v5 && !**(_DWORD **)(v3 + 56) && !**(_DWORD **)(v3 + 64) && !**(_DWORD **)(v3 + 80) && !**(_DWORD **)(v3 + 72) )
{
v8 = (_QWORD *)(v4 + 8);
if ( !v4 )
v8 = (_QWORD *)(v3 + 112);
v19 = 0i64;
*(_OWORD *)Src = 0i64;
if ( v8[2] )
{
v9 = v8[1];
Src[1] = *(void **)v9;
*(_QWORD *)&v19 = (char *)Src[1] + *(_QWORD *)(v9 + 8);
}
v10 = v8[7];
v20 = &v23;
v21 = 844497944576000i64;
v22 = 63;
v23 = 0;
v6 = -2147024882;
if ( nsTSubstring_char_::SetLength((volatile signed __int32 **)&v20, v10) )
...
if ( (unsigned __int8)nsTSubstring_char_::SetLength(&v20, (unsigned int)v10, &std::nothrow) ) #1
{
v11 = *(_QWORD *)(v3 + 240);
v12 = v11 + 8;
if ( !v11 )
v12 = v3 + 112;
if ( !(unsigned __int8)nsTSubstring_char_::EnsureMutable(&v20, 0xFFFFFFFFi64) )
NS_ABORT_OOM((unsigned int)v21);
if ( v10 )
{
v13 = (__int64)v20;
v14 = 0i64;
do
{
if ( Src[1] > (void *)v19 )
{
gMozCrashReason = "MOZ_RELEASE_ASSERT(mData <= mDataEnd)";
__debugbreak();
MOZ_NoReturn(209i64);
}
v15 = v19 - (unsigned __int64)Src[1];
if ( v10 < (unsigned __int64)v19 - (unsigned __int64)Src[1] )
v15 = v10;
if ( !v15 )
break;
if ( Src[1] == (void *)v19 )
{
gMozCrashReason = "MOZ_RELEASE_ASSERT(!Done())";
__debugbreak();
MOZ_NoReturn(196i64);
}
memcpy_0((void *)(v13 + v14), Src[1], v15);
v14 += v15;
mozilla::BufferList_js::SystemAllocPolicy_::IterImpl::Advance(Src, v12, v15);
v10 -= v15;
}
while ( v10 );
}
....
}
####OOB Write details:
Debugging Details:
------------------
0:000> r
rax=000001d4af2fe808 rbx=00000000000ad000 rcx=000001d4af2fef20
rdx=000001d4b5f16718 rsi=000001d4b57996a0 rdi=00000000fff53430
rip=00007ffd35bd151e rsp=000000036c7fb7f8 rbp=0000000000001000
r8=00000000000008e8 r9=ffffffffffffffe8 r10=00007ffd35bd0000
r11=000001d665c00000 r12=000001d4af251808 r13=000000036c7fbaa0
r14=000000036c7fb820 r15=000001d4af205828
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
VCRUNTIME140!memcpy+0x22e:
00007ffd`35bd151e c5fd7fa1e0000000 vmovdqa ymmword ptr [rcx+0E0h],ymm4 ds:000001d4`af2ff000=??
0:000> kb
RetAddr : Args to Child : Call Site
00007ffd`16446f5b : 00000000`00000000 0000f1a7`6bbca904 000001d4`b5624000 00000003`6c7fb8b0 : VCRUNTIME140!memcpy+0x22e
00007ffd`17083170 : 0000f1a7`6bbca9e4 00000003`6c7fb978 000001d4`b5624088 00000003`6c7fb968 : xul!nsStructuredCloneContainer::GetDataAsBase64+0x18b [/builds/worker/checkouts/gecko/dom/base/nsStructuredCloneContainer.cpp @ 151]
00007ffd`1707f930 : 000001d4`b5750c00 00007ffd`165d8d8c 0000f1a7`6bbca614 fffe02e8`44f009b0 : xul!mozilla::dom::Notification::InitFromJSVal+0x80 [/builds/worker/checkouts/gecko/dom/notification/Notification.cpp @ 1952]
00007ffd`1707f7a0 : 000001d4`b5624068 00000003`6c7fbde8 fff98000`00000000 000001d4`00000c01 : xul!mozilla::dom::Notification::CreateAndShow+0xe0 [/builds/worker/checkouts/gecko/dom/notification/Notification.cpp @ 2234]
00007ffd`165f2a2d : 00000003`6c7fba88 00000000`00000000 00000000`00000002 000001d4`b735c1e0 : xul!mozilla::dom::Notification::Constructor+0x140 [/builds/worker/checkouts/gecko/dom/notification/Notification.cpp @ 779]
00007ffd`157b6066 : 000001d4`b5620001 00000003`6c7fbf00 00000000`00000000 00000000`b5010111 : xul!mozilla::dom::Notification_Binding::_constructor+0x41d [/builds/worker/workspace/obj-build/dom/bindings/NotificationBinding.cpp @ 2212]
00007ffd`159753cb : 00000101`0000fe05 00000003`6c7fc400 00007ffd`18ae8bb0 00000000`00000077 : xul!InternalConstruct+0x296 [/builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp @ 612]
0:000> dd rcx
000001d4`af2fef20 41414141 41414141 41414141 41414141
000001d4`af2fef30 41414141 41414141 41414141 41414141
000001d4`af2fef40 41414141 41414141 41414141 41414141
000001d4`af2fef50 41414141 41414141 41414141 41414141
000001d4`af2fef60 41414141 41414141 41414141 41414141
000001d4`af2fef70 41414141 41414141 41414141 41414141
000001d4`af2fef80 41414141 41414141 41414141 41414141
000001d4`af2fef90 41414141 41414141 41414141 41414141
#########################################################################################################################################
This vuln is discovered by bo13oy of Cyber Kunlun Lab.
Thanks.
Comment 1•3 years ago
|
||
Olli, are you the right person to take a look?
Comment 2•3 years ago
|
||
Here's the poc.html from the zip file.
Tested Version: Ubuntu 64-bit memory 16G + linux64-fuzzing-asan-opt(94.0 (64-bit)) => https://firefox-ci-tc.services.mozilla.com/tasks/index/gecko.v2.mozilla-release.latest.firefox/linux64-fuzzing-asan-opt
Loading poc.html with firefox, the crash report is as follows:
###################################################################################################################################################################################
==2203==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61d0000b3480 at pc 0x564bb2d01f6a bp 0x7ffe4d4b27e0 sp 0x7ffe4d4b1fa8
WRITE of size 4096 at 0x61d0000b3480 thread T0 (file:// Content)
#0 0x564bb2d01f69 in __asan_memcpy /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3
#1 0x7f2520adee09 in mozilla::BufferList<js::SystemAllocPolicy>::ReadBytes(mozilla::BufferList<js::SystemAllocPolicy>::IterImpl&, char*, unsigned long) const /builds/worker/workspace/obj-build/dist/include/mozilla/BufferList.h:492:5
#2 0x7f2520adb6d2 in ReadBytes /builds/worker/workspace/obj-build/dist/include/js/StructuredClone.h:505:21
#3 0x7f2520adb6d2 in nsStructuredCloneContainer::GetDataAsBase64(nsTSubstring<char16_t>&) /builds/worker/checkouts/gecko/dom/base/nsStructuredCloneContainer.cpp:151:32
#4 0x7f2523dfc781 in mozilla::dom::Notification::InitFromJSVal(JSContext*, JS::Handle<JS::Value>, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/notification/Notification.cpp:1952:30
#5 0x7f2523df007d in mozilla::dom::Notification::CreateAndShow(JSContext*, nsIGlobalObject*, nsTSubstring<char16_t> const&, mozilla::dom::NotificationOptions const&, nsTSubstring<char16_t> const&, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/notification/Notification.cpp:2233:17
#6 0x7f2523defb29 in mozilla::dom::Notification::Constructor(mozilla::dom::GlobalObject const&, nsTSubstring<char16_t> const&, mozilla::dom::NotificationOptions const&, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/notification/Notification.cpp:780:7
#7 0x7f25211ae4a2 in mozilla::dom::Notification_Binding::_constructor(JSContext*, unsigned int, JS::Value*) /builds/worker/workspace/obj-build/dom/bindings/NotificationBinding.cpp:2212:58
#8 0x7f25290d357f in CallJSNative /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:385:13
#9 0x7f25290d357f in CallJSNativeConstructor /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:401:8
#10 0x7f25290d357f in InternalConstruct(JSContext*, js::AnyConstructArgs const&) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:596:10
#11 0x7f2529f47010 in js::jit::DoCallFallback(JSContext*, js::jit::BaselineFrame*, js::jit::ICFallbackStub*, unsigned int, JS::Value*, JS::MutableHandle<JS::Value>) /builds/worker/checkouts/gecko/js/src/jit/BaselineIC.cpp:1571:10
#12 0x7f2492b66de7 (<unknown module>)
0x61d0000b3480 is located 0 bytes to the right of 2048-byte region [0x61d0000b2c80,0x61d0000b3480)
allocated by thread T0 (file:// Content) here:
#0 0x564bb2d02b0d in malloc /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:145:3
#1 0x7f251d047b54 in Alloc /builds/worker/checkouts/gecko/xpcom/string/nsSubstring.cpp:206:42
#2 0x7f251d047b54 in nsTSubstring<char>::StartBulkWriteImpl(unsigned int, unsigned int, bool, unsigned int, unsigned int, unsigned int) /builds/worker/checkouts/gecko/xpcom/string/nsTSubstring.cpp:202:32
#3 0x7f251d050ab1 in nsTSubstring<char>::SetLength(unsigned int, std::nothrow_t const&) /builds/worker/checkouts/gecko/xpcom/string/nsTSubstring.cpp:936:7
#4 0x7f2520adb66a in nsStructuredCloneContainer::GetDataAsBase64(nsTSubstring<char16_t>&) /builds/worker/checkouts/gecko/dom/base/nsStructuredCloneContainer.cpp:147:19
#5 0x7f2523dfc781 in mozilla::dom::Notification::InitFromJSVal(JSContext*, JS::Handle<JS::Value>, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/notification/Notification.cpp:1952:30
#6 0x7f2523df007d in mozilla::dom::Notification::CreateAndShow(JSContext*, nsIGlobalObject*, nsTSubstring<char16_t> const&, mozilla::dom::NotificationOptions const&, nsTSubstring<char16_t> const&, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/notification/Notification.cpp:2233:17
#7 0x7f2523defb29 in mozilla::dom::Notification::Constructor(mozilla::dom::GlobalObject const&, nsTSubstring<char16_t> const&, mozilla::dom::NotificationOptions const&, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/notification/Notification.cpp:780:7
#8 0x7f25211ae4a2 in mozilla::dom::Notification_Binding::_constructor(JSContext*, unsigned int, JS::Value*) /builds/worker/workspace/obj-build/dom/bindings/NotificationBinding.cpp:2212:58
#9 0x7f25290d357f in CallJSNative /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:385:13
#10 0x7f25290d357f in CallJSNativeConstructor /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:401:8
#11 0x7f25290d357f in InternalConstruct(JSContext*, js::AnyConstructArgs const&) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:596:10
#12 0x7f2529f47010 in js::jit::DoCallFallback(JSContext*, js::jit::BaselineFrame*, js::jit::ICFallbackStub*, unsigned int, JS::Value*, JS::MutableHandle<JS::Value>) /builds/worker/checkouts/gecko/js/src/jit/BaselineIC.cpp:1571:10
#13 0x7f2492b66de7 (<unknown module>)
#14 0x61600006b9af (<unknown module>)
#15 0x7f2492b6456e (<unknown module>)
SUMMARY: AddressSanitizer: heap-buffer-overflow /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3 in __asan_memcpy
Shadow bytes around the buggy address:
0x0c3a8000e640: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c3a8000e650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c3a8000e660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c3a8000e670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c3a8000e680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c3a8000e690:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c3a8000e6a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c3a8000e6b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c3a8000e6c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c3a8000e6d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c3a8000e6e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==2203==ABORTING
###################################################################################################################################################################################
Assignee | ||
Comment 4•3 years ago
|
||
Updated•3 years ago
|
Updated•3 years ago
|
Assignee | ||
Comment 5•3 years ago
|
||
Comment on attachment 9248652 [details]
Bug 1738237, don't try to create too large string buffers, r=mccr8
Security Approval Request
- How easily could an exploit be constructed based on the patch?: Not sure about exploit but the issue is very obvious.
- Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: No
- Which older supported branches are affected by this flaw?: All
- If not all supported branches, which bug introduced the flaw?: None
- Do you have backports for the affected branches?: Yes
- If not, how different, hard to create, and risky will they be?: The same patch applies even to esr78 (with a bit --fuzz)
- How likely is this patch to cause regressions; how much testing does it need?: Should be very safe. It is just checking overflow.
Comment 6•3 years ago
|
||
FWIW, DoConsumeStream has a similar explicit check. CopyCocoaStringToXPCOMString has a different sort of check.
Updated•3 years ago
|
Comment 7•3 years ago
|
||
Comment on attachment 9248652 [details]
Bug 1738237, don't try to create too large string buffers, r=mccr8
Approved to land and request uplift
Assignee | ||
Comment 8•3 years ago
|
||
Comment on attachment 9248652 [details]
Bug 1738237, don't try to create too large string buffers, r=mccr8
Beta/Release Uplift Approval Request
- User impact if declined: Crashes, in an unsafe way
- Is this code covered by automated tests?: No
- Has the fix been verified in Nightly?: No
- Needs manual test from QE?: Yes
- If yes, steps to reproduce: Run the poc.html
- List of other uplifts needed: None
- Risk to taking this patch: Low
- Why is the change risky/not risky? (and alternatives if risky): Requires passing unusually large array to an API which isn't usually used for that
- String changes made/needed: NA
ESR Uplift Approval Request
- If this is not a sec:{high,crit} bug, please state case for ESR consideration:
- User impact if declined:
- Fix Landed on Version:
- Risk to taking this patch: Low
- Why is the change risky/not risky? (and alternatives if risky):
- String or UUID changes made by this patch:
Assignee | ||
Updated•3 years ago
|
![]() |
||
Comment 9•3 years ago
|
||
don't try to create too large string buffers, r=mccr8
https://hg.mozilla.org/integration/autoland/rev/5db680fef44eccc0f8a1d1a8713dd8456c87b0f3
https://hg.mozilla.org/mozilla-central/rev/5db680fef44e
Updated•3 years ago
|
Updated•3 years ago
|
Updated•3 years ago
|
Comment 10•3 years ago
|
||
As part of a security bug pattern analysis, we are requesting your help with a high level analysis of this bug. It is our hope to develop static analysis (or potentially runtime/dynamic analysis) in the future to identify classes of bugs.
Please visit this google form to reply.
Assignee | ||
Updated•3 years ago
|
Comment 11•3 years ago
•
|
||
Reproduced the tab crash on Windows 10x64 using 95.0a1 (20211028223457) and on Ubuntu 18.04 using Firefox 94.0.2 (20211104142158) asan build from comment 3 and the attached test case.
Tab no longer crashes on Ubuntu 18.04 with Firefox 96.0a1 (20211111045525) asan and normal build. Also, the test case was loaded on Windows 10x64 and macOS 10.15 with Firefox 96.0a1 (20211111045525), and the tab is loaded correctly with no crashes.
Maybe worth mentioning that the tab with the attached test case from comment 2 is not loaded instantly and only after ~2-4 sec.
Comment 12•3 years ago
|
||
Comment on attachment 9248652 [details]
Bug 1738237, don't try to create too large string buffers, r=mccr8
Approved for 95.0b6
Comment 13•3 years ago
|
||
uplift |
Comment 14•3 years ago
|
||
Verified that the tab is no longer crashing when loading the attached test case with Firefox 95.0b6 on Windows 10x64, macOS 10.15, and Ubuntu 18.04 and 20.04.
Just to be sure here. Is it ok that the loading time for the attached test case is higher on Firefox than on other browsers? For example, Chrome and Safari load the test case instantly and Firefox loads it in like ~2-5 seconds. Should we open another issue for this? Thank you!
Assignee | ||
Comment 15•3 years ago
|
||
That would be JS perf issue. If you extract that part from the test, filing a new bug sounds reasonable.
Comment 16•3 years ago
|
||
(In reply to Olli Pettay [:smaug] from comment #15)
That would be JS perf issue. If you extract that part from the test, filing a new bug sounds reasonable.
Thank you! Marking Firefox 95 as verified per comment 14 and comment 15. Also, bug 1740923 was logged for the remaining issue.
Comment 17•3 years ago
|
||
Comment on attachment 9248652 [details]
Bug 1738237, don't try to create too large string buffers, r=mccr8
Approved for 91.4esr.
Comment 18•3 years ago
|
||
uplift |
Comment 19•3 years ago
|
||
Verified fixed with 91.4.0esr (20211115182036) on Windows 10x64, macOS 10.15, Ubuntu 18.04 and 20.04. Tab no longer crashes when loading the attached test case.
Updated•3 years ago
|
Comment 20•3 years ago
|
||
Updated•3 years ago
|
Updated•3 years ago
|
Updated•9 months ago
|
Description
•