We recently discovered a path traversal vulnerability in Grandnode v4.40. This issue allows unauthenticated users to retrieve any files accessible to the web application. If the application is run under administrative permissions, the damage can be significant. This vulnerability was reproduced on Windows and Linux.
We tested Grandnode v4.40, but previous versions may also be affected.
Timeline
Date | Action |
---|---|
05/16/2019 | Vulnerability discovered by Security401 |
05/17/2019 | Reached out to vendor to request the preferred method of reporting the issue. |
05/20/2019 | Reached out to the main code contributer to report the issue. |
05/28/2019 | Reached via the product forum to request preferred method of reporting the issue. |
05/30/2019 | Issue acknowledged and fixed by the vendor. |
Fix
The fix was added in this commit, please be sure to upgrade your installation if your version of Grandnode is older than 05/30/2019.
Background
Grandnode is an open source eCommerce solution powered by .NET Core 2.2, supporting Windows, Linux and Mac operating systems.
Technical Details
The vulnerability exists in LetsEncryptController.cs in the Index action method. This method is used in the Let’s Encrypt HTTP challenge to validate domain ownership of the domain(s) in a certificate request. As part of the certification process, the ACME client will generate a token file on the web server which will be publicly accessible from a URL in the format below:
http://YOUR_DOMAIN/.well-known/acme-challenge/TOKEN_FILE
Grandnode exposes this URL by mapping this route to the LetsEncryptController:
routeBuilder.MapRoute("well-known", ".well-known/acme-challenge/{fileName}",
new { controller = "LetsEncrypt", action = "Index" });
routeBuilder.MapRoute("well-known", ".well-known/acme-challenge/{fileName}",
new { controller = "LetsEncrypt", action = "Index" });
In the final steps, Let's Encrypt's servers will attempt to retrieve this token file from the URL to perform validation and if successful, issue a certificate. In order for this process to work, Let's Encrypt's servers must be able to access the token validation URL without authenticating. This is accomplished via the [AllowAnonymous] attribute on the LetsEncryptController class.
[AllowAnonymous]
public partial class LetsEncryptController : Controller
{
public virtual IActionResult Index(string fileName)
{
if (fileName == null)
return Content("");
var file = Path.Combine(CommonHelper.MapPath("~/wwwroot/content/acme/"),
fileName);
if (System.IO.File.Exists(file))
{
return new PhysicalFileResult(file, "text/plain");
}
return Content("");
}
}
The expectation is that only token files will be retrieved from this endpoint. Unfortunately, the application does not perform any validation to ensure that this is the case. By specifying a fully-qualified path, an unauthorized attacker can download any file in which the application has read permissions to. It is worth noting that we were not able to exploit this issue via the Let's Encrypt endpoint.
http://localhost/.well-known/acme-challenge/etc/passwd
Each attempt to add a fully-qualified file in the REST style URL resulted in a Page not found error. However, we can still access the controller method using the convention routing format:
http://localhost/LetsEncrypt/Index?fileName=/etc/passwd
Reproduction Steps
This issue can be reproduced with the curl commands as shown below or simply browsing to the site and specifying an absolute file path:
curl -i -s -k -H $'Connection: close' \
$'http://localhost/LetsEncrypt/Index?fileName=/etc/passwd'

curl -i -s -k -H $'Connection: close' \
$'http://localhost/LetsEncrypt/Index?fileName=c:\windows\win.ini'

A python POC for this issue can be found here.