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