Hacker News new | past | comments | ask | show | jobs | submit login
How we exploited a code execution vulnerability in math.js (capacitorset.github.io)
151 points by CapacitorSet on April 2, 2017 | hide | past | favorite | 29 comments



I was bored so I found a bunch more:

https://github.com/josdejong/mathjs/issues/821


Ugh...that's not good. Are they using their own approach for (supposedly) safe eval(), or some library that other packages might be using?


Their own approach: although many other "safe eval" packages may be affected by similar issues


You can actually get access to require using this bug: cos.constructor("return process.mainModule.require")()


Oh well, that would have been so much easier. Thank you!


Blacklists are a losing game. Always use a whitelist.


This was actually the second fix I had in mind, after the author mentioned that they would like mathjs to have complete browser support (and therefore couldn't use the `vm` module from Node.js):

>If, anyway, you want to make math.eval resistant against arbitrary code execution, I think it would be best to have a whitelist of methods and constructs (i.e. you parse the code that is meant to be evaluated and ensure that every construct is allowed). I analyze JS malware in my free time (see [box-js](https://github.com/CapacitorSet/box-js)), and I found that it is virtually impossible to blacklist functions. For instance, if the parser forbids `[].map.constructor`, I could very well use `[].map["constructor"]`; and if you blacklist the word "constructor", I could use `[].map["rotcurtsnoc".split("").reverse().join("")]`, and so on, there's an infinity of methods one can come up with to avoid blacklists.

The examples didn't really work in math.js, but it turns out that there's still [quite a few ways to get around it](https://github.com/josdejong/mathjs/issues/821).


> and therefore couldn't use the `vm` module from Node.js

browsers do have isolated contexts these days, they're just cumbersome to use.

  1. spawn an iframe with sandbox="allow-script" and srcdoc="...<meta http-equiv="Content-Security-Policy" ...><script>...</script>..."
  2. use window.postMessage to communicate between the parent page and the iframe running in a null origin
  3. send code to eval into that iframe


I'm not really an expert on JS, but I'm guessing that the constructor function is the same function object, although it can be accessed in different ways. Wouldn't it be possible to blacklist the function object itself, instead of the access path?


This is what the author attempted to do: if you read the first commit linked in the article, they made it so that math.js wouldn't execute Function when it encountered it (either an actual Function or a variable that equals Function).

However, the trick is to make Javascript execute Function, through a function that math.js won't mind executing. What I found was simply using Function.apply and Function.call; the author found Function.bind, and someone in this thread found several more.


Alright, so there are simply too many unique functions accessible through the math.js API, for blacklisting to be feasible.

I'm curious why he didn't go down the whitelisting path in the first place. Basic maths don't require that many functions anyway, so he could've started with a small list, and expanded it as people asked for more. Alternatively he could have allowed for custom whitelists.


Thanks for your suggestions. I'm the author of math.js working on these fixes. If feasible, a whitelist is safer than a blacklist of course. However, math.js supports objects like JavaScript, and these objects can have arbitrary properties. A naive whitelist would simply render these completely blocked. An option could be to try to distinguish between own properties and inherited properties but that gives issues too with regular methods on classes. It's not so easy to "just" create a whitelist and put it in place (and... which place or places?). I try to understand the underlying cause and try to put security measures at the right place rather than a hail-shot all over the place. I'm no security expert so it's a bit of a search. I'm really happy with all people helping to find and report more issues in this regard!

A first approach was to try to put security checks right before executing any function. That didn't work out since the parser doesn't have control over all function executions: for example not over the ones invoked by Array.forEach and Array.map. A second approach was to blacklist the "constructor" property since all issues did go via constructor and managed to call Function that way. That wasn't enough either. Current approach is to guard the values of symbols and properties (the places where unknown stuff can come in) and test whether there value equals Function. To be continued I think...


The math.js api appears to be broken right now, it just says "error: method.apply is not a function"


Probably related to the second exploit which used apply.


That's the definition of a clean presentation. Very clean. Well done.


ITYM "epitome", not "definition".


No, people use 'definition' in that way too.


In fact, albeebe1 just did.


Nice article. I wrote that lua "wrapper" 2 years ago. I used math.js to avoid RCE on bots, turns out math.js API is vulnerable but doesn't affect the wrapper.


Did you also write the gnuplot plugin? Because that's also vulnerable, as found by the same @denysvitali: https://github.com/LucentW/s-uzzbot/issues/9


That was from @francesco-p (he renammed his account from psykomantis) https://github.com/yagop/telegram-bot/commit/89b92b4cbf81ce1... its in my repo but disabled by default.



> Gist [here](https://gist.github.com/CapacitorSet/c41ab55a54437dcbcb4e627...

An unmatched left parenthesis creates an unresolved tension that will stay with you all day.


Whoops, thank you - it went unnoticed because I didn't proofread the noscript version as much as the default one. I pushed an edit.


So the vulnerability is online in the service of mathjs.org:

  Math.js is available as a RESTful web service:   http://api.mathjs.org


Yeah - or anyone that doesn't sanitize input before pushing it through mathjs.


EDIT: removed my suggestion since it was unsafe. Thanks for pointing it out.

I was hoping vm to offer you an isolated v8 interpreter without bindings that could used as a sandbox, but this wasn't the case.


The page explicitly says: "Note: The vm module is not a security mechanism. Do not use it to run untrusted code."

https://nodejs.org/api/vm.html#vm_vm_executing_javascript


Though the docs pretty clearly state "Note: The vm module is not a security mechanism. Do not use it to run untrusted code."




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: