You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Nesting many scopes as seen in the input leads to an integer overflow in OP_GET_UPVAR:
CASE(OP_GETUPVAR) {
/* A B C R(A) := uvget(B,C) */
int a = GETARG_A(i);
int b = GETARG_B(i);
int c = GETARG_C(i);
mrb_value *regs_a = regs + a;
struct REnv *e = uvenv(mrb, c);
if (!e) {
*regs_a = mrb_nil_value();
}
else {
*regs_a = e->stack[b];
}
NEXT;
}
Details:
If 128 scopes are nested and we access a variable from the outermost scope from the innermost scope, c overflows to 0. b contains the index of of the variable in the outer scope, but is used to offset into the inner scopes stackframe. If the outermost scope contains many variables, b can become large. If b is large, and the innermost stackframe is small, we can use memory from outside the stackframe as mrb_value. In some cases, a mrb_value can contains a pointer (objects, strings etc). In these cases, the pointer can be corrupted by controlling the memory outside the stack frame. Our POC triggers a use after free access (see use_after_free.rb).
In other cases, we can simply change variables from other stackframes, we observed some type confusions in various C functions that seem to dislike values changing magically (see the attached input nullptr_deref.rb, where a corrupted proc object is used in places where an object is expected).
Bugfix:
Ensure that deep nesting is an error condition (As this will cause bugs in ruby programs, even if the underlaying C code is fixed). Additionally add the following check (taken from SET_UPVAR, where the size is checked correctly)
if (b < MRB_ENV_STACK_LEN(e)) {
[.... perform code ]
}
./bin/mruby use_after_free.rb
=================================================================
==24954==ERROR: AddressSanitizer: heap-use-after-free on address 0x62500002a900 at pc 0x0000004a387d bp 0x7ffe67f129f0 sp 0x7ffe67f121a0
READ of size 16 at 0x62500002a900 thread T0
#0 0x4a387c (/tmp/mruby/bin/mruby+0x4a387c)
#1 0x63f591 (/tmp/mruby/bin/mruby+0x63f591)
#2 0x63a041 (/tmp/mruby/bin/mruby+0x63a041)
#3 0x66e8e9 (/tmp/mruby/bin/mruby+0x66e8e9)
#4 0x60ee53 (/tmp/mruby/bin/mruby+0x60ee53)
#5 0x60fab5 (/tmp/mruby/bin/mruby+0x60fab5)
#6 0x4ec357 (/tmp/mruby/bin/mruby+0x4ec357)
#7 0x7f4a0ea8f82f (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#8 0x419a18 (/tmp/mruby/bin/mruby+0x419a18)
0x62500002a900 is located 0 bytes inside of 9760-byte region [0x62500002a900,0x62500002cf20)
freed by thread T0 here:
#0 0x4b9ec8 (/tmp/mruby/bin/mruby+0x4b9ec8)
#1 0x4ee465 (/tmp/mruby/bin/mruby+0x4ee465)
#2 0x4f3d16 (/tmp/mruby/bin/mruby+0x4f3d16)
#3 0x4f43f4 (/tmp/mruby/bin/mruby+0x4f43f4)
#4 0x630ea3 (/tmp/mruby/bin/mruby+0x630ea3)
#5 0x6356d9 (/tmp/mruby/bin/mruby+0x6356d9)
#6 0x63608e (/tmp/mruby/bin/mruby+0x63608e)
#7 0x645e60 (/tmp/mruby/bin/mruby+0x645e60)
#8 0x63a041 (/tmp/mruby/bin/mruby+0x63a041)
#9 0x66e8e9 (/tmp/mruby/bin/mruby+0x66e8e9)
#10 0x60ee53 (/tmp/mruby/bin/mruby+0x60ee53)
#11 0x60fab5 (/tmp/mruby/bin/mruby+0x60fab5)
#12 0x4ec357 (/tmp/mruby/bin/mruby+0x4ec357)
#13 0x7f4a0ea8f82f (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
previously allocated by thread T0 here:
#0 0x4b9ec8 (/tmp/mruby/bin/mruby+0x4b9ec8)
#1 0x4ee465 (/tmp/mruby/bin/mruby+0x4ee465)
#2 0x4f3d16 (/tmp/mruby/bin/mruby+0x4f3d16)
#3 0x4f43f4 (/tmp/mruby/bin/mruby+0x4f43f4)
#4 0x630ea3 (/tmp/mruby/bin/mruby+0x630ea3)
#5 0x6356d9 (/tmp/mruby/bin/mruby+0x6356d9)
#6 0x63608e (/tmp/mruby/bin/mruby+0x63608e)
#7 0x645e60 (/tmp/mruby/bin/mruby+0x645e60)
#8 0x63a041 (/tmp/mruby/bin/mruby+0x63a041)
#9 0x66e8e9 (/tmp/mruby/bin/mruby+0x66e8e9)
#10 0x60ee53 (/tmp/mruby/bin/mruby+0x60ee53)
#11 0x60fab5 (/tmp/mruby/bin/mruby+0x60fab5)
#12 0x4ec357 (/tmp/mruby/bin/mruby+0x4ec357)
#13 0x7f4a0ea8f82f (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
SUMMARY: AddressSanitizer: heap-use-after-free (/tmp/mruby/bin/mruby+0x4a387c)
Shadow bytes around the buggy address:
0x0c4a7fffd4d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffd4e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffd4f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffd500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffd510: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c4a7fffd520:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c4a7fffd530: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c4a7fffd540: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c4a7fffd550: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c4a7fffd560: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c4a7fffd570: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
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
==24954==ABORTING
https://hackerone.com/pnoltof reported the following:
Root Cause:
Nesting many scopes as seen in the input leads to an integer overflow in OP_GET_UPVAR:
Details:
If 128 scopes are nested and we access a variable from the outermost scope from the innermost scope,
c
overflows to 0.b
contains the index of of the variable in the outer scope, but is used to offset into the inner scopes stackframe. If the outermost scope contains many variables, b can become large. If b is large, and the innermost stackframe is small, we can use memory from outside the stackframe as mrb_value. In some cases, a mrb_value can contains a pointer (objects, strings etc). In these cases, the pointer can be corrupted by controlling the memory outside the stack frame. Our POC triggers a use after free access (see use_after_free.rb).In other cases, we can simply change variables from other stackframes, we observed some type confusions in various C functions that seem to dislike values changing magically (see the attached input nullptr_deref.rb, where a corrupted proc object is used in places where an object is expected).
Bugfix:
Ensure that deep nesting is an error condition (As this will cause bugs in ruby programs, even if the underlaying C code is fixed). Additionally add the following check (taken from SET_UPVAR, where the size is checked correctly)
Steps to Reproduce:
setup latest mruby
run inputs:
and also:
Authors:
Daniel Teuchert, Cornelius Aschermann, Tommaso Frassetto, Tigist Abera
Impact
By leveraging the use after free, an attacker is most likely able to obtain arbitrary code execution.
use_after_free.rb
:null_ptr_deref.rb
:The text was updated successfully, but these errors were encountered: