Closed Bug 675747 (CVE-2011-3005) Opened 13 years ago Closed 13 years ago

ogg crash [@ nsOggReader::ReadHeaders]

Categories

(Core :: Audio/Video, defect)

5 Branch
defect
Not set
critical

Tracking

()

VERIFIED FIXED
mozilla8
Tracking Status
firefox5 - wontfix
firefox6 - wontfix
firefox7 + fixed
firefox8 + fixed
status1.9.2 --- unaffected
status1.9.1 --- unaffected

People

(Reporter: sczimmer, Assigned: cpearce)

Details

(5 keywords, Whiteboard: [sg:critical?][qa!])

Crash Data

Attachments

(4 files, 1 obsolete file)

Attached video example.ogg
User Agent: Mozilla/5.0 (X11; U; Linux x86_64; c) AppleWebKit/531.2+ (KHTML, like Gecko) Safari/531.2+ Epiphany/2.30.6

Steps to reproduce:

ran firefox on an .ogg file

it seems like firefox crashes on any ogg file where the beginning of stream flag is set in the second page of a stream, i attached one example


Actual results:

firefox seg faults


Expected results:

firefox should just not play the video like it normally does for corrupted videos rather than seg faulting
Component: General → Video/Audio
Product: Firefox → Core
QA Contact: general → video.audio
Status: UNCONFIRMED → NEW
Crash Signature: [@ nsOggReader::ReadHeaders]
Ever confirmed: true
Keywords: crash, testcase
Summary: ogg crash → ogg crash [@ nsOggReader::ReadHeaders]
Whiteboard: [sg:critical?]
Attached file stack
Stack from a recent debug build on Linux64.
Severity: normal → critical
Assignee: nobody → chris
Status: NEW → ASSIGNED
OS: Linux → All
Hardware: x86_64 → All
Attached patch Patch v1 (obsolete) — Splinter Review
The problem is that we're creating a new nsOggCodecState for every bos page we encounter in the stream, and storing a mapping from stream serialno to the nsOggCodecState in the mCodecStates hash table. There are two bos pages for the Vorbis stream. When we create the second nsOggCodecState and add it to mCodecState, we overwrite the original seiral-to-nsOggCodecState mapping in mCoedcStates. mCodecStates holds the owning reference, so the memory pointed to be mVorbisState is then cleared, and when we try to use mVorbisState at nsOggReader.cpp:288 we crash.

So I propose we simply don't create a new nsOggCodecState for serialnos we've seen before in bos pages. The media plays correctly then.
Attachment #549965 - Flags: review?(chris.double)
Comment on attachment 549965 [details] [diff] [review]
Patch v1

Review of attachment 549965 [details] [diff] [review]:
-----------------------------------------------------------------

Can you add a test? r- for the main thread/decode thread issue.

::: content/media/ogg/nsOggReader.cpp
@@ +195,5 @@
>      nsOggCodecState* codecState = 0;
>  
> +    if (!ogg_page_bos(&page)) {
> +      // We've encountered the a non Beginning Of Stream page. No more
> +      // BOS pages can follow in this Ogg segment, so there will be no other

The first sentence in the comment seems grammatically incorrect. Should the 'the' be there?

@@ +198,5 @@
> +      // We've encountered the a non Beginning Of Stream page. No more
> +      // BOS pages can follow in this Ogg segment, so there will be no other
> +      // bitstreams in the Ogg (unless it's invalid).
> +      readAllBOS = PR_TRUE;
> +    } else if (!IsKnownStream(ogg_page_serialno(&page))) {

The comment for IsKnownStream says this can be called on the main thread only, but this call is inside ReadMetadata which is called on the decode thread.
Attachment #549965 - Flags: review?(chris.double) → review-
Attached patch Patch v2Splinter Review
Add test and use the decode-thread hash table instead of IsKnownStream.
Attachment #549965 - Attachment is obsolete: true
Attachment #550507 - Flags: review?(chris.double)
Attachment #550507 - Flags: review?(chris.double) → review+
Chris, I think we're fairly well done with 6 by now, so I won't be tracking this for 6, but if you feel it's critical that we get this into 6 then please nominate the patch for beta approval.
Ok, cool. This isn't exploitable, so it's not a big deal if we don't get this onto beta at this stage. I'll request aurora approval once it's stuck on central.

Inbound:
http://hg.mozilla.org/integration/mozilla-inbound/rev/f824fbd4b94c
Whiteboard: [sg:critical?] → [sg:critical?][inbound]
I believe this is exploitable, in most files that trigger the crash the free'd memory is not given to anything, but in the example.ogg I attached, for some reason the free'd mVorbisState buffer is then always allocated in the theora decoder at huffdec.c:399 "tree=(ogg_int16_t *)_ogg_malloc(size*sizeof(*tree));"

in particular what is stored in the important part of the buffer is "leaf=(ogg_int16_t)-(_tokens[ti][1]-depth[l]<<8|_tokens[ti][0]);" in example.ogg leaf is 0xfbfc and when I run the normal linux 32 bit binary from the mozilla download page it crashes with instruction pointer = this address:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xe56ffb70 (LWP 5514)]
0xfbfcfbfc in ?? ()

I believe _tokens comes from the video file so we should have some control over the value. I don't think its even necessary to have complete control over the value, just make it something a bit less than 0xfbfcfbfc and you can do a standard javascript heap spray
(In reply to comment #7)
> I believe this is exploitable, in most files that trigger the crash the
> free'd memory is not given to anything, but in the example.ogg I attached,
> for some reason the free'd mVorbisState buffer is then always allocated in
> the theora decoder at huffdec.c:399 "tree=(ogg_int16_t
> *)_ogg_malloc(size*sizeof(*tree));"

Just so I understand this, are you saying that exploit happens if we don't crash in ReaderHeaders()? The memory pointed to by mVorbisState gets reallocated in the Theora decoder setup, and then we overwrite the Theora decode info when we init mVorbisState and then the exploit happens when we use the Theora decode info later to to decode some Theora data pages subsequently?

That sounds... plausible to me...

CCing Tim Terriberry, libtheora maintainer, in case he has any insights or can convince me otherwise...
(In reply to comment #8)
> CCing Tim Terriberry, libtheora maintainer, in case he has any insights or
> can convince me otherwise...

The attacker control of that value is somewhat limited: the smallest non-zero value you can get is 0xf8e1f8e1. Bytes 0 and 2 will always be either between 0xe1 and 0xff, or 0x00 (_tokens[ti][0] will be between 0 and 31, inclusive), and bytes 1 and 3 will always be between 0xf8 and 0xff, or 0x00 (nbits will always be between 1 and 7, inclusive, and _tokens[ti][1]-depth[l] is <= nbits). The only way to get _tokens[ti][1]==depth[l] (i.e., to get 0x00 in one of the odd bytes) is with a singleton tree, in which case you will write exactly 32 bits into the buffer, both odd bytes will be 0x00, and both even bytes will also have to be 0x00 (or carry propagation would make the odd bytes 0xff). So the only possible value less than 0xf8e1f8e1 is 0x00000000.

But I don't know enough to say that that would make this unexploitable.
To clarify,
1. mVorbisState is free'd
2. theora mallocs some memory and recieves the same location mVorbisState was
3. theora modifies the memory, which you have some control over as tim describes above. If you can only make the value > 0xf8e1f8e1 it should still be fine, just a bit less convenient as you will need to place shellcode in the stack instead of the heap
4. mVorbisState->Init() is called. I do not know c++ internals, but apparently that equals the assembly instruction call [rax+0x30] where rax comes from the mVorbisState buffer and is the previously mentioned value from theora we have some control over

This is in 64bit linux firefox 5, the "recent linux64 debug build" above looks like it crashes in a slightly different place

So 64 bit linux crashes due to a read violation on the instruction call [0xfbfcfbfcfbfcfbfc + 0x30], but on 32 bit linux (I am not sure why) it actually crashes with the instruction pointer at address $eip = 0xfbfcfbfc

Of course the only real way to show it's exploitable is with an actual exploit. I'm busy this weekend but will try to write one next week.
Fixed on central.
http://hg.mozilla.org/mozilla-central/rev/f824fbd4b94c
Status: ASSIGNED → RESOLVED
Closed: 13 years ago
Resolution: --- → FIXED
Whiteboard: [sg:critical?][inbound] → [sg:critical?]
Target Milestone: --- → mozilla8
Comment on attachment 550507 [details] [diff] [review]
Patch v2

Requesting aurora approval. Still not sure if this is exploitable, so haven't requested beta approval.
Attachment #550507 - Flags: approval-mozilla-aurora?
Comment on attachment 550507 [details] [diff] [review]
Patch v2

Land this on aurora this week please, before the migration
Attachment #550507 - Flags: approval-mozilla-aurora? → approval-mozilla-aurora+
Attachment #549931 - Attachment mime type: application/octet-stream → video/ogg
fwiw a slightly different (similar) crash with Fx6 Beta:
bp-0f68a896-93e8-426a-9695-a29062110810
Crash Signature: [@ nsOggReader::ReadHeaders] → [@ nsOggReader::ReadHeaders] [@ nsDeque::PopFront ]
This is a regression from something, Firefox 3.6.x is not affected by the testcase and the code being patched doesn't exist AFAICT.
I finally wrote an exploit for this bug. Download the attached exploit.html and exploit.ogg in the same folder and visit exploit.html in firefox. I have only coded the exploit for 32 bit linux, and while it might work with random distro's firefox binary I have only tested it with the official mozilla 32 bit linux download firefox-5.0.1. Currently the exploit just prints out "hi!" but of course you can replace the code for printing "hi!" with something more malicious. It works about 90% of the time for me with address space layout randomization off and a bit under 50% of the time with address space layout randomization on though I'm sure that could be improved by someone better at exploit writing than me.
Comment on attachment 550507 [details] [diff] [review]
Patch v2

Someone more qualified than I needs to make a call on whether this exploit warrants respinning the beta/release-candidate builds, so nominating for beta approval.

* Exploit has only been demonstrated on 32bit Linux, and is less than 100% reproducibility, though higher may be possible.
* Exploit relies on the system malloc returning the memory allocated to a Vorbis decoder (which was incorrectly freed, but to which we still hold a pointer) when allocating a Theora decoder. The Vorbis decoder is initialized in that memory (using setup data read from an ogg file), which overwrites the Theora decoder's memory. When the Theora decoder decodes data, because its setup data is corrupted, the exploit happens.
* Not sure if this can happen on non-Linux platforms, it relies on system malloc behaviour (not jemalloc behaviour).
* Patch is landed in aurora, so if we don't take this on beta, the fix will reach users in 6 more weeks.
Attachment #550507 - Flags: approval-mozilla-beta?
Just to clarify a few things,
While what I said above about using the theora stuff might be possible but this exploit works a much simpler way.
The exploitable bug:
1. A buffer used by ogg decoder is freed.
2. Stuff happens.
3. the instruction call [$eax + 0x18] is executed where $eax is the first word from the freed buffer

Normally the freed memory is garbage so it simply crashes. But, in the exploit.html as the video is started I have looping in the background javascript that rapidly allocates strings of the same size as the buffer that is freed. Almost of all of the time, one of these strings receives the same location in memory as the buffer. We can put whatever we want in the string, so we can control the value of $eax at the instruction call [$eax+0x18], which is straightforward to exploit. I have tested this on windows xp 32 bit and it crashes with attacker control of $eax so it is certainly exploitable on windows as well, I just have a little experience writing exploits for linux and no experience with windows so I wrote it for linux.
Comment on attachment 550507 [details] [diff] [review]
Patch v2

We did not respin Firefox 6 for this and the fix is now already in the current "Beta" (Firefox 7) releases.
Attachment #550507 - Flags: approval-mozilla-beta?
qa+ for verification in Firefox 7 and 8
Whiteboard: [sg:critical?] → [sg:critical?][qa+]
Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0) Gecko/20100101 Firefox/7.0 ID:20110922153450
Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0a2) Gecko/20110926 Firefox/8.0a2 ID:20110926042011
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0) Gecko/20100101 Firefox/7.0 ID:20110922153450
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:8.0a2) Gecko/20110926 Firefox/8.0a2 ID:20110926042011
Mozilla/5.0 (X11; Linux x86_64; rv:7.0) Gecko/20100101 Firefox/7.0 ID:20110922153450
Mozilla/5.0 (X11; Linux x86_64; rv:8.0a2) Gecko/20110926 Firefox/8.0a2 ID:20110926042011

Verified fixed using the attached test video.
Status: RESOLVED → VERIFIED
Whiteboard: [sg:critical?][qa+] → [sg:critical?][qa!]
Alias: CVE-2011-3005
Group: core-security
Flags: sec-bounty+
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: