Last Comment Bug 443299 - (CVE-2008-0017) Investigate possible buffer overflow in nsDirIndexParser
: Investigate possible buffer overflow in nsDirIndexParser
: fixed1.8.1.18, fixed1.9.1, verified1.9.0.4
Product: Core
Classification: Components
Component: XPCOM (show other bugs)
: unspecified
: All All
P1 normal (vote)
: ---
Assigned To: Justin Schuh
: Nathan Froyd [:froydnj]
Depends on: CVE-2011-0070
  Show dependency treegraph
Reported: 2008-07-02 17:31 PDT by Brandon Sterne (:bsterne)
Modified: 2011-05-20 12:49 PDT (History)
10 users (show)
benjamin: blocking1.9.1+
dveditz: blocking1.9.0.4+
dveditz: blocking1.8.1.18+
dveditz: wanted1.8.1.x+
See Also:
Crash Signature:
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---

IBM advisory (1.64 KB, text/plain)
2008-07-02 17:31 PDT, Brandon Sterne (:bsterne)
no flags Details
crash script (1.06 KB, text/plain)
2008-08-14 11:14 PDT, Justin Schuh
no flags Details
Path to limit columns and catch NULL deref (1.32 KB, patch)
2008-08-27 13:02 PDT, Justin Schuh
cbiesinger: review-
dveditz: superreview+
Details | Diff | Splinter Review
updated to comments (1.32 KB, patch)
2008-10-20 18:48 PDT, Daniel Veditz [:dveditz]
cbiesinger: review+
dveditz: approval1.8.1.18+
dveditz: approval1.9.0.4+
Details | Diff | Splinter Review

Description User image Brandon Sterne (:bsterne) 2008-07-02 17:31:55 PDT
Created attachment 327894 [details]
IBM advisory

Justin Schuh of the IBM X-Force reported the following issue via security@m.o.

The issue appears to require specially-formatted HTTP response headers, so I will send email to the reporter to see if they have a PoC CGI they can provide.

From the advisory:

The http-index-format parser does not check for an allocation failure on a format array. The resulting array is then terminated with a sentinel value of 0xFFFFFFFF. A malicious party can exploit this issue to execute arbitrary code by overwriting a sensitive memory location (such as a buffer length or boolean variable).

When handling a content type of "application/http-index-format", a specially crafted 200 header line in an HTTP index response can result in an exploitable memory corruption condition. The following code fails to check for a NULL pointer returned from a new [] statement (and no exception is thrown). 

lines 203-204

  mFormat = new int[num+1];
  mFormat[num] = -1;

The resulting pointer is then dereferenced by four times the number of tokens provided in the header line, and a 32-bit integer value of -1 (0xFFFFFFFF) is assigned to the dereferenced location.

By manipulating the currently available memory (such as through Javascript) a malicious party can reliably exploit this issue to overwrite an arbitrary memory location with the 4-byte value 0xFFFFFFFF.
Comment 1 User image Brandon Sterne (:bsterne) 2008-07-02 17:32:54 PDT
Sent email to reporter requesting proof-of-concept CGI for exploit.
Comment 2 User image Brandon Sterne (:bsterne) 2008-08-14 09:47:08 PDT
Reporter sent additional exploit notes but isn't able to send CGI code for PoC due to his company's internal policies.  From his email:

General Exploitation
The exploit runs against Firefox on Windows, and takes advantage of a 4-byte write of 0xFFFFFFFF to an arbitrary (attacker selectable) location by dereferencing a NULL pointer. The exploit occurs in multiple stages in which memory is fragmented, a buffer length is overwritten (nsHTMLTags::sMaxTagNameLength at 0xB45AD0), and a group of function
pointers are then overwritten past the buffer's intended bounds.

Stage 1
This stage fragments memory in the browser by using JavaScript to create a large number of canvas elements set to consume roughly 0xB419E8 bytes. The canvas element was chosen because it is initialized reasonably quickly to a controllable size and does not result in a crash on allocation failure.

Stage 2
This stage delivers a application/http-index-format content body containing a single 200 header line with exactly 0x2D16B4 space-delimited, single-character tokens. In the interest of efficiency, this content is delivered gzip-compressed, resulting in a 5778-byte buffer.

