HexBrain

Internet Bug Bounty · Report #1455248

Buffer Overflow in optimized_escape_html method

Validstatus
mediumseverity
6upvotes
22 Jan 2022disclosed

Summary

A buffer overrun vulnerability was discovered in CGI.escape_html. This vulnerability has been assigned the CVE identifier CVE-2021-41816. We strongly recommend upgrading Ruby.

Details A security vulnerability that causes buffer overflow when you pass a very large string (> 700 MB) to CGI.escape_html on a platform where long type takes 4 bytes, typically, Windows.

Please update the cgi gem to version 0.3.1, 0.2,1, and 0.1,1 or later. You can use gem update cgi to update it. If you are using bundler, please add gem "cgi", ">= 0.3.1" to your Gemfile. Alternatively, please update Ruby to 2.7.5 or 3.0.3.

This issue has been introduced since Ruby 2.7, so the cgi version bundled with Ruby 2.6 is not vulnerable.

Affected versions cgi gem 0.1.0 or prior (which are bundled versions with Ruby 2.7 series prior to Ruby 2.7.5) cgi gem 0.2.0 or prior (which are bundled versions with Ruby 3.0 series prior to Ruby 3.0.3) cgi gem 0.3.0 or prior

Credits Thanks to chamal for discovering this issue.

https://www.ruby-lang.org/en/news/2021/11/24/buffer-overrun-in-cgi-escape_html-cve-2021-41816/

How it was found

  • This report is a copy of bug report (https://hackerone.com/reports/1328463). I was asked to submit this bug here, because Ruby bug bounty program is moved to this new Internet Bug Bounty program.

Operating System

Windows 10 This should reproduce in any other operating system where long data type takes 4 bytes.

Description

This bug is present in optimized_escape_html method of ext\cgi\escape\escape.c file. Below mentioned line causes this bug.

char *buf = ALLOCV_N(char, vbuf, RSTRING_LEN(str) * HTML_ESCAPE_MAX_LEN);

RSTRING_LEN is a preprocessor directive. It expands to below code.

static inline long
RSTRING_LEN(VALUE str)
{
    return rbimpl_rstring_getmem(str).as.heap.len;
}

HTML_ESCAPE_MAX_LEN is defined as this.

#define HTML_ESCAPE_MAX_LEN 6

Note that RSTRING_LEN returns a long data type value. Long data type takes 4 bytes on Windows operating system. So maximum value for a unsigned long data type is 4,294,967,295.

Attached test case passes a string, which has 715828054 characters to CGI.escapeHTML method. Length of the buffer is calculated as -> RSTRING_LEN(str) * HTML_ESCAPE_MAX_LEN 715828054 * 6 = 4,294,968,324 This value is too large for long data type. So 4,294,968,324 becomes 1028, when stored in a long variable. So ALLOCV_N method creates a buffer of 1028 bytes.

optimized_escape_html method contains a loop, which writes to this buffer.

    const char *cstr = RSTRING_PTR(str);
    const char *end = cstr + RSTRING_LEN(str);
    char *dest = buf;
    while (cstr < end) {
        const unsigned char c = *cstr++;
        uint8_t len = html_escape_table[c].len;
        if (len) {
            memcpy(dest, html_escape_table[c].str, len);
            dest += len;
        }
        else {
            *dest++ = c;
        }
    }

This loops expects a buffer of size 4,294,968,324. But it receives a buffer with 1028 bytes. So this loop writes data, beyond provided buffer.

Reproduction Steps

  1. Download attached F1585929 file.
  2. Run ruby escape.rb. Please attach to a debugger to notice the crash.

Impact

Attacker may be able to write unintended data to memory.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original