mpruett / audiofile

Audio File Library
https://audiofile.68k.org/
GNU Lesser General Public License v2.1
155 stars 42 forks source link

NULL pointer dereference bug in ulaw2linear_buf, in G711.cpp #54

Open cuanduo opened 5 years ago

cuanduo commented 5 years ago

There exists one NULL pointer dereference bug in ulaw2linear_buf, in G711.cpp, which allows an attacker to cause a denial of service via a crafted file. To reproduce with the attached poc file: ./sfconvert poc output format voc poc.zip

gdb output

[----------------------------------registers-----------------------------------]
RAX: 0xffff8284 
RBX: 0x0 
RCX: 0x7 
RDX: 0x7e00 ('')
RSI: 0x7d7c ('|}')
RDI: 0xffffffff 
RBP: 0x7fffebce2010 --> 0x0 
RSP: 0x7fffffffe2a0 --> 0x7ffff7b20ef6 (<afGetFrameCount(AFfilehandle, int)+390>:   mov    rax,QWORD PTR [rsp+0x10])
RIP: 0x7ffff7b388bf (<G711::runPull()+3199>:    mov    WORD PTR [rbx+r12*2],ax)
R8 : 0x0 
R9 : 0x55555576b648 --> 0x0 
R10: 0x55555576af48 --> 0x3e9 
R11: 0x246 
R12: 0x0 
R13: 0x0 
R14: 0x1 
R15: 0x55555576b120 --> 0x7ffff7dd3568 --> 0x7ffff7b39440 (<G711::~G711()>: lea    rsp,[rsp-0x98])
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff7b388ac <G711::runPull()+3180>:   lea    rsp,[rsp+0x98]
   0x7ffff7b388b4 <G711::runPull()+3188>:   movzx  edi,BYTE PTR [rbp+r12*1+0x0]
   0x7ffff7b388ba <G711::runPull()+3194>:   call   0x7ffff7b23370 <_af_ulaw2linear>
=> 0x7ffff7b388bf <G711::runPull()+3199>:   mov    WORD PTR [rbx+r12*2],ax
   0x7ffff7b388c4 <G711::runPull()+3204>:   add    r12,0x1
   0x7ffff7b388c8 <G711::runPull()+3208>:   cmp    QWORD PTR [rsp+0x10],r12
   0x7ffff7b388cd <G711::runPull()+3213>:   je     0x7ffff7b38348 <G711::runPull()+1800>
   0x7ffff7b388d3 <G711::runPull()+3219>:   nop
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe2a0 --> 0x7ffff7b20ef6 (<afGetFrameCount(AFfilehandle, int)+390>:  mov    rax,QWORD PTR [rsp+0x10])
0008| 0x7fffffffe2a8 --> 0x0 
0016| 0x7fffffffe2b0 --> 0x61616161 ('aaaa')
0024| 0x7fffffffe2b8 --> 0x7ffff7b1ae32 (<afReadFrames(AFfilehandle, int, void*, int)+34>:  mov    rax,QWORD PTR [rsp+0x10])
0032| 0x7fffffffe2c0 --> 0x0 
0040| 0x7fffffffe2c8 --> 0x1 
0048| 0x7fffffffe2d0 --> 0x55555576b360 --> 0x7fff00000003 
0056| 0x7fffffffe2d8 --> 0x55555576af48 --> 0x3e9 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007ffff7b388bf in ulaw2linear_buf (nsamples=<optimized out>, linear=<optimized out>, ulaw=<optimized out>) at G711.cpp:42
42          linear[i] = _af_ulaw2linear(ulaw[i]);
gdb-peda$ bt
#0  0x00007ffff7b388bf in ulaw2linear_buf (nsamples=<optimized out>, linear=<optimized out>, ulaw=<optimized out>) at G711.cpp:42
#1  G711::runPull (this=0x55555576b120) at G711.cpp:206
#2  0x00007ffff7b1b4b6 in afReadFrames (file=<optimized out>, trackid=<optimized out>, samples=0x0, nvframeswanted=<optimized out>) at data.cpp:222
#3  0x0000555555555f9e in copyaudiodata (infile=0x55555576ae90, outfile=0x55555576b6c0, trackid=0x3e9) at sfconvert.c:340
#4  0x00005555555555e1 in main (argc=argc@entry=0x5, argv=argv@entry=0x7fffffffe508) at sfconvert.c:248
#5  0x00007ffff76f3b97 in __libc_start_main (main=0x555555555370 <main>, argc=0x5, argv=0x7fffffffe508, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe4f8) at ../csu/libc-start.c:310
#6  0x0000555555555c3a in _start ()
cuanduo commented 5 years ago

caused by allocate too large mem, and then alloc return 0

asan output

root@ubuntu:~/audiofile-santi/sfcommands/.libs# ./sfconvert /home/tim/poc output format voc
==129695==WARNING: AddressSanitizer failed to allocate 0xffffffffc2c00000 bytes
==129695==AddressSanitizer's allocator is terminating the process instead of returning 0
==129695==If you don't like this behavior set allocator_may_return_null=1
==129695==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/sanitizer_common/sanitizer_allocator.cc:218 "((0)) != (0)" (0x0, 0x0)
    #0 0x7f48c8503c02  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe9c02)
    #1 0x7f48c8522595 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x108595)
    #2 0x7f48c8509342  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xef342)
    #3 0x7f48c8441e46  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x27e46)
    #4 0x7f48c84f8b1a in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb1a)
    #5 0x558dc209af68 in copyaudiodata /home/tim/audiofile-santi/sfcommands/sfconvert.c:327
    #6 0x558dc209a620 in main /home/tim/audiofile-santi/sfcommands/sfconvert.c:248
    #7 0x7f48c7d38b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #8 0x558dc209ac79 in _start (/home/tim/audiofile-santi/sfcommands/.libs/sfconvert+0x1c79)
carnil commented 3 years ago

CVE-2019-13147 was assigned for this issue.

asarubbo commented 9 months ago

For completeness, this is the stacktrace I get with the provided poc:

==6157==WARNING: AddressSanitizer failed to allocate 0xffffffff85858580 bytes
AddressSanitizer:DEADLYSIGNAL
=================================================================
==6157==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f68322200db bp 0x7f6824545800 sp 0x7ffd57f71660 T0)
==6157==The signal is caused by a WRITE memory access.
==6157==Hint: address points to the zero page.
    #0 0x7f68322200db in ulaw2linear_buf(unsigned char const*, short*, int) /var/tmp/portage/media-libs/audiofile-0.3.6-r5/work/audiofile-0.3.6/libaudiofile/modules/G711.cpp:42:13
    #1 0x7f68322200db in G711::runPull() /var/tmp/portage/media-libs/audiofile-0.3.6-r5/work/audiofile-0.3.6/libaudiofile/modules/G711.cpp:206:3
    #2 0x7f6832212ec1 in afReadFrames /var/tmp/portage/media-libs/audiofile-0.3.6-r5/work/audiofile-0.3.6/libaudiofile/data.cpp:222:14
    #3 0x4f42e1 in copyaudiodata /var/tmp/portage/media-libs/audiofile-0.3.6-r5/work/audiofile-0.3.6/sfcommands/sfconvert.c:370:29
    #4 0x4f3b57 in main /var/tmp/portage/media-libs/audiofile-0.3.6-r5/work/audiofile-0.3.6/sfcommands/sfconvert.c:275:17
    #5 0x7f6831f31676 in __libc_start_call_main /var/tmp/portage/sys-libs/glibc-2.37-r3/work/glibc-2.37/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #6 0x7f6831f31734 in __libc_start_main /var/tmp/portage/sys-libs/glibc-2.37-r3/work/glibc-2.37/csu/../csu/libc-start.c:360:3
    #7 0x41e780  (/usr/bin/sfconvert+0x41e780)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /var/tmp/portage/media-libs/audiofile-0.3.6-r5/work/audiofile-0.3.6/libaudiofile/modules/G711.cpp:42:13 in ulaw2linear_buf(unsigned char const*, short*, int)
==6157==ABORTING
Aborted

While the asan output complains about " failed to allocate" I confirm that under normal condition I get a Segmentation fault.

bastien-roucaries commented 7 months ago

Partial fix (symptom), nevertheless a fix:

commit 2f762fa2dd93fe2c66c59150d89ed15778274690
Author: Bastien Roucariès <rouca@debian.org>
Date:   Sat Nov 11 17:42:03 2023 +0000

    Partial fix of CVE-2019-13147

    This fix the symptom do not allow to allocate negative memory:
    ==129695==WARNING: AddressSanitizer failed to allocate 0xffffffffc2c00000 bytes
    ==129695==AddressSanitizer's allocator is terminating the process instead of returning 0
    ==129695==If you don't like this behavior set allocator_may_return_null=1
    ==129695==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/sanitizer_common/sanitizer_allocator.cc:218 "((0)) != (0)" (0x0, 0x0)
        #0 0x7f48c8503c02  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe9c02)
        #1 0x7f48c8522595 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x108595)
        #2 0x7f48c8509342  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xef342)
        #3 0x7f48c8441e46  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x27e46)
        #4 0x7f48c84f8b1a in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb1a)
        #5 0x558dc209af68 in copyaudiodata /home/tim/audiofile-santi/sfcommands/sfconvert.c:327
        #6 0x558dc209a620 in main /home/tim/audiofile-santi/sfcommands/sfconvert.c:248
        #7 0x7f48c7d38b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
        #8 0x558dc209ac79 in _start (/home/tim/audiofile-santi/sfcommands/.libs/sfconvert+0x1c79)

    If negative bail out

diff --git a/sfcommands/sfconvert.c b/sfcommands/sfconvert.c
index 367f7a5..400d485 100644
--- a/sfcommands/sfconvert.c
+++ b/sfcommands/sfconvert.c
@@ -349,7 +349,8 @@ void printversion (void)
 bool copyaudiodata (AFfilehandle infile, AFfilehandle outfile, int trackid)
 {
        int frameSize = afGetVirtualFrameSize(infile, trackid, 1);
-
+       if(frameSize <= 0)
+               return false;
        int kBufferFrameCount = 65536;
        int bufferSize;
        while (multiplyCheckOverflow(kBufferFrameCount, frameSize, &bufferSize))
bastien-roucaries commented 7 months ago

Real fix is too many channel:

commit 18e39112376f488bf57ca6527d42afc644f06a94 (HEAD -> patch-queue/master)
Author: Bastien Roucariès <rouca@debian.org>
Date:   Sat Nov 11 17:43:19 2023 +0000

    Partial fix of CVE-2019-13147

    This is the fix of the POC. Do not allow too many channel

    Now it fail with:
    Audio File Library: invalid file with 1633771873 channels [error 15]
    Could not open file 'poc' for reading.

diff --git a/libaudiofile/NeXT.cpp b/libaudiofile/NeXT.cpp
index c462dbe..01c967c 100644
--- a/libaudiofile/NeXT.cpp
+++ b/libaudiofile/NeXT.cpp
@@ -32,6 +32,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>

 #include "File.h"
 #include "Setup.h"
@@ -122,6 +123,12 @@ status NeXTFile::readInit(AFfilesetup setup)
                _af_error(AF_BAD_CHANNELS, "invalid file with 0 channels");
                return AF_FAIL;
        }
+       /* avoid overflow of INT for double size rate */
+       if (channelCount > (INT32_MAX / (sizeof(double))))
+       {
+               _af_error(AF_BAD_CHANNELS, "invalid file with %i channels", channelCount);
+               return AF_FAIL;
+       }

        Track *track = allocateTrack();
        if (!track)