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-17 \u2014 DEPRECATED: Code
This entry has been deprecated.  It was originally used for organizing the Development View (CWE-699) and some other views, but it introduced unnecessary complexity and depth to the resulting tree.
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;