Lucene search

K
hackeroneJeriko_oneH1:824163
HistoryMar 18, 2020 - 10:01 p.m.

Internet Bug Bounty: Squid leaks previous content from reusable buffer

2020-03-1822:01:47
jeriko_one
hackerone.com
40

7.5 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

NONE

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

5 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

NONE

Availability Impact

NONE

AV:N/AC:L/Au:N/C:P/I:N/A:N

0.046 Low

EPSS

Percentile

91.3%

Summary:

A malicious response to a FTP request can cause Squid to miscalculate the length of a string copying data past the terminating NULL. Due to Squid’s memory pool the contents that is exposed could range from internal data, to other user’s private Request/Response to Squid.

This exist in Squid-4.9 and Below and was fixed in Squid-4.10
This vulnerability was assigned CVE-2019-12528.

Steps To Reproduce:

A custom config is should not be needed.
I’ve attached a python script that returns the needed response to trigger this.

  1. Start Squid
./sbin/squid
  1. Start your malicious FTP Server
./squid_leak.py 8080
  1. Make a request to the FTP server via Squid.
printf "GET ftp://<ftp ip>:8080/ HTTP/1.1\r\n\r\n" | nc <squid hostname> 3128
  1. The FTP server should have sent the listing. A message from it saying
<- 226 Listing sent

Should be visible

The leaked data is now in the HTML that Squid has returned. The data will be under the line

<th><a href="../">Parent Directory</a> (<a href="/">Root Directory</a>)</th>

Within the following <tr>

For reference a normal response would look like

<tr><td colspan="5">hi</td></tr>

Analysis

The issue begins in Ftp::Gateway::parsingListing the relevant snippet being

    line = (char *)memAllocate(MEM_4K_BUF);
    ++end;
    s = sbuf;
    s += strspn(s, crlf);

    for (; s &lt; end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
        debugs(9, 7, HERE &lt;&lt; "s = {" &lt;&lt; s &lt;&lt; "}");
        linelen = strcspn(s, crlf) + 1;
		&lt;snip&gt;
        xstrncpy(line, s, linelen);
		&lt;snip&gt;
		if (htmlifyListEntry(line, html)) 

A crucial thing to notice here is the following:

  • line is allocated with memAllocate(MEM_4K_BUF) this is what will lead us to reading previous content. Buffers allocated via this method aren’t ever free’d, but are put back into their respective pools. Zeroing of the buffer is possible, but is not enabled for this type of memory.

Within ftpListParseParts (FtpGateway.cc) is where the root of the vulnerability exist.
This function can handle various formats for listings.

A common procedure is done on all of them before that then. They are converted
into tokens by strtok.

    for (t = strtok(xbuf, w_space); t && n_tokens &lt; MAX_TOKENS; t = strtok(NULL, w_space)) {
        tokens[n_tokens] = xstrdup(t);
        ++n_tokens;
    }

Please note that strok uses w_space as delimiters

	#define w_space     " \t\n\r"

The listing format that we’ll focus on is DOS format (FtpGateway.cc:648)

For listings that aren’t directories the following code is executed:

        } else {
            /* A file. Name begins after size, with a space in between */
            snprintf(tbuf, 128, " %s %s", tokens[2], tokens[3]);
            ct = strstr(buf, tbuf);

            if (ct) {
                ct += strlen(tokens[2]) + 2;
            }
        }

        p-&gt;name = xstrdup(ct ? ct : tokens[3]);

Squid will put tokens[2] and tokens[3] in a temporary buffer with 2 spaces. It
then searches for this string in the original line setting ct to the start of
this string. It then increments ct by the length of tokens[2] + 2. What is
pointed to now is used as the name.

The false assumption here is that tokens will be separated by spaces in the
original line.

Consider the following example where \t denotes a tab, and * is for repetition:

04-05-70 09:33PM\tA*126 A*126

Going through the referenced code path when snprintf is called tbuf will be
filled as: " A*126". Then when strstr is called, it’ll find the token, but it
won’t be token[2] it’ll be token[3] as token[2] started with a tab. When it
increments by the length strlen(tokens[2]) + 2 it’ll put ct past the
terminating NULL byte of this line.

The contents is then copied into another buffer which will be displayed to the attacker

p->name = xstrdup(ct ? ct : tokens[3]);

Setting a breakpoint in we can confirm that it’s leaking data

Confirming that the tokens are 126:

(gdb) call (size_t)strlen(tokens[2])
$2 = 126
(gdb) call (size_t)strlen(tokens[3])
$3 = 126

Here ct is set to the wrong token since it’s looking for " A"

snprintf(tbuf, 128, " %s %s", tokens[2], tokens[3]);
(gdb) n
675	            ct = strstr(buf, tbuf);
(gdb) call (size_t)strlen(tbuf)
$4 = 127

(gdb) p tbuf
$5 = " ", 'A' &lt;repeats 126 times&gt;

(gdb) p ct
$6 = 0x62100006918f " ", 'A' &lt;repeats 126 times&gt;

678	                ct += strlen(tokens[2]) + 2;
(gdb) call (size_t) strlen(tokens[2]) + 2
$8 = 128

Here we see ct is now past the terminating NULL

(gdb) x/2xb ct - 1
0x62100006920e:	0x00	0x66

Impact

An attacker can leak sensitive information from the Squid process. This could include other user’s Request and Response which could have headers, cookies, full bodies, and post data.

7.5 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

NONE

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

5 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

NONE

Availability Impact

NONE

AV:N/AC:L/Au:N/C:P/I:N/A:N

0.046 Low

EPSS

Percentile

91.3%