
How we exploited a code execution vulnerability in math.js - CapacitorSet
https://capacitorset.github.io/mathjs/
======
comex
I was bored so I found a bunch more:

[https://github.com/josdejong/mathjs/issues/821](https://github.com/josdejong/mathjs/issues/821)

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

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

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

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

------
emmab
Blacklists are a losing game. Always use a whitelist.

~~~
CapacitorSet
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)](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](https://github.com/josdejong/mathjs/issues/821)).

~~~
jmaa
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?

~~~
CapacitorSet
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.

~~~
jmaa
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.

~~~
josdejong
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...

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

~~~
catmanjan
Probably related to the second exploit which used apply.

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

~~~
jwilk
ITYM "epitome", not "definition".

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

~~~
eru
In fact, albeebe1 just did.

------
yagop
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.

~~~
CapacitorSet
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](https://github.com/LucentW/s-uzzbot/issues/9)

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

------
jwilk
> Gist
> [here]([https://gist.github.com/CapacitorSet/c41ab55a54437dcbcb4e627...](https://gist.github.com/CapacitorSet/c41ab55a54437dcbcb4e62713a195822)

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

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

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

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

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

------
partycoder
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.

~~~
fransr
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](https://nodejs.org/api/vm.html#vm_vm_executing_javascript)