If the fragmentation is successful this will result in a failed allocation of nsDirIndexParser::mFormat and the value 0xFFFFFFFF being written to the nsHTMLTags::sMaxTagNameLength at address 0xB45AD0. Because the token string matches no standard tokens, the token buffer nsDirIndexParser::mFormat is never written to after being terminated.

Further examination will likely reveal other exploit methods (particularly if common plugins are considered). However, this location is adequate for demonstration purposes and has been shown to produce a 100% reliable code execution exploit.

Stage 3
This stage contains a malformed HTML tag to write beyond the bounds of the static variable buf (address 0xB45CA8) in nsHTMLTags::LookupTag(). The exploit buffer overwrites several function pointers and replaces them with a pointer to the shellcode stub. Current tests have shown that the overwritten value previously containing uxtheme.OpenThemeDat () is called immediately after the overwrite occurs.

Stage 3 always proceeds regardless of whether stage 2 succeeded or not. If code execution does not result from this stage, available memory is altered and stage 2 is repeated.
Comment 3 User image Justin Schuh 2008-08-14 11:14:26 PDT
Created attachment 333792 [details]
crash script

The the obvious fix is to change line 203  in nsDirIndexParser::ParseFormat() to check for an allocation failure with something like this:

  if ((mFormat = new int[num+1]) == NULL) return NS_ERROR_OUT_OF_MEMORY;

As for triggering the bug, that's a little more annoying and the impact is OS and runtime dependent. You also have to force an allocation failure, which is why the proof of concept exploit is a bit complicated. So, I'm attaching a python script (not the exploit script) that should crash Firefox on Windows, but the crash is very slow and will consume over a gig of memory in the process.

An offending content body will look something like what I've shown below, but with a much longer string of "x x x" tokens (enough that the new call will fail). The address at the tokens * 4 is then overwritten with 0xFFFFFFFF. Also, the script gzips the content to save some time (and even then it's intolerably slow).

Response Content Body:

HTTP/1.0 200 OK
Content-Type: application/http-index-format

200: x x x x x x x x x x x x x x x
Comment 4 User image Justin Schuh 2008-08-26 15:21:54 PDT
I just realized that this bug is mislabeled. It affects all hardware/OS/versions. In some combinations it's a code execution bug (Firefox 2.0 and below on Windows) and on some it's a crash bug (like Firefox 3.0 on Windows). Whether it's code execution or crash depends primarily on if the new call throws an unhandled exception (which Firefox 3.0 does). After that, exploitation is a matter of finding a reasonable address to overwrite.
Comment 5 User image Justin Schuh 2008-08-27 13:02:52 PDT
Created attachment 335756 [details] [diff] [review]
Path to limit columns and catch NULL deref

This prevents the vulnerability both by checking for an excessive number of column headers and identifying a failed allocation. 

Checking the column headers isn't necessary to fix the bug, but there's no good reason to allow an arbitrary number of columns given the spec at
Comment 6 User image Daniel Veditz [:dveditz] 2008-09-29 11:32:38 PDT
Assigning to Justin since he's supplied the patch, but we'll find someone to check it in for you
Comment 7 User image Daniel Veditz [:dveditz] 2008-10-18 00:41:31 PDT
Comment on attachment 335756 [details] [diff] [review]
Path to limit columns and catch NULL deref


Looks good to me, but we need a network peer to also OK this.

Looks like the original code should have had "if(!*pos) break;" before incrementing num, but it doesn't really hurt to allocate one too many if the format ends with extra spaces. Irrelevant to the security problem.
Comment 8 User image Christian :Biesinger (don't email me, ping me on IRC) 2008-10-20 10:49:08 PDT
Comment on attachment 335756 [details] [diff] [review]
Path to limit columns and catch NULL deref

+    // There are a maximum of six allowed header fields (doubled + terminator, just in case)

please limit your lines to 80 characters

+    if (num > (2*(sizeof(gFieldTable) / sizeof(gFieldTable[0]))))

spaces around the * operator, please. and use NS_ARRAY_LENGTH to get the size of the array


no tabs please
Comment 9 User image Daniel Veditz [:dveditz] 2008-10-20 18:48:18 PDT
Created attachment 344000 [details] [diff] [review]
updated to comments
Comment 10 User image Daniel Veditz [:dveditz] 2008-10-20 18:50:08 PDT
Christian: did you have substantive complaints about the patch? I've addressed your comments, please re-review so we can get this in for the code-freeze.
Comment 11 User image Daniel Veditz [:dveditz] 2008-10-22 14:54:42 PDT
Comment on attachment 344000 [details] [diff] [review]
updated to comments

Approved for and, a=dveditz for release-drivers
Comment 12 User image Daniel Veditz [:dveditz] 2008-10-24 16:50:03 PDT
Fix checked into trunk
Comment 13 User image Daniel Veditz [:dveditz] 2008-10-24 17:22:31 PDT
Fix checked into the 1.8 and 1.9.0 branches
Comment 14 User image Al Billings [:abillings] 2008-10-27 16:34:13 PDT
So, reading above, we don't have a repro case because we didn't get a PoC cgi. QA would like to know if there is a way to verify this fix works.
Comment 15 User image Al Billings [:abillings] 2008-10-28 17:47:07 PDT
Never mind, I see that the python file acts as a server for the creating the bug conditions.  Script says:

localhost - - [28/Oct/2008 17:18:42] "GET / HTTP/1.1" 200 -
388877 compressed bytes served

when running and a connection is made to it via

Running with 3.0.3, Firefox peaks memory usage at around 490 MB and pegs my CPU at 100% while doing it. After about 15 minutes, it recovers and Firefox is usable again. No crash is evident...

Running with last night's nightly on the same virtual machine (Ubuntu 8.04), I'm seeing the exact same behavior with no difference at all. Comment 4 states that with Firefox 3, I should get a crash when I try this in 3.0.3. 

I'm going to try this on Windows as well but I need to set up an environment to do so.
Comment 16 User image Al Billings [:abillings] 2008-10-29 17:33:54 PDT
Interesting. Testing on Windows XP, the same test crashed Firefox 3.0.3. With the nightly with the fix (Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv: Gecko/2008102905 GranParadiso/3.0.4pre), the memory usage rose but it eventually recovered with no crash. Marking verified for
Comment 17 User image Al Billings [:abillings] 2008-10-29 18:00:08 PDT
Testing with Firefox on Windows XP, I get up to about 420 MB of RAM usage (and 99% cpu) before it drops back to 28 MB of RAM and no cpu usage, bringing up a page. I cannot get to crash with this.
Comment 18 User image Justin Schuh 2008-10-29 19:47:51 PDT
(In reply to comment #17)
> Testing with Firefox on Windows XP, I get up to about 420 MB of RAM
> usage (and 99% cpu) before it drops back to 28 MB of RAM and no cpu usage,
> bringing up a page. I cannot get to crash with this.

Try editing the python script and changing the value 800000000 on the commented line to something bigger like 1000000000 or 1200000000. Or, if the test system is low on memory, try changing it to something smaller like 500000000. The proof-of-concept exploit used JavaScript to dynamically set up the memory space since that varies between installations.
Comment 19 User image Al Billings [:abillings] 2008-10-30 18:35:05 PDT
So, I've tried this a bunch of different times with on XP, even dropping the available RAM in the virtual machine down to 128 MB to constrain things. While I can get 3.0.3 to crash, nothing ever happens with other than it becoming very busy for a while

Has anyone actually seen this crash or earlier?
Comment 20 User image Alexander Sack 2008-11-10 09:14:49 PST
Comment on attachment 344000 [details] [diff] [review]
updated to comments

a=asac for 1.8.0 branch
Comment 21 User image Al Billings [:abillings] 2008-11-10 15:25:32 PST
I'm unable to verify this fix in because of the inability to replicate the crash on a 1.8.1.x build.
Comment 22 User image georgi - hopefully not receiving bugspam 2008-11-13 01:20:41 PST
the patch is not nice:

206 mFormat = new int[num+1];
207 // Prevent NULL Deref - Bug 443299 
208 if (mFormat == nsnull)

operator |new| throws exception on OOM, it doesn't return 0 as demonstrated by this proggie:

int *mFormat;
mFormat=new int[-2000];
if (mFormat == 0)
	cout << "==" << endl;
return 0;

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Comment 23 User image timeless 2008-11-29 16:17:47 PST
georgi: this isn't the case with gecko 1.8 on windows, the toolchain we use is msvc6 where new returns null instead of throwing.

handling oom by null checking is the way all of our code expects to work. until we've changed things, we should continue to ensure we have null checks.
Comment 24 User image Benjamin Smedberg [:bsmedberg] 2009-01-27 17:44:02 PST
This landed before 1.9.1 branched

Note You need to log in before you can comment on or make changes to this bug.