1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
Source: https://code.google.com/p/google-security-research/issues/detail?id=513 There's an integer overflow issue in sanity checking section lengths when parsing the vcdiff format (used in SDCH content encoding). This results in the parser parsing outside of sane memory bounds when parsing the contents of a vcdiff window - see attached crash PoC. (/src/sdch/open-vcdiff/src/headerparser.cc) bool VCDiffHeaderParser::ParseSectionLengths( bool has_checksum, size_t* add_and_run_data_length, size_t* instructions_and_sizes_length, size_t* addresses_length, VCDChecksum* checksum) { ParseSize("length of data for ADDs and RUNs", add_and_run_data_length); // <---- user controlled ParseSize("length of instructions section", instructions_and_sizes_length); // <---- user controlled ParseSize("length of addresses for COPYs", addresses_length); // <---- user controlled if (has_checksum) { ParseChecksum("Adler32 checksum value", checksum); } if (RESULT_SUCCESS != return_code_) { return false; } if (!delta_encoding_start_) { VCD_DFATAL << "Internal error: VCDiffHeaderParser::ParseSectionLengths " "was called before ParseWindowLengths" << VCD_ENDL; return_code_ = RESULT_ERROR; return false; } const size_t delta_encoding_header_length = UnparsedData() - delta_encoding_start_; if (delta_encoding_length_ != (delta_encoding_header_length + *add_and_run_data_length + *instructions_and_sizes_length + *addresses_length)) {// <---- Integer overflow here (32-bit systems only) VCD_ERROR << "The length of the delta encoding does not match " "the size of the header plus the sizes of the data sections" << VCD_ENDL; return_code_ = RESULT_ERROR; return false; } return true; } These returned lengths are subsequently used to initialise length-checked buffer objects for continuing the parsing (vcdecoder.cc:1024) size_t add_and_run_data_length = 0; size_t instructions_and_sizes_length = 0; size_t addresses_length = 0; if (!header_parser->ParseSectionLengths(has_checksum_, &add_and_run_data_length, &instructions_and_sizes_length, &addresses_length, &expected_checksum_)) { return header_parser->GetResult(); } if (parent_->AllowInterleaved() && // snip... } else { // If interleaved format is not used, then the whole window contents // must be available before decoding can begin.If only part of // the current window is available, then report end of data // and re-parse the whole header when DecodeChunk() is called again. if (header_parser->UnparsedSize() < (add_and_run_data_length + instructions_and_sizes_length + addresses_length)) { return RESULT_END_OF_DATA; } data_for_add_and_run_.Init(header_parser->UnparsedData(), add_and_run_data_length); instructions_and_sizes_.Init(data_for_add_and_run_.End(), instructions_and_sizes_length); addresses_for_copy_.Init(instructions_and_sizes_.End(), addresses_length); This issue only affects 32-bit builds, since ParseSize is parsing a positive int32_t; on 64-bit builds it cannot be large enough to wrap a size_t. It's unclear if this is exploitable as a browser-process infoleak; the results of SDCH decoding will be returned to a renderer process, but the way that the returned values are used mean that it is likely that the process will have to survive reads at opposite ends of the address space, which *should* be guaranteed to crash with a 2:2 address space split. It is possible that on 32-bit Windows with a 1:3 address space split this can be survived, or with careful crafting of the input file these reads can be avoided; I've not investigated further at this point. It appears to be necessary to host the PoC on a legitimate domain; as localhost is not supported for SDCH. VERSION Chrome Version: 47.0.2499.0 Operating System: Linux x86 REPRODUCTION CASE Please include a demonstration of the security bug, such as an attached HTML or binary file that reproduces the bug when loaded in Chrome. PLEASE make the file as small as possible and remove any content not required to demonstrate the bug. FOR CRASHES, PLEASE INCLUDE THE FOLLOWING ADDITIONAL INFORMATION Type of crash: browser Crash State: eax0xf9ae8a78 -106001800 ecx0xe7502d43 -414175933 edx0x7b83e020 2072240160 ebx0xf76597a0 -144336992 esp0xe75025d0 0xe75025d0 ebp0xe7502798 0xe7502798 esi0x5 5 edi0xf9061200 -117042688 eip0xf1ddebee 0xf1ddebee <open_vcdiff::VCDiffCodeTableReader::GetNextInstruction(int*, unsigned char*)+94> eflags 0x210a93 [ CF AF SF IF OF RF ID ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x63 99 => 0xf1ddebee <open_vcdiff::VCDiffCodeTableReader::GetNextInstruction(int*, unsigned char*)+94>: movzbl (%edx),%ecx 0xf1ddebf1 <open_vcdiff::VCDiffCodeTableReader::GetNextInstruction(int*, unsigned char*)+97>: mov(%edi),%esi 0xf1ddebf3 <open_vcdiff::VCDiffCodeTableReader::GetNextInstruction(int*, unsigned char*)+99>: cmpb $0x0,0x100(%esi,%ecx,1) 0xf1ddebfb <open_vcdiff::VCDiffCodeTableReader::GetNextInstruction(int*, unsigned char*)+107>: je 0xf1ddec06 <open_vcdiff::VCDiffCodeTableReader::GetNextInstruction(int*, unsigned char*)+118> 0xf1ddebfd <open_vcdiff::VCDiffCodeTableReader::GetNextInstruction(int*, unsigned char*)+109>: movsbl %cl,%edx #0open_vcdiff::VCDiffCodeTableReader::GetNextInstruction (this=0xf9061200, size=0x5, mode=0xf9ae8a78 " \340\203{Ox\a\376\001") at ../../sdch/open-vcdiff/src/decodetable.cc:78 #10xf1ddcab5 in open_vcdiff::VCDiffDeltaFileWindow::DecodeBody (this=0xf90611c4, parseable_chunk=<optimized out>) at ../../sdch/open-vcdiff/src/vcdecoder.cc:1231 #20xf1ddbc8b in open_vcdiff::VCDiffDeltaFileWindow::DecodeWindow (this=0xf90611c4, parseable_chunk=0xe75031a8) at ../../sdch/open-vcdiff/src/vcdecoder.cc:1359 #30xf1ddb6f0 in open_vcdiff::VCDiffStreamingDecoderImpl::DecodeChunk (this=0xf90611b0, data=<optimized out>, len=<optimized out>, output_string=0x8) at ../../sdch/open-vcdiff/src/vcdecoder.cc:887 #40xf1ddd499 in open_vcdiff::VCDiffStreamingDecoder::DecodeChunkToInterface (this=0x8b, data=0xe7503300 "8\026B\367\030'\317", <incomplete sequence \371\226>, len=3880792832, output_string=0xf76597a0 <_GLOBAL_OFFSET_TABLE_>) at ../../sdch/open-vcdiff/src/vcdecoder.cc:1393 #50xf1d2b17f in DecodeChunk<std::basic_string<char> > (this=0x7b83e020, data=<optimized out>, len=3880791363, output=<optimized out>) at ../../sdch/open-vcdiff/src/google/vcdecoder.h:83 #6net::SdchFilter::ReadFilteredData (this=0xf9cf26e0, dest_buffer=0xd2ce0000 "", dest_len=<optimized out>) at ../../net/filter/sdch_filter.cc:424 #70xf1d28990 in net::Filter::ReadData (this=0xf9cf26e0, dest_buffer=0x7b83e020 <error: Cannot access memory at address 0x7b83e020>, dest_len=0xe75033c8) at ../../net/filter/filter.cc:131 #80xf1d2895c in net::Filter::ReadData (this=0xfd6b7c00, dest_buffer=<optimized out>, dest_len=0xe75033c8) at ../../net/filter/filter.cc:145 #90xf1ca8dde in net::URLRequestJob::ReadFilteredData (this=0xf9891a00, bytes_read=<optimized out>) at ../../net/url_request/url_request_job.cc:673 #10 0xf1ca8c1d in net::URLRequestJob::Read (this=0xf9891a00, buf=<optimized out>, buf_size=<optimized out>, bytes_read=0xe75034fc) at ../../net/url_request/url_request_job.cc:126 Proof of Concept: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/38763.zip |