Pull to refresh

Почему анализ защищенности JavaScript нельзя по настоящему автоматизировать?

Reading time 8 min
Views 7.5K
Почему в случае JavaScript приходится обходиться простыми подходами статического анализа, когда есть более интересные подходы к автоматическому анализу кода?

В ответ на этот вопрос, мой коллега Алексей Гончаров kukumumu ответил лаконично: «JavaScript это панковский язык» и кинул ссылку на статью Jasper Cashmore «A Javascript journey with only six characters», которая действительно погружает нас в путешествие в эзотерический мир JSFuck и сразу все ставит на свои места.
Мне настолько понравилось, что я решил перевести статью на русский язык.


image

Перевод статьи “A Javascript journey with only six characters”


Javascript — это странный и замечательный язык, позволяющий писать чокнутый код, который все равно работает. Он пытается помочь нам, преобразуя данные в определенные типы на основе того, как мы обращаемся с ними.

Если добавим знаки плюса или минуса перед чем-то, JS предположит, что мы хотим добавить текст, и преобразует тип данных в String.
JS решит, что мы имеем в виду число, и данные переведутся в тип Number (если это возможно).

Если мы будем отрицать какие-то данные, они переведутся в Boolean.
Мы можем использовать JS, чтобы вытворять всякие магические вещи, используя только символы [,],(,),! и +.

Если вы читаете это не с мобильного устройства, можете открыть JS-консоль, чтобы проследить за рассказом и проверить работоспособность приведенных примеров, просто скопировав их.

Начнем с основ. Несколько золотых правил, которые нужно запомнить:

Начав с ! получаем тип Boolean
Начав с + получаем тип Number
Добавляя [] получаем тип String

Вот эти правила в действии:

![] === false
+[] === 0
[]+[] === ""

Еще одна вещь которую важно знать, — это то, что можно возвращать определенные символы из строк используя скобки, вот таким образом:

"hello"[0] === "h"

Кроме того, помните, что можно получать числа, добавляя их составляющие в виде строк, а потом переведя результат в тип Number с помощью правила №2:

+("1" + "1") === 11

Вот так. Теперь давайте скомбинируем все вышеперечисленное, чтобы получить символ a.

![] === false
![]+[] === "false"
+!![] === 1
------------------------
(![]+[])[+!![]] === "a" // same as "false"[1]

Клево!

Получается, что с относительно простыми комбинациями мы можем получить любую из букв, составляющих слова true и false. a,e,f,l,r,s,t,u. Как же мы можем получить остальные буквы?

Ну, есть, например, undefined, который мы можем получить, написав глупости типа [][[]]. Переводим в тип String, используя одно из наших Золотых Правил, и дополнительно получаем буквы d,i и n.

[][[]] + [] === "undefined"

Из всех букв, которые у нас уже есть, мы можем получить такие слова, как fill, filter и find . Конечно, можно получить и другие, но эти примечательны тем, что являются методами массивов. Это значит, что они являются объектами Array и могут быть вызваны напрямую в массивах, например, [2,1].sort().

Другая важная вещь, которую нужно знать о JS, — это то, что свойства объекта могут быть доступны с помощью точечной или скобочной нотации. Так как упомянутые выше методы массива являются свойствами самого массива, мы можем вызывать эти методы, используя квадратные скобки вместо точечной нотации.

Так получается что [2,1]["sort"]() тоже самое что [2,1].sort().

Давайте пойдем дальше и посмотрим, что получится, если мы попробуем использовать один из наших методов массива, записанный с помощью текущей коллекции букв, без его вызова.

[]["fill"]

Получается function fill() { [native code] }. Мы можем превратить этот метод в строку, используя наше золотое правило:

[]["fill"]+[] === "function fill() { [native code] }"

Вот так мы и получаем следующие символы: c,o,v,(,),{,[,],}, .

С новоприобретенными c и o мы можем сформировать слово constructor. constructor это метод, который имеют все объекты JS, возвращающий их функцию-конструктор.

Давайте получим в виде строки представление функций-конструкторов для объектов, с которыми мы до сих пор имели дело:


true["constructor"] + [] === "function Boolean() { [native code] }"  
0["constructor"] + []    === "function Number() { [native code] }"  
""["constructor"] + []   === "function String() { [native code] }"
[]["constructor"] + []   === "function Array() { [native code] }"

Так мы добавим в наш арсенал следующие символы: B,N,S,A,m,g,y.

Теперь мы можем создать "toString", функцию, которую можно использовать с квадратными скобками.

(10)["toString"]() === "10"

Но мы ведь и так можем превратить что угодно в строку, используя наше золотое правило, так как же это может быть полезно?

Что если я скажу вам, что у метода toString типа Number есть секретный аргумент секретный аргумент под названием radix , который может изменить основание системы счисления заданного числа перед переводом в строку? Взгляните:

(12)["toString"](10) === "12" // base 10 - normal to us
(12)["toString"](2) === "1100" // base 2, or binary, for 12
(12)["toString"](8) === "14" // base 8 (octonary) for 12
(12)["toString"](16) === "c" // hex for 12

Но зачем останавливаться на 16? Максимум — это 36, что по сути дает нам все символы от 0-9 и a-z. Так что мы можем вызвать любую цифру или букву:

(10)["toString"](36) === "a"
(35)["toString"](36) === "z"

Замечательно! Но как насчет других символов типа заглавных букв и знаков препинания? Копаем глубже.

В зависимости от того, где выполняется ваш код, у вас может быть доступ к предустановленным объектам или данным. Если вы запускаете код в браузере, велики шансы, что у вас есть доступ к некоторым методам обертки HTML.

Например, bold — это метод String который добавляет теги <b>.

"test"["bold"]() === "<b>test</b>"

Это дает нам символы <, > и /.

Она конвертирует строку в URI-совместимый формат, который простые браузеры в состоянии переварить. Эта функция — важная часть нашего квеста, так что нам нужно получить к ней доступ. Мы можем ее написать, но сможем ли мы ее выполнить? Это не типичная функция, как все предыдущие, а функция глобального уровня.

Что представляет собой конструктор функции?

Ответ — function Function() { [native code] }, сам объект Function и есть конструктор.

.
[]["fill"]["constructor"] === Function

Используя это, мы можем передать строку кода для создания функции.

Function("alert('test')");  

Получается:

Function anonymous() {  
    alert('test')
}

Уже этот код мы способны вызвать, просто используя () в конце.
Так что теперь мы используем функцию escape следующим образом:

[]["fill"]["constructor"]("return escape(' ')")() === "%20"

Если мы передаем нашу < описанную ранее в функцию escape, мы получаем %3C. Это заглавная C очень важна, чтобы получить остальные символы, которых нам не хватает.

[]["fill"]["constructor"]("return escape('<')")()[2] === "C"

Используя ее, мы можем написать написать функцию fromCharCode, которая возвращает символы Unicode из заданного десятичного представления. Это часть объектов String, которую мы можем получить точно так же, как делали ранее.

""["constructor"]["fromCharCode"](65) === "A"
""["constructor"]["fromCharCode"](46) === "."

Мы можем проверить любые десятичные представления символов Юникод здесь: Unicode lookup.

Фух. Вроде все!

Теперь у нас есть возможность вызвать почти любой символ на свете, составить из них код и даже выполнить. Это значит, что мы получаем полноту по Тьюрингу в Javascript, используя всего шесть символов: [,],(,),+ и !.

Хотите доказательство? Запустите этот код в своем браузере:

Раскрыть код
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+(+(!+[]+!+[]+!+[]+[!+[]+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(+![]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(+![]+[![]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]+!+[]])+(!![]+[])[+[]]+(![]+[])[+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()

Если вы читаете это с мобильного, код выше это alert(«wtf»).

Есть даже тулза под названием JSFuck которое автоматизирует преобразование, а тут можно посмотреть, как она переводит каждый символ.

Какое практическое применение?


Никакое. Никак! Правда, недавно eBay сделал несколько плохих вещей, благодаря которым продавцы теперь могут внедрять JS-код в свои страницы, используя только эти символы, но это достаточно необычный вектор атаки. Еще некоторые люди вспомнят про обфускацию, но, будем честны, есть способы обфускации получше этого.

Извините.

Надеюсь вам понравилось путешествие!

Спасибо VladimirKochetkov и Ксеня Кириллова за помощь с переводом.
Tags:
Hubs:
+8
Comments 20
Comments Comments 20

Articles

Information

Website
www.ptsecurity.com
Registered
Founded
2002
Employees
1,001–5,000 employees
Location
Россия