新闻: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/