Wiki.js >2.4.17 was vulnerable to stored cross-site scripting through template injection. This vulnerability existed due to a malicious payload in a top-level text element bypassing the intended protection mechanisms.
Date Released: 15/06/2020
Author: Denis Andzakovic
Project Website: https://wiki.js.org/
Affected Software: Wiki.js > 2.4.17
Patched Version: 2.4.107
GHSA: GHSA-9jgg-4xj2-vjjj
CVE: CVE-2020-4052
Details
By creating a crafted wiki page, a malicious Wiki.js user may stage a stored cross-site scripting attack. This allows the attacker to execute malicious JavaScript when the page is viewed by other users. The following figure shows an example Wiki.js page (using the raw-HTML editor mode) which executed attacker provided JavaScript:
<h1>Title</h1>
<p>Some text here</p> {{constructor.constructor('alert("Wiki.JS Stored XSS")')()}}
The following screenshot shows the POC above executing:
This vulnerability existed due to the HTML rendering logic incorrectly handling curly-braces in a top-level text element. The protection was intended to match on curly braces and insert a v-pre
attribute into the parent tag. By injecting the curly braces into a top level text element, the protection was bypassed and the raw payload returned to the user, resulting in stored cross-site scripting. The following snippet shows the code responsible for escaping curly braces:
232
233 // --------------------------------
234 // Escape mustache expresions
235 // --------------------------------
236
237 function iterateMustacheNode (node) {
238 const list = $(node).contents().toArray()
239 list.forEach(item => {
240 if (item.type === 'text') {
241 const rawText = $(item).text()
242 if (rawText.indexOf('{{') >= 0 && rawText.indexOf('}}') > 1) {
243 $(item).parent().attr('v-pre', true)
244 }
245 } else {
246 iterateMustacheNode(item)
247 }
248 })
249 }
250 iterateMustacheNode($.root())
251
252 // --------------------------------
253 // STEP: POST
254 // --------------------------------
255
256 let output = decodeEscape($.html('body').replace('<body>', '').replace('</body>', ''))
257
Timeline
12/06/2020 - Advisory sent to Requarks
13/06/2020 - GHSA created
13/06/2020 - Fix created
15/06/2020 - Fix merged to master branch
15/06/2020 - Advisory release