Brave Browser implements a built-in AdBlock component that can parse AdBlock Plus filters (e.g. EasyList). The parser is implemented from Brave in native C++ code and was found to be vulnerable to an out-of-bounds (OOB) read of arbitrary size. Exploiting this vulnerability enables an adversary to read to an arbitrary memory address from Chrome’s privileged process since the AdBlock initialization is executed from the main process before delegating to sandboxed workers. This means one could use this vulnerability to perform information disclosure chain this with other vulnerabilities to perform code execution.
CVSS 3.0 Base Score
6.5 (AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N)
Researcher
xen1thlabs software labs
POC
The vulnerability was identified via fuzzing of a standalone AdBlock parser implementation and was reproduced in Brave version 1.0.77 and 1.0.94.The crash was initially triaged with Clang’s Address Sanitizer (ASan), which produced the following report.
================================================================= ==22248==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fa1330f673f at pc 0x7fa13705a935 bp 0x7ffedf96f210 sp 0x7ffedf96e9b8 READ of size 150001 at 0x7fa1330f673f thread T0 #0 0x7fa13705a934 in __asan_memcpy (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x8c934) #1 0x423783 in BloomFilter::BloomFilter( char const*, int, HashFn*, int) ../../../node_modules/bloom-filter-cpp/BloomFilter.cpp:31 #2 0x40bce9 in AdBlockClient::initBloomFilter(BloomFilter**, char const*,int) ../../../ad_block_client.cc:1005 #3 0x413ff7 in AdBlockClient::deserialize( char*) ../../../ad_block_client.cc:1768 #4 0x4034df in checkUrls( char const*, std::vector<std::__cxx11::basic_string< char, std::char_traits< char>, std::allocator< char> >, std::allocator<std::__cxx11::basic_string< char, std::char_traits< char>, std::allocator< char> > > > const&) ../../../main.cc:97 #5 0x40394b in main ../../../main.cc:125 #6 0x7fa13668c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #7 0x4026c8 in _start (/home/dmuser/brave/sample_asan+0x4026c8)0x7fa1330f673f is located 0 bytes to the right of 2694975- byte region [0x7fa132e64800,0x7fa1330f673f) allocated by thread T0 here: #0 0x7fa137067532 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99532) #1 0x7fa136d5eeec (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x112eec) #2 0x7fa136cfc19d in std::__basic_file< char>::close() (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0xb019d) #3 0x7ffedf96f48f (<unknown module>)SUMMARY: AddressSanitizer: heap-buffer-overflow ??:0 __asan_memcpy Shadow bytes around the buggy address: 0x0ff4a6616c90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ff4a6616ca0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ff4a6616cb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ff4a6616cc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ff4a6616cd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0ff4a6616ce0: 00 00 00 00 00 00 00[07]fa fa fa fa fa fa fa fa 0x0ff4a6616cf0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0ff4a6616d00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0ff4a6616d10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0ff4a6616d20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0ff4a6616d30: 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 Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 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 ==22248==ABORTING
The top frame indicates an OOB (Out-Of-Bands) read in line 31, with 150001 bytes being read beyond the boundaries of the buffer heap allocated buffer.
1 2 3 4 5 6 7 8 9 10 11 12 13
File: bloom-filter-cpp/BloomFilter.cpp 21 // Constructs a BloomFilter by copying the specified buffer and number of bytes 22 BloomFilter::BloomFilter(const char *buffer, int byteBufferSize, 23 HashFn *hashFns, int numHashFns) : 24 hashFns(nullptr), numHashFns(0), byteBufferSize(0), buffer(nullptr) { 25 this->hashFns = hashFns; 26 this->numHashFns = numHashFns; 27 lastHashes = new uint64_t[numHashFns]; 28 this->byteBufferSize = byteBufferSize; 29 bitBufferSize = byteBufferSize * 8; 30 this->buffer = new char[byteBufferSize]; 31 memcpy(this->buffer, buffer, byteBufferSize); 32 }
BloomFilter::BloomFilter is invoked from AdBlockClient::initBloomFilter (line 1005), as indicated in the second frame of the stack trace.
1 2 3 4 5 6 7 8 9 10
File: ad-block/ad_block_client.cc 999 void AdBlockClient::initBloomFilter(BloomFilter **pp, 1000 const char *buffer, int len) { 1001 if (*pp) { 1002 delete *pp; 1003 } 1004 if (len > 0) { 1005 *pp = new BloomFilter(buffer, len); 1006 } 1007 }
AdBlockClient::initBloomFilter is invoked from AdBlockClient::deserialize (line 1768), as indicated in the third frame of the stack trace. Notice in line 1721 that the bloomFilterSize variable is directly read from the untrusted input file and used to offset the input buffer without any boundary checks taking place. Therefore, an attacker can read up to max size_t bytes beyond the heap buffer, since the integer variable will be eventually casted to size_t in the memcpy call of the BloomFilter::BloomFilter function.
Now tracing the place within the application that the AdBlock payload (as downloaded over the Internet from Brave endpoints in AmazonS3) is actually parsed identified the following. The AdBlockClient::deserialize is called in line 197 of the BlockersWorker::InitAdBlock function, which first reads the file contents in line 192 that were downloaded on previous steps (see ADBlockUpdater.UpdateADBlock() invoked early on application launch).
BlockersWorker::InitAdBlock is invoked from ChromeNetworkDelegate::OnBeforeURLRequest_AdBlockFileWork (line 577). The ChromeNetworkDelegate family of callbacks is the central point from within the chrome code to add hooks into the network stack that executed from the main process.
As a proof of concept the malformed file generated from the fuzzer was provided as input to the Brave application (served over the web via a Man-in-The-Middle attack by replacing 4ABPFilterParserData.dat file). Upon visiting any URL the main process of the Brave Browser crashed with the following logcat message.
04-03 08:28:42.933 3641 3659 I cr_ADB : Downloaded 4ABPFilterParserData.dat 04-03 08:28:42.933 3641 3659 I cr_ADB : Creating ABPFilterParserDataDownloaded.dat from 4ABPFilterParserData.dat 04-03 08:28:42.937 3641 3659 I cr_ADB : Created ABPFilterParserDataDownloaded.dat from 4ABPFilterParserData.dat 04-03 08:28:42.950 3641 3659 I cr_ADB : Downloading 6.0httpse.leveldb.zip 04-03 08:28:58.671 3641 3659 I cr_ADB : Downloaded 6.0httpse.leveldb.zip 04-03 08:28:58.894 3641 3659 I cr_ADB : Creating httpse.leveldbDownloaded.zip from 6.0httpse.leveldb 04-03 08:28:58.895 3641 3659 I cr_ADB : Created httpse.leveldbDownloaded.zip from 6.0httpse.leveldb 04-03 08:29:10.352 3641 3641 W cr_AutocompleteEdit: Using spannable model... 04-03 08:29:10.355 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:10.370 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:10.405 3641 3646 I zygote : Do partial code cache collection, code=62KB, data=49KB 04-03 08:29:10.405 3641 3646 I zygote : After code cache collection, code=62KB, data=49KB 04-03 08:29:10.405 3641 3646 I zygote : Increasing code cache capacity to 256KB 04-03 08:29:10.604 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:10.617 3641 3641 I chatty : uid=10081(com.brave.browser) identical 2 lines 04-03 08:29:10.619 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:12.154 3641 3641 W cr_SpanAutocomplete: Did not notify - in batch edit. 04-03 08:29:12.159 3641 3641 I chatty : uid=10081(com.brave.browser) identical 2 lines 04-03 08:29:12.171 3641 3641 W cr_SpanAutocomplete: Did not notify - in batch edit. 04-03 08:29:12.194 3641 3641 W cr_UrlBar: Text change observed, triggering autocomplete. 04-03 08:29:12.194 3641 3641 W cr_Autocomplete: onTextChangedForAutocomplete 04-03 08:29:12.194 3641 3641 W cr_Autocomplete: stopping autocomplete. 04-03 08:29:12.225 3641 3641 W cr_Autocomplete: starting autocomplete controller..[ false][ false] 04-03 08:29:12.229 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:12.234 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:12.351 3641 3641 W cr_SpanAutocomplete: Did not notify - in batch edit. 04-03 08:29:12.354 3641 3641 I chatty : uid=10081(com.brave.browser) identical 2 lines 04-03 08:29:12.372 3641 3641 W cr_SpanAutocomplete: Did not notify - in batch edit. 04-03 08:29:12.374 3641 3641 W cr_UrlBar: Text change observed, triggering autocomplete. 04-03 08:29:12.374 3641 3641 W cr_Autocomplete: onTextChangedForAutocomplete 04-03 08:29:12.374 3641 3641 W cr_Autocomplete: stopping autocomplete. 04-03 08:29:12.406 3641 3641 W cr_Autocomplete: starting autocomplete controller..[ false][false] 04-03 08:29:12.410 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:12.412 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:12.531 3641 3641 W cr_SpanAutocomplete: Did not notify - in batch edit. 04-03 08:29:12.532 3641 3641 I chatty : uid=10081(com.brave.browser) identical 2 lines 04-03 08:29:12.539 3641 3641 W cr_SpanAutocomplete: Did not notify - in batch edit. 04-03 08:29:12.542 3641 3641 W cr_UrlBar: Text change observed, triggering autocomplete. 04-03 08:29:12.542 3641 3641 W cr_Autocomplete: onTextChangedForAutocomplete 04-03 08:29:12.542 3641 3641 W cr_Autocomplete: stopping autocomplete. 04-03 08:29:12.573 3641 3641 W cr_Autocomplete: starting autocomplete controller..[ false][ false] 04-03 08:29:12.577 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:12.578 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:12.735 3641 3641 W cr_SpanAutocomplete: Did not notify - in batch edit. 04-03 08:29:12.737 3641 3641 I chatty : uid=10081(com.brave.browser) identical 2 lines 04-03 08:29:12.740 3641 3641 W cr_SpanAutocomplete: Did not notify - in batch edit. 04-03 08:29:12.743 3641 3641 W cr_UrlBar: Text change observed, triggering autocomplete. 04-03 08:29:12.743 3641 3641 W cr_Autocomplete: onTextChangedForAutocomplete 04-03 08:29:12.743 3641 3641 W cr_Autocomplete: stopping autocomplete. 04-03 08:29:12.774 3641 3641 W cr_Autocomplete: starting autocomplete controller..[ false][04-03 08:29:12.778 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:12.780 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:12.906 3641 3641 W cr_SpanAutocomplete: Did not notify - in batch edit. 04-03 08:29:12.910 3641 3641 I chatty : uid=10081(com.brave.browser) identical 2 lines 04-03 08:29:12.925 3641 3641 W cr_SpanAutocomplete: Did not notify - in batch edit. 04-03 08:29:12.928 3641 3641 W cr_UrlBar: Text change observed, triggering autocomplete. 04-03 08:29:12.928 3641 3641 W cr_Autocomplete: onTextChangedForAutocomplete 04-03 08:29:12.928 3641 3641 W cr_Autocomplete: stopping autocomplete. 04-03 08:29:12.960 3641 3641 W cr_Autocomplete: starting autocomplete controller..[ false][ false]04-03 08:29:12.963 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:12.965 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:13.410 3641 3641 W cr_Autocomplete: stopping autocomplete. 04-03 08:29:13.561 3641 3641 W cr_SpanAutocomplete: Did not notify - ignored. 04-03 08:29:13.568 3641 3641 W cr_Autocomplete: stopping autocomplete. 04-03 08:29:13.569 3641 3641 W cr_Autocomplete: stopping autocomplete. 04-03 08:29:13.570 3641 3641 W cr_Autocomplete: stopping autocomplete. 04-03 08:29:13.571 3641 3641 W cr_Autocomplete: stopping autocomplete. 04-03 08:29:13.571 3641 3641 W cr_SpanAutocomplete: Did not notify - no change. 04-03 08:29:13.578 3641 3689 E cr_ApiBridge: Failed to init handler: Attempt to invoke virtual method'java.lang.reflect.Constructor java.lang. Class.getDeclaredConstructor(java.lang. Class[])' on a null object reference 04-03 08:29:13.589 3641 3641 W IInputConnectionWrapper: getSelectedText on inactive InputConnection 04-03 08:29:13.592 3641 3641 W IInputConnectionWrapper: requestCursorAnchorInfo on inactive InputConnection 04-03 08:29:13.592 3641 3641 W IInputConnectionWrapper: getTextBeforeCursor on inactive InputConnection 04-03 08:29:13.595 3641 3641 W IInputConnectionWrapper: getTextBeforeCursor on inactive InputConnection 04-03 08:29:13.598 3641 3641 W IInputConnectionWrapper: getTextBeforeCursor on inactive InputConnection 04-03 08:29:15.852 3641 3641 W cr_SpanAutocomplete: Did not notify - ignored. 04-03 08:29:15.893 3641 3641 I zygote : Deoptimizing void bmi.onMeasure( int, int) due to JIT inline cache 04-03 08:29:15.908 3641 3689 F libc : Fatal signal 8 (SIGFPE), code -6, fault addr 0xe39 in tid 3689 (Chrome_IOThread), pid 3641 (m.brave.browser) 04-03 08:29:15.964 3834 3834 I crash_dump32: obtaining output fd from tombstoned, type: kDebuggerdTombstone 04-03 08:29:15.968 3834 3834 I crash_dump32: performing dump of process 3641 (target tid = 3689) 04-03 08:29:15.968 3834 3834 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 04-03 08:29:15.969 3834 3834 F DEBUG : Build fingerprint: 'Android/aosp_sailfish/sailfish:8.1.0/OPM4.171019.021.P1/anesti09272147:userdebug/test-keys' 04-03 08:29:15.969 3834 3834 F DEBUG : Revision: '0' 04-03 08:29:15.969 3834 3834 F DEBUG : ABI: 'arm' 04-03 08:29:15.969 3834 3834 F DEBUG : pid: 3641, tid: 3689, name: Chrome_IOThread >>> com.brave.browser <<< 04-03 08:29:15.969 3834 3834 F DEBUG : signal 8 (SIGFPE), code -6 (SI_TKILL), fault addr -------- 04-03 08:29:15.969 3834 3834 F DEBUG : r0 00000000 r1 00000e69 r2 00000008 r3 c4a7f344 04-03 08:29:15.969 3834 3834 F DEBUG : r4 c4a7f344 r5 00000000 r6 dc4535e8 r7 0000010c 04-03 08:29:15.969 3834 3834 F DEBUG : r8 0000000e r9 dc46b7ee sl dc46bdd0 fp 0000000e 04-03 08:29:15.969 3834 3834 F DEBUG : ip c4a7f344 sp c4a7f328 lr c64dec00 pc e70eebbc cpsr 00070010 04-03 08:29:15.970 3834 3834 F DEBUG : 04-03 08:29:15.970 3834 3834 F DEBUG : backtrace: 04-03 08:29:15.970 3834 3834 F DEBUG : #00 pc 0004abbc /system/lib/libc.so (tgkill+12) 04-03 08:29:15.970 3834 3834 F DEBUG : #01 pc 00035bfc /data/app/ com.brave.browser-3sVE2mDMPdmzlOkZCO13Hw==/base.apk (offset 0x16dc000) 04-03 08:29:17.198 626 626 I Zygote : Process 3641 exited due to signal (8)
POC Attachments
Not released
Disclosure Timelines
19-Jun-2019 - Notified vendor
15-Aug-2019 - Brave browser Android v1.2.0 released which resolves this