Nginx SSI Information Disclosure

Stack of rocks

When implementing Server Side Includes (SSI) on nginx 1.16.1, extra bytes were being returned in the responses. While this wouldn’t be noticed when viewed in a webpage, it can provide information disclosure when viewing the raw stream. The extra bytes were caused by Transfer-Encoding: Chunked being used in the HTTP response.

Transfer-Encoding

The Transfer-Encoding HTTP header is defined by RFC2616 as:

Transfer-Encoding: Chunked

When Transfer-Encoding: Chunked is in use, each chunked portion of the body must be preceded by it’s length in hexadecimal format followed by a \r\n. The last line of the body should be 0 indicating transfer is complete.

The following example shows an HTTP response using this technique. The HTTP body begins with 6b indicating the length of the body followed by the terminating 0.

Information Disclosure

The reason this leads to information disclosure is each SSI section of the webpage is proceeded by it’s size which the following example will illustrate. The message body shows fb as the size of the first chunck. Then 6b is used to show the SSI portion while 401 is the length of the remaining body. This makes it clear that the CSS files are being added via SSI.

Having this information allows someone to understand the logic of the webpage/webapp and potentially target that portion. While not concerning if SSI is being used to serve static html files. The issue is using SSI for dynamic purposes such as requests to a CGI script or another web application.

Remediation

Fixing this is quite easy, simply add the following to the server configuration block.

Test

Testing can be done using the SocketHttpRequest PowerShell module from my github page: https://github.com/phbits/SocketHttpRequest. Running the following commands will return the raw HTTP response headers and body.