• nginx1.3.9和1.4.0 的 chunked 溢出漏洞


    新闻:nginx 1.3.9-1.4.0版本文件http/ngx_http_parse.c代码中的ngx_http_parse_chunked()函数在对 chunked的长度进行解析时未考虑到该值为负数的情况,导致后续发生基于栈的缓冲区溢出。远程攻击者无需认证即可利用此漏造成nginx拒绝服务,甚 至执行任意代码。

    ngx_int_t
    ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
        ngx_http_chunked_t *ctx)
    {
        u_char     *pos, ch, c;
        ngx_int_t   rc;
        enum {
            sw_chunk_start = 0,
            sw_chunk_size,
            sw_chunk_extension,
            sw_chunk_extension_almost_done,
            sw_chunk_data,
            sw_after_data,
            sw_after_data_almost_done,
            sw_last_chunk_extension,
            sw_last_chunk_extension_almost_done,
            sw_trailer,
            sw_trailer_almost_done,
            sw_trailer_header,
            sw_trailer_header_almost_done
        } state;

        state = ctx->state;

        if (state == sw_chunk_data && ctx->size == 0) {
            state = sw_after_data;
        }

        rc = NGX_AGAIN;

        for (pos = b->pos; pos < b->last; pos++) {

            ch = *pos;

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http chunked byte: %02Xd s:%d", ch, state);

            switch (state) {

            case sw_chunk_start:
                if (ch >= '0' && ch <= '9') {
                    state = sw_chunk_size;
                    ctx->size = ch - '0';
                    break;
                }

                c = (u_char) (ch | 0x20);

                if (c >= 'a' && c <= 'f') {
                    state = sw_chunk_size;
                    ctx->size = c - 'a' + 10;
                    break;
                }

                goto invalid;

            case sw_chunk_size:
                if (ch >= '0' && ch <= '9') {
                    ctx->size = ctx->size * 16 + (ch - '0');
                    break;
                }

                c = (u_char) (ch | 0x20);

                if (c >= 'a' && c <= 'f') {
                    ctx->size = ctx->size * 16 + (c - 'a' + 10);
                    break;
                }

                if (ctx->size == 0) {

                    switch (ch) {
                    case CR:
                        state = sw_last_chunk_extension_almost_done;
                        break;
                    case LF:
                        state = sw_trailer;
                        break;
                    case ';':
                    case ' ':
                    case '\t':
                        state = sw_last_chunk_extension;
                        break;
                    default:
                        goto invalid;
                    }

                    break;
                }

                switch (ch) {
                case CR:
                    state = sw_chunk_extension_almost_done;
                    break;
                case LF:
                    state = sw_chunk_data;
                    break;
                case ';':
                case ' ':
                case '\t':
                    state = sw_chunk_extension;
                    break;
                default:
                    goto invalid;
                }

                break;

            case sw_chunk_extension:
                switch (ch) {
                case CR:
                    state = sw_chunk_extension_almost_done;
                    break;
                case LF:
                    state = sw_chunk_data;
                }
                break;

            case sw_chunk_extension_almost_done:
                if (ch == LF) {
                    state = sw_chunk_data;
                    break;
                }
                goto invalid;

            case sw_chunk_data:
                rc = NGX_OK;
                goto data;

            case sw_after_data:
                switch (ch) {
                case CR:
                    state = sw_after_data_almost_done;
                    break;
                case LF:
                    state = sw_chunk_start;
                }
                break;

            case sw_after_data_almost_done:
                if (ch == LF) {
                    state = sw_chunk_start;
                    break;
                }
                goto invalid;

            case sw_last_chunk_extension:
                switch (ch) {
                case CR:
                    state = sw_last_chunk_extension_almost_done;
                    break;
                case LF:
                    state = sw_trailer;
                }
                break;

            case sw_last_chunk_extension_almost_done:
                if (ch == LF) {
                    state = sw_trailer;
                    break;
                }
                goto invalid;

            case sw_trailer:
                switch (ch) {
                case CR:
                    state = sw_trailer_almost_done;
                    break;
                case LF:
                    goto done;
                default:
                    state = sw_trailer_header;
                }
                break;

            case sw_trailer_almost_done:
                if (ch == LF) {
                    goto done;
                }
                goto invalid;

            case sw_trailer_header:
                switch (ch) {
                case CR:
                    state = sw_trailer_header_almost_done;
                    break;
                case LF:
                    state = sw_trailer;
                }
                break;

            case sw_trailer_header_almost_done:
                if (ch == LF) {
                    state = sw_trailer;
                    break;
                }
                goto invalid;

            }
        }

    data:

        ctx->state = state;
        b->pos = pos;

        switch (state) {

        case sw_chunk_start:
            ctx->length = 3 /* "0" LF LF */;
            break;
        case sw_chunk_size:
            ctx->length = 2 /* LF LF */
                          + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */ : 0);
            break;
        case sw_chunk_extension:
        case sw_chunk_extension_almost_done:
            ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;
            break;
        case sw_chunk_data:
            ctx->length = ctx->size + 4 /* LF "0" LF LF */;
            break;
        case sw_after_data:
        case sw_after_data_almost_done:
            ctx->length = 4 /* LF "0" LF LF */;
            break;
        case sw_last_chunk_extension:
        case sw_last_chunk_extension_almost_done:
            ctx->length = 2 /* LF LF */;
            break;
        case sw_trailer:
        case sw_trailer_almost_done:
            ctx->length = 1 /* LF */;
            break;
        case sw_trailer_header:
        case sw_trailer_header_almost_done:
            ctx->length = 2 /* LF LF */;
            break;

        }
     //这是1.4.1升级后的补救措施,1.3.9和1.4.0没有这个代码判断
        if (ctx->size < 0 || ctx->length < 0) {
            goto invalid;
        }

        return rc;

    done:

        ctx->state = 0;
        b->pos = pos + 1;

        return NGX_DONE;

    invalid:

        return NGX_ERROR;
    }

    关于chunked介绍博客:

    http://blog.xiuwz.com/2012/01/10/talk-nginx-with-http/

  • 相关阅读:
    day06
    day05
    day04
    day03
    day02
    day01
    python-study-42
    OI 知识总览 算法篇 之 图论
    OI 知识总览 算法篇 之 基础算法
    [CSP2019-JX] 散步 解题报告
  • 原文地址:https://www.cnblogs.com/samurail/p/3070327.html
Copyright © 2020-2023  润新知