[CVE-2017-6181] Ruby 2.4.0 – Stack Overflow

Słowo wstępu

Chwila odpoczynku od projektów wykorzystywanych w branży bezpieczeństwa – dzisiejszym targetem jest interpreter języka Ruby, podstawy popularnego frameworka webowego Ruby on Rails – obecnego w kodzie takich serwisów jak GitHub, BaseCamp, AirBnb czy Twitch.

Krótki opis podatności

Błąd znajdował się w wersji 2.4.0, a więc na dzień pisania posta ostatniej stabilnej (Git HEAD: fbd5cda6aad6db01bbca3d893a9970314a1bd52c).

Funkcja parsująca wyrażenie regularne, przed sprawdzeniem głębokości wyrażenia regularnego, nie inicjalizowała zwracanej wartości, tylko tuż za nim (sprawdzeniem). Powodowało to przy odpowiednio “dużym” wyrażeniu regularnym za dużą alokację ilości ramek stosu dla rekurencyjnie wywoływanej funkcji i w konsekwencji jego przepełnienie.

Naprawa tego błędu była bardzo prosta i polegała na przeniesieniu linijki odpowiedzialnej za inicjalizację zmiennej o 4 linie w górę.

Poniżej podatna funkcja z zaznaczonym błędem:

static int
parse_char_class(Node** np, Node** asc_np, OnigToken* tok, UChar** src, UChar* end,
		 ScanEnv* env)
{
  int r, neg, len, fetched, and_start;
  OnigCodePoint v, vs;
  UChar *p;
  Node* node;
  Node* asc_node;
  CClassNode *cc, *prev_cc;
  CClassNode *asc_cc, *asc_prev_cc;
  CClassNode work_cc, asc_work_cc;

  enum CCSTATE state;
  enum CCVALTYPE val_type, in_type;
  int val_israw, in_israw;
 
  env->parse_depth++;
  if (env->parse_depth > ParseDepthLimit)
    return ONIGERR_PARSE_DEPTH_LIMIT_OVER;
  prev_cc = asc_prev_cc = (CClassNode* )NULL;
  *np = *asc_np = NULL_NODE;
  r = fetch_token_in_cc(tok, src, end, env);
  if (r == TK_CHAR && tok->u.c == '^' && tok->escaped == 0) {
    neg = 1;
    r = fetch_token_in_cc(tok, src, end, env);
  }
  else {
    neg = 0;
}

Payload triggerujący tą lukę był bardzo prosty – 4096 razy ‘[‘ w wyrażeniu regularnym Ruby.

Log błędu:

[BUG] Segmentation fault at 0x00006e0000005b
ruby 2.5.0dev (2017-02-18 trunk 57652) [x86_64-linux]

-- Control frame information -----------------------------------------------
c:0001 p:0000 s:0003 E:000c70 (none) [FINISH]


-- Machine register context ------------------------------------------------
RIP: 0x0000562fdcb53209 RBP: 0x0000562fdcf929b0 RSP: 0x00007ffea4154b40
RAX: 0x0000000000002431 RBX: 0x0000000000000000 RCX: 0x0000562fdcfa32d0
RDX: 0x00007ffea4294c30 RDI: 0x0000006e0000005b RSI: 0x00007ffea4154c10
R8: 0x0000562fddd7422a  R9: 0x0000562fdcf929b0 R10: 0x00007f977f639ba8
R11: 0x0000000000000001 R12: 0x0000562fdcf929b0 R13: 0x0000006e0000005b
R14: 0x0000562fdccf5aa0 R15: 0xfffffffffffffffc EFL: 0x0000000000010202

-- C level backtrace information -------------------------------------------
/XYZ/ruby/miniruby(rb_vm_bugreport+0x2b7) [0x562fdccbc2f7] vm_dump.c:683
/XYZ/ruby/miniruby(rb_bug_context+0x2e6) [0x562fdc981a86] error.c:520
/XYZ/ruby/miniruby(sigsegv+0x6a) [0x562fdcb842fa] signal.c:907
/lib/x86_64-linux-gnu/libpthread.so.0 [0x7f977fd95390]
/XYZ/ruby/miniruby(onig_node_free+0x79) [0x562fdcb53209] regparse.c:1065
/XYZ/ruby/miniruby(parse_char_class+0x28ae) [0x562fdcb6c08e] regparse.c:4824
/XYZ/ruby/miniruby(parse_char_class+0x2194) [0x562fdcb6b974] regparse.c:4814
=================== SNIP (1015 wierszy) ===================
/XYZ/ruby/miniruby(parse_char_class+0x2194) [0x562fdcb6b974] regparse.c:4814
/XYZ/ruby/miniruby(parse_char_class+0x2194) [0x562fdcb6b974] regparse.c:4814

-- Other runtime information -----------------------------------------------

* Loaded script: id:000000,sig:06,src:000106+002150,op:splice,rep:128

* Loaded features:

0 enumerator.so
1 thread.rb
2 rational.so
3 complex.so

* Process memory map:

562fdc86c000-562fdcd8d000 r-xp 00000000 fc:00 849526
/XYZ/ruby/miniruby
562fdcf8c000-562fdcf92000 r--p 00520000 fc:00 849526
/XYZ/ruby/miniruby
562fdcf92000-562fdcf93000 rw-p 00526000 fc:00 849526
/XYZ/ruby/miniruby
562fdcf93000-562fdcfb4000 rw-p 00000000 00:00 0
562fddc4d000-562fddde4000 rw-p 00000000 00:00 0
[heap]
7f977e13d000-7f977ed88000 r--s 00000000 fc:00 849526
/XYZ/ruby/miniruby
7f977ed88000-7f977ed9e000 r-xp 00000000 fc:00 392981
/lib/x86_64-linux-gnu/libgcc_s.so.1
7f977ed9e000-7f977ef9d000 ---p 00016000 fc:00 392981
/lib/x86_64-linux-gnu/libgcc_s.so.1
7f977ef9d000-7f977ef9e000 rw-p 00015000 fc:00 392981
/lib/x86_64-linux-gnu/libgcc_s.so.1
7f977ef9e000-7f977f276000 r--p 00000000 fc:00 6318
/usr/lib/locale/locale-archive
7f977f276000-7f977f435000 r-xp 00000000 fc:00 415253
/lib/x86_64-linux-gnu/libc-2.23.so
7f977f435000-7f977f635000 ---p 001bf000 fc:00 415253
/lib/x86_64-linux-gnu/libc-2.23.so
7f977f635000-7f977f639000 r--p 001bf000 fc:00 415253
/lib/x86_64-linux-gnu/libc-2.23.so
7f977f639000-7f977f63b000 rw-p 001c3000 fc:00 415253
/lib/x86_64-linux-gnu/libc-2.23.so
7f977f63b000-7f977f63f000 rw-p 00000000 00:00 0
7f977f63f000-7f977f747000 r-xp 00000000 fc:00 415258
/lib/x86_64-linux-gnu/libm-2.23.so
7f977f747000-7f977f946000 ---p 00108000 fc:00 415258
/lib/x86_64-linux-gnu/libm-2.23.so
7f977f946000-7f977f947000 r--p 00107000 fc:00 415258
/lib/x86_64-linux-gnu/libm-2.23.so
7f977f947000-7f977f948000 rw-p 00108000 fc:00 415258
/lib/x86_64-linux-gnu/libm-2.23.so
7f977f948000-7f977f951000 r-xp 00000000 fc:00 415255
/lib/x86_64-linux-gnu/libcrypt-2.23.so
7f977f951000-7f977fb50000 ---p 00009000 fc:00 415255
/lib/x86_64-linux-gnu/libcrypt-2.23.so
7f977fb50000-7f977fb51000 r--p 00008000 fc:00 415255
/lib/x86_64-linux-gnu/libcrypt-2.23.so
7f977fb51000-7f977fb52000 rw-p 00009000 fc:00 415255
/lib/x86_64-linux-gnu/libcrypt-2.23.so
7f977fb52000-7f977fb80000 rw-p 00000000 00:00 0
7f977fb80000-7f977fb83000 r-xp 00000000 fc:00 415252
/lib/x86_64-linux-gnu/libdl-2.23.so
7f977fb83000-7f977fd82000 ---p 00003000 fc:00 415252
/lib/x86_64-linux-gnu/libdl-2.23.so
7f977fd82000-7f977fd83000 r--p 00002000 fc:00 415252
/lib/x86_64-linux-gnu/libdl-2.23.so
7f977fd83000-7f977fd84000 rw-p 00003000 fc:00 415252
/lib/x86_64-linux-gnu/libdl-2.23.so
7f977fd84000-7f977fd9c000 r-xp 00000000 fc:00 415259
/lib/x86_64-linux-gnu/libpthread-2.23.so
7f977fd9c000-7f977ff9b000 ---p 00018000 fc:00 415259
/lib/x86_64-linux-gnu/libpthread-2.23.so
7f977ff9b000-7f977ff9c000 r--p 00017000 fc:00 415259
/lib/x86_64-linux-gnu/libpthread-2.23.so
7f977ff9c000-7f977ff9d000 rw-p 00018000 fc:00 415259
/lib/x86_64-linux-gnu/libpthread-2.23.so
7f977ff9d000-7f977ffa1000 rw-p 00000000 00:00 0
7f977ffa1000-7f977ffc7000 r-xp 00000000 fc:00 394124
/lib/x86_64-linux-gnu/ld-2.23.so
7f9780095000-7f97800b7000 r--s 00000000 fc:00 415259
/lib/x86_64-linux-gnu/libpthread-2.23.so
7f97800b7000-7f97801bc000 rw-p 00000000 00:00 0
7f97801c0000-7f97801c1000 ---p 00000000 00:00 0
7f97801c1000-7f97801c6000 rw-p 00000000 00:00 0
7f97801c6000-7f97801c7000 r--p 00025000 fc:00 394124
/lib/x86_64-linux-gnu/ld-2.23.so
7f97801c7000-7f97801c8000 rw-p 00026000 fc:00 394124
/lib/x86_64-linux-gnu/ld-2.23.so
7f97801c8000-7f97801c9000 rw-p 00000000 00:00 0
7ffea3a99000-7ffea4298000 rw-p 00000000 00:00 0
[stack]
7ffea42fa000-7ffea42fc000 r--p 00000000 00:00 0
[vvar]
7ffea42fc000-7ffea42fe000 r-xp 00000000 00:00 0
[vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0
[vsyscall]


[NOTE]
You may have encountered a bug in the Ruby interpreter or extension
libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html

Issue #13234 na Redmine projektu

Commit naprawiający: ea940cc4dcff8d6c345d7015eda0bf06671f87e9

CVE: CVE-2017-6181