-------------------------------------------------------------- Security Report : bad handling of Out of Memory condition leads to controlled memory overwrite in Mozilla's string handling functions. by Gael Delalleau (sept. 2004) -------------------------------------------------------------- Overview -------- This report presents the results of a security audit of a part of the Mozilla 1.7.3 C++ source code tree. Reported bugs affect Mozilla, Firefox, and are likely to affect works based on Mozilla like the Netscape 7 browser, Galeon, etc. I found that the nsTSubstring functions incorrectly handle an Out of Memory condition. As a result an overwrite of large parts of the memory of the process with arbitrary data can be achieved. It can lead either to a crash of the browser or to arbitrary code execution with the privileges of the user running Mozilla. Source code filename -------------------- mozilla/xpcom/string/src/nsTSubstring.cpp Technical Details ----------------- The bug lies in nsTSubstring_CharT::Replace and other similar functions. These functions are called from other string functions, for instance nsCSubstring::Append. Let's take an example of a vulnerable function. In mozilla/xpcom/string/src/nsTSubstring.cpp we have : 423 size_type length = tuple.Length(); <-- length of the string we want to copy 424 425 cutStart = PR_MIN(cutStart, Length()); 426 427 ReplacePrep(cutStart, cutLength, length); <-- this function is supposed to resize the string buffer. However it can fails if not enough memory is available. But there is no error check here, the code assumes the function will always succeed ! 428 429 if (length > 0) 430 tuple.WriteTo(mData + cutStart, length); <-- buffer overflow if the previous call to ReplacePrep failed This is not a standard heap overflow though, because when it fails to allocate memory, the ReplacePrep function "nullifies" the string : mData = NS_CONST_CAST(char_type*, char_traits::sEmptyBuffer); mLength = 0; Thus, the data which is copied at line 430 actually overwrites memory starting from the address of char_traits::sEmptyBuffer which is in the .rodata section (on Linux). The attacker is then able to overwrite the memory of the following sections, including .data, .bss and the heap memory. Because these areas of memory contain functions pointers, it has been found that a "blind" overwrite of this memory with AAAAA... is usually enough to take control of the EIP register. Consequences: By overwriting data, control structures or function pointers on the heap or in other writable memory areas within reach, an attacker can achieve arbitrary code execution. Exploitation ------------ It is likely that there are multiple exploitation vectors, because the string functions affected are widely used in Mozilla code. I verified that exploitation is possible by a rogue web server who sends malicious HTTP headers after having consumed all the memory available to the Mozilla process. Memory consumption can be achieved in different ways, for instance using a "gzip bomb" or a "GIF bomb". For the sake of simplicity, the Proof of Concept exploit that I wrote consumes memory by sending a large number of HTTP headers. The python script 'crash_bigmem.py' shows that exploitation is possible. It opens a web server on port localhost:8000 and feeds a large number of HTTP headers to the browser connecting to it. The text file 'controlled_eip.txt' shows the EIP register of Mozilla and Firefox processes jumping to 0x41414141 after successful exploitation with this script. I verified exploitation is possible on a Linux 2.4 system with 3.5 Gb of memory. I also verified exploitation on a system with 512 Mb memory where the Mozilla process was given a virtual memory limit. Further research would be needed to check whether or not exploitation is possible on systems where Mozilla is able to consume all the memory of the system, but I have some reasons to think it would be exploitable (mainly the fact that it is not necessary to consume all memory, it is only necessary to make the ReplacePrep function fail, which is possible by trying to Append a string larger than the available memory). - End of Security Report - Gael Delalleau