CVE-2015-3183 (Other, published 2015-07-20T23:59Z)
Description: value: The chunked transfer coding implementation in the Apache HTTP Server before 2.4.14 does not properly parse chunk headers, which allows remote attackers to conduct HTTP request smuggling attacks via a crafted request, related to mishandling of large chunk-size values and invalid chunk-extension characters in modules/http/http_filters.c.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: CWE-20 \u2014 Improper Input Validation
The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Repository: https://github.com/apache/httpd
Files changed:
CHANGES
docs/log-message-tags/next-number
modules/http/http_filters.c
Commit message: Limit accepted chunk-size to 2^63-1 and be strict about chunk-ext
authorized characters.

Submitted by: Yann Ylavic



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1684513 13f79535-47bb-0310-9956-ffa450edef68


Vulnerable code:
2901
#define INVALID_CHAR -2

    apr_int16_t chunkbits;
        BODY_CHUNK_END, /* chunk terminating CRLF */
        apr_size_t len, int linelimit)
        if (ctx->state == BODY_CHUNK_END) {
                 * Detect invalid character at beginning. This also works for empty
                 * chunk size lines.
                return APR_EGENERAL;
            ctx->chunkbits = sizeof(long) * 8;
        /* handle a chunk part, or a chunk extension */
        /*
         * In theory, we are supposed to expect CRLF only, but our
         * test suite sends LF only. Tolerate a missing CR.
         */
        if (c == ';' || c == CR) {
            ctx->state = BODY_CHUNK_EXT;
        }
        else if (c == LF) {
        else if (ctx->state != BODY_CHUNK_EXT) {
            int xvalue = 0;
                return APR_EGENERAL;
            ctx->chunkbits -= 4;
            if (ctx->chunkbits <= 0 || ctx->remaining < 0) {

        ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
                              "Unknown Transfer-Encoding: %s;"
                              " using read-until-close", tenc);
                ap_log_rerror(
                        APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01587)
                        "Invalid Content-Length");
                return APR_ENOSPC;
                ap_log_rerror(
                        APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01588)
                        "Requested content-length of %" APR_OFF_T_FMT
                        " is larger than the configured limit"
                        " of %" APR_OFF_T_FMT, ctx->remaining, ctx->limit);
        case BODY_CHUNK_END: {
                        ap_log_rerror(
                                APLOG_MARK, APLOG_INFO, rv, f->r, APLOGNO(01590) "Error reading chunk %s ", (APR_ENOSPC == rv) ? "(overflow)" : "");
                int merge_trailers =
                    conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE;
                return read_chunked_trailers(ctx, f, b, merge_trailers);
                    ap_log_rerror(
                            APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01591) "Read content-length of %" APR_OFF_T_FMT " is larger than the configured limit"
                            " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
            break;

Fixed code:
  *) core: Limit accepted chunk-size to 2^63-1 and be strict about chunk-ext
     authorized characters.  [Yann Ylavic]

2902
    apr_int32_t chunkbits;
        BODY_CHUNK_LF, /* got CR, expect LF after digits/extension */
        BODY_CHUNK_END, /* chunked data terminating CRLF */
        BODY_CHUNK_END_LF, /* got CR, expect LF after data */
                                     apr_size_t len, int linelimit)
        if (ctx->state == BODY_CHUNK_END
                || ctx->state == BODY_CHUNK_END_LF) {
            else if (c == CR && ctx->state == BODY_CHUNK_END) {
                ctx->state = BODY_CHUNK_END_LF;
            }
            else {
                /*
                 * LF expected.
                 */
                return APR_EINVAL;
            }
                 * Detect invalid character at beginning. This also works for
                 * empty chunk size lines.
                return APR_EINVAL;
            ctx->chunkbits = sizeof(apr_off_t) * 8;
        if (c == LF) {
        else if (ctx->state == BODY_CHUNK_LF) {
            /*
             * LF expected.
             */
            return APR_EINVAL;
        }
        else if (c == CR) {
            ctx->state = BODY_CHUNK_LF;
        }
        else if (c == ';') {
            ctx->state = BODY_CHUNK_EXT;
        }
        else if (ctx->state == BODY_CHUNK_EXT) {
            /*
             * Control chars (but tabs) are invalid.
             */
            if (c != '\t' && apr_iscntrl(c)) {
                return APR_EINVAL;
            }
        }
        else if (ctx->state == BODY_CHUNK_PART) {
            int xvalue;
            ctx->chunkbits -= 4;
            if (ctx->chunkbits < 0) {
                /* overflow */
                return APR_ENOSPC;
            }

                return APR_EINVAL;
            if (ctx->remaining < 0) {
        }
        else {
            /* Should not happen */
            return APR_EGENERAL;
                            ap_input_mode_t mode, apr_read_type_e block,
                            apr_off_t readbytes)
                              "Unknown Transfer-Encoding: %s; "
                              "using read-until-close", tenc);
                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01587)
                              "Invalid Content-Length");
                return APR_EINVAL;
                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01588)
                          "Requested content-length of %" APR_OFF_T_FMT
                          " is larger than the configured limit"
                          " of %" APR_OFF_T_FMT, ctx->remaining, ctx->limit);
                apr_brigade_cleanup(bb);
        case BODY_CHUNK_LF:
        case BODY_CHUNK_END:
        case BODY_CHUNK_END_LF: {
                        ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r, APLOGNO(01590)
                                      "Error reading chunk %s ",
                                      (APR_ENOSPC == rv) ? "(overflow)" : "");
                return read_chunked_trailers(ctx, f, b,
                            conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE);
                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01591)
                                  "Read content-length of %" APR_OFF_T_FMT
                                  " is larger than the configured limit"
                                  " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
            /* Should not happen */
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(02901)
                          "Unexpected body state (%i)", (int)ctx->state);
            return APR_EGENERAL;