Bug 223988 - heap overflow in libogg when trying to play a corrupt stream
Summary: heap overflow in libogg when trying to play a corrupt stream
Keywords:
Status: CLOSED WONTFIX
Alias: None
Product: Fedora
Classification: Fedora
Component: libogg
Version: 6
Hardware: All
OS: Linux
high
medium
Target Milestone: ---
Assignee: Behdad Esfahbod
QA Contact:
URL: http://sam.zoy.org/zzuf/
Whiteboard: impact=moderate,source=debian,public=...
Depends On:
Blocks: 223990
TreeView+ depends on / blocked
 
Reported: 2007-01-23 14:39 UTC by Lubomir Kundrak
Modified: 2007-11-30 22:11 UTC (History)
2 users (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Environment:
Last Closed: 2007-10-11 12:23:28 UTC
Type: ---
Embargoed:


Attachments (Terms of Use)
libogg ogg_stream_pagein() heap corruption reproducer (15.40 KB, application/ogg)
2007-01-23 14:39 UTC, Lubomir Kundrak
no flags Details

Description Lubomir Kundrak 2007-01-23 14:39:30 UTC
Description of problem:

Problem is triggered with fuzzed .ogg file, that was created with zzuf
(http://sam.zoy.org/zzuf/). Let us see what happens:

(gdb) $ gdb totem
GNU gdb Red Hat Linux (6.5-15.fc6rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu"...(no debugging symbols found)
Using host libthread_db library "/lib64/libthread_db.so.1".

(gdb) break ogg_stream_pagein
Function "ogg_stream_pagein" not defined.
Breakpoint 1 (ogg_stream_pagein) pending.
(gdb) run lol-gstreamer.ogg
[Thread debugging using libthread_db enabled]
[New Thread 46912496390112 (LWP 28628)]
[New Thread 1084229952 (LWP 28632)]

** (totem:28628): CRITICAL **: bacon_video_widget_can_direct_seek: assertion
`bvw != NULL' failed

** (totem:28628): CRITICAL **: bacon_video_widget_can_direct_seek: assertion
`bvw != NULL' failed
[New Thread 1094719808 (LWP 28633)]
Breakpoint 2 at 0x2aaab5a04b76: file framing.c, line 674.
Pending breakpoint "ogg_stream_pagein" resolved
[New Thread 1105209664 (LWP 28634)]
[Switching to Thread 1105209664 (LWP 28634)]

Breakpoint 2, ogg_stream_pagein (os=0xbe0218, og=0x41e01f10) at framing.c:674
674       unsigned char *header=og->header;
(gdb) c

Breakpoint 2, ogg_stream_pagein (os=0xbe0218, og=0x41e01f10) at framing.c:674
674       unsigned char *header=og->header;
(gdb) until 736
ogg_stream_pagein (os=0xbe0218, og=0x41e01f10) at framing.c:739
739       if(continued){
(gdb) print bodysize
$1 = 30
(gdb) l
734         }
735       }
736
737       /* are we a 'continued packet' page?  If so, we may need to skip
738          some segments */
739       if(continued){
740         if(os->lacing_fill<1 ||
741            os->lacing_vals[os->lacing_fill-1]==0x400){
742           bos=0;
743           for(;segptr<segments;segptr++){
(gdb)
744             int val=header[27+segptr];
745             body+=val;
746             bodysize-=val;
747             if(val<255){
748               segptr++;
749               break;
750             }
751           }
752         }
753       }
(gdb)
754
755       if(bodysize){
756         _os_body_expand(os,bodysize);
757         memcpy(os->body_data+os->body_fill,body,bodysize);
758         os->body_fill+=bodysize;
759       }
760
761       {
762         int saved=-1;
763         while(segptr<segments){
(gdb) until 755
ogg_stream_pagein (os=0xbe0218, og=0x41e01f10) at framing.c:755
755       if(bodysize){
(gdb) print bodysize
$2 = -57
(gdb) c

Program received signal SIGSEGV, Segmentation fault.
0x00002aaab5a04f16 in ogg_stream_pagein (os=0xbe0218, og=0x41e01f10) at
framing.c:757
757         memcpy(os->body_data+os->body_fill,body,bodysize);
(gdb) print (size_t)bodysize
$3 = 18446744073709551559
(gdb)

At line 741 signed bodysize gets negative, but as memcpy on line 757
expects unsigned size_t, it tries to copy data from far beyond the
buffer boundary resulting in a heap corruption and out-of-bound read
that triggers a segmentation violation.

I can not say whether this could be exploited, but hardly with this
big bodysize. I am no libogg expert and can't say how much can the
bodysize be decremented. Just in case it could be modified to some more
sensible value, the heap overflow could result in arbitrary code execution.

Version-Release number of selected component (if applicable):

libogg-1.1.3-2

How reproducible:

Allways.

Steps to Reproduce:
1. Open the fuzzed file with the player that utilizes libogg.
Totem could be an example.
The file is attached to this report.
  
Actual results:

The player receives SIGSEGV.

Expected results:

We expected it to receive SIGSEGV, didn't we? :)
Probably it should complain loudly instead.

Additional info:

Today it snowed for the first time this year in Brno.

Comment 1 Lubomir Kundrak 2007-01-23 14:39:31 UTC
Created attachment 146299 [details]
libogg ogg_stream_pagein() heap corruption reproducer

Comment 2 Lubomir Kundrak 2007-01-23 14:43:15 UTC
Here's debian bug. They claim to have that fixed in gstreamer.
Weird. I still believe this is an libogg bug.
Adding ajackson to CC:
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=407004

Comment 4 Josh Bressers 2007-02-01 22:02:26 UTC
I'm spent quite a bit of time on this flaw, it should not be considered a
security flaw.  The problem here is that the memcpy causes a heap overflow, but
the heap data which gets written is not controllable by an attacker.  The input
buffer (os->body_data+os->body_fill) is of considerably larger size than the
output buffer, which means random memory is written onto the heap, causing the
crash.

If someone believes my analysis to be in error, please restore the Security keyword.

Comment 5 Behdad Esfahbod 2007-02-02 03:31:45 UTC
I came up with a similar analysis.  Sent it to Lubomir but seems like he forgot
to paste here.  Anyway, here it is:

First, the fix in gstreamer makes sense, because apparently the crash is
caused by an invalid input stream to libogg.  So that's one fix.

The crash is another bug, in libogg.  The problem is that libogg doesn't
seem to handle errors gracefully.  So, while we can patch this one place
to bail out if bodysize becomes negative, there are probably many many
other places that can cause a crash with an invalid input stream.

As for exploitablity, I don't think it's an issue, because: 1) val can
be at most 255, so you need quite a lot of bytes in the header to bring
bodysize down to a reasonable overflow size.  2) In Fedora, our heap is
not executable.  3) Before the memcpy, it does a _os_body_expand which
in turn does a realloc().  So, it either crashes in realloc() failing to
allocate memory, or it will allocate the buffer and killed by kernel OOM
handler when copying data over.  If it survives the copy and does not
crash, the buffer is legitimately allocated memory.  So, no overflows
here.


Comment 6 Lubomir Kundrak 2007-09-21 11:01:34 UTC
No security impact, I do not insist on fixing, I'll leave it open though. Feel
free to WONTFIX it.

Comment 7 Hans de Goede 2007-10-11 12:23:28 UTC
On request of Behdad, I'm helping out with ogg / vorbis / theora bugs.

Closing this as WONTFIX as suggested.



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