radareorg / radare2-extras

Source graveyard and random candy for radare2
http://www.radare.org/
GNU Lesser General Public License v3.0
241 stars 116 forks source link

Stack buffer overflow of parsing swf #138

Open binjo opened 7 years ago

binjo commented 7 years ago

Hi,

When I play with r2 and the swf plugin, it crashes with a buffer overflow.

(lldb) run
Process 54371 launched: '/usr/local/bin/r2' (x86_64)
2017-11-09 08:40:51.921302+0800 r2[54371:19870295] detected buffer overflow
Process 54371 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x00007fff98965d42 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
->  0x7fff98965d42 <+10>: jae    0x7fff98965d4c            ; <+20>
    0x7fff98965d44 <+12>: movq   %rax, %rdi
    0x7fff98965d47 <+15>: jmp    0x7fff9895ecaf            ; cerror_nocancel
    0x7fff98965d4c <+20>: retq
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
  * frame #0: 0x00007fff98965d42 libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x00007fff98a53457 libsystem_pthread.dylib`pthread_kill + 90
    frame #2: 0x00007fff988cb420 libsystem_c.dylib`abort + 129
    frame #3: 0x00007fff988cb592 libsystem_c.dylib`abort_report_np + 181
    frame #4: 0x00007fff988f1f28 libsystem_c.dylib`__chk_fail + 48
    frame #5: 0x00007fff988f1ef8 libsystem_c.dylib`__chk_fail_overflow + 16
    frame #6: 0x00007fff988f1f8e libsystem_c.dylib`__memset_chk + 37
    frame #7: 0x0000000101a2de09 bin_swf.dylib`r_bin_swf_get_header + 169
    frame #8: 0x0000000101a2d9b7 bin_swf.dylib`entries + 119
    frame #9: 0x0000000100357d90 libr_bin.dylib`r_bin_object_set_items(binfile=0x0000000101c09ca0, o=0x0000000101b373a0) at bin.c:770
    frame #10: 0x000000010035aa35 libr_bin.dylib`r_bin_object_new(binfile=0x0000000101c09ca0, plugin=0x0000000101d00740, baseaddr=18446744073709551615, loadaddr=0, offset=0, sz=856) at bin.c:1377
    frame #11: 0x0000000100359ebc libr_bin.dylib`r_bin_file_new_from_bytes(bin=0x0000000101b09220, file="testfk.swf", bytes="CWS\x1b, sz=856, file_sz=856, rawstr=0, baseaddr=18446744073709551615, loadaddr=0, fd=3, pluginname=0x0000000000000000, xtrname=0x0000000000000000, offset=0, steal_ptr=true) at bin.c:1567
    frame #12: 0x00000001003598c4 libr_bin.dylib`r_bin_load_io_at_offset_as_sz(bin=0x0000000101b09220, fd=3, baseaddr=18446744073709551615, loadaddr=0, xtr_idx=0, offset=0, name=0x0000000000000000, sz=856) at bin.c:1118
    frame #13: 0x0000000100358ad6 libr_bin.dylib`r_bin_load_io_at_offset_as(bin=0x0000000101b09220, fd=3, baseaddr=18446744073709551615, loadaddr=0, xtr_idx=0, offset=0, name=0x0000000000000000) at bin.c:1132
    frame #14: 0x0000000100358970 libr_bin.dylib`r_bin_load_io(bin=0x0000000101b09220, fd=3, baseaddr=18446744073709551615, loadaddr=0, xtr_idx=0) at bin.c:1021
    frame #15: 0x0000000100196584 libr_core.dylib`r_core_file_do_load_for_io_plugin(r=0x00000001000084e0, baseaddr=18446744073709551615, loadaddr=0) at file.c:406
    frame #16: 0x000000010019507f libr_core.dylib`r_core_bin_load(r=0x00000001000084e0, filenameuri="testfk.swf", baddr=18446744073709551615) at file.c:563
    frame #17: 0x0000000100003962 r2`main(argc=2, argv=0x00007fff5fbff548, envp=0x00007fff5fbff560) at radare2.c:1009
    frame #18: 0x00007fff98837235 libdyld.dylib`start + 1
    frame #19: 0x00007fff98837235 libdyld.dylib`start + 1

Checking the code, turns out a simple stack buffer overflow.

// https://github.com/radare/radare2-extras/blob/master/libr/bin/format/swf/swf_specs.h
#define SWF_HDR_MIN_SIZE 12

// https://github.com/radare/radare2-extras/blob/master/libr/bin/format/swf/swf.h
typedef struct __attribute__((__packed__)) {
  ut8 signature[3];
  ut8 version;
  ut32 file_size;
  ut8 rect_size;
  ut16 frame_rate;
  ut16 frame_count;
} swf_hdr;

// https://github.com/radare/radare2-extras/blob/master/libr/bin/p/bin_swf.c
static RList* entries(RBinFile *arch) {
  RList *ret = NULL;
  RBinAddr *ptr = NULL;

  if (!(ret = r_list_new()))
    return NULL;
  if (!(ptr = R_NEW0 (RBinAddr)))
    return ret;

  swf_hdr header;        // stack variable
  header = r_bin_swf_get_header(arch);
  // ...
}

// https://github.com/radare/radare2-extras/blob/master/libr/bin/format/swf/swf.c
swf_hdr r_bin_swf_get_header(RBinFile *arch) {
  swf_hdr header;     // stack variable
  ut8 nBits;

  /* First, get the rect size */
  r_buf_read_at (arch->buf, 8, (ut8*)&nBits, 1);
  nBits = (nBits & 0xf8) >> 3;
  ut32 rect_size_bits = nBits*4 + 5;
  ut32 rect_size_bytes = rect_size_bits / 8;
  if (rect_size_bits % 8) rect_size_bytes++;

  /* Read the whole header */
  memset(&header, 0, SWF_HDR_MIN_SIZE + rect_size_bytes);   // stack overflow
  r_buf_read_at(arch->buf, 0, (ut8*)&header, 8);
  // ...
}

the header is a variable on the stack, the memset call with size of SWF_HDR_MIN_SIZE + rect_size_bytes will easily exceeds the bound, leads to buffer overflow.

The parser needs to decompress the flash file first when it's compressed flash(CWS/ZWS) and proceed the parse logic IMHO.

Cheers.

Maijin commented 7 years ago

Can you send a Pull Request fixing this.

radare commented 7 years ago

what about providing a binary to reproduce the issue?

binjo commented 7 years ago

Hi @radare, try this one, it is a CWS compressed flash file. http://www.diyed.co.uk/diy-ed-information-book-sample/files/flash/expressInstall.swf you may use my trivial script to decompress, https://github.com/binjo/utils/blob/master/cws2fws.py

md5sum expressInstall.swf fws-expressInstall.swf
7b65fbfaec8b2955090389af60646e8b  expressInstall.swf
7089405dc1945dd72bcfb14630900f46  fws-expressInstall.swf

it should crash on any flash file :<

radare commented 6 years ago

@xarkes please have a look at this