Почему анализ защищенности JavaScript нельзя по настоящему автоматизировать?
Почему в случае JavaScript приходится обходиться простыми подходами статического анализа, когда есть более интересные подходы к автоматическому анализу кода?
В ответ на этот вопрос, мой коллега Алексей Гончаров kukumumu ответил лаконично: «JavaScript это панковский язык» и кинул ссылку на статью Jasper Cashmore «A Javascript journey with only six characters», которая действительно погружает нас в путешествие в эзотерический мир JSFuck и сразу все ставит на свои места. Мне настолько понравилось, что я решил перевести статью на русский язык.
Перевод статьи “A Javascript journey with only six characters”
Javascript — это странный и замечательный язык, позволяющий писать чокнутый код, который все равно работает. Он пытается помочь нам, преобразуя данные в определенные типы на основе того, как мы обращаемся с ними.
Если добавим знаки плюса или минуса перед чем-то, JS предположит, что мы хотим добавить текст, и преобразует тип данных в String . JS решит, что мы имеем в виду число, и данные переведутся в тип Number (если это возможно).
Если мы будем отрицать какие-то данные, они переведутся в Boolean . Мы можем использовать JS, чтобы вытворять всякие магические вещи, используя только символы [, ] , ( , ) , ! и + .
Если вы читаете это не с мобильного устройства, можете открыть JS-консоль, чтобы проследить за рассказом и проверить работоспособность приведенных примеров, просто скопировав их.
Начнем с основ. Несколько золотых правил, которые нужно запомнить:
Начав с ! получаем тип Boolean Начав с + получаем тип Number Добавляя [] получаем тип String
Вот эти правила в действии:
Еще одна вещь которую важно знать, — это то, что можно возвращать определенные символы из строк используя скобки, вот таким образом:
Кроме того, помните, что можно получать числа, добавляя их составляющие в виде строк, а потом переведя результат в тип Number с помощью правила №2:
Вот так. Теперь давайте скомбинируем все вышеперечисленное, чтобы получить символ a .
Получается, что с относительно простыми комбинациями мы можем получить любую из букв, составляющих слова true и false . a , e , f , l , r , s , t , u . Как же мы можем получить остальные буквы?
Ну, есть, например, undefined , который мы можем получить, написав глупости типа [][[]] . Переводим в тип String , используя одно из наших Золотых Правил, и дополнительно получаем буквы d , i и n .
Из всех букв, которые у нас уже есть, мы можем получить такие слова, как fill , filter и find . Конечно, можно получить и другие, но эти примечательны тем, что являются методами массивов. Это значит, что они являются объектами Array и могут быть вызваны напрямую в массивах, например, [2,1].sort() .
Другая важная вещь, которую нужно знать о JS, — это то, что свойства объекта могут быть доступны с помощью точечной или скобочной нотации. Так как упомянутые выше методы массива являются свойствами самого массива, мы можем вызывать эти методы, используя квадратные скобки вместо точечной нотации.
Так получается что [2,1]["sort"]() тоже самое что [2,1].sort() .
Давайте пойдем дальше и посмотрим, что получится, если мы попробуем использовать один из наших методов массива, записанный с помощью текущей коллекции букв, без его вызова.
Получается function fill() < [native code] >. Мы можем превратить этот метод в строку, используя наше золотое правило:
Вот так мы и получаем следующие символы: c , o , v , ( , ) , < , [ , ] , >, .
С новоприобретенными c и o мы можем сформировать слово constructor . constructor это метод, который имеют все объекты JS, возвращающий их функцию-конструктор.
Давайте получим в виде строки представление функций-конструкторов для объектов, с которыми мы до сих пор имели дело:
Так мы добавим в наш арсенал следующие символы: B , N , S , A , m , g , y .
Теперь мы можем создать " toString ", функцию, которую можно использовать с квадратными скобками.
Но мы ведь и так можем превратить что угодно в строку, используя наше золотое правило, так как же это может быть полезно?
Что если я скажу вам, что у метода toString типа Number есть секретный аргумент секретный аргумент под названием radix , который может изменить основание системы счисления заданного числа перед переводом в строку? Взгляните:
Но зачем останавливаться на 16? Максимум — это 36, что по сути дает нам все символы от 0-9 и a-z . Так что мы можем вызвать любую цифру или букву:
Замечательно! Но как насчет других символов типа заглавных букв и знаков препинания? Копаем глубже.
В зависимости от того, где выполняется ваш код, у вас может быть доступ к предустановленным объектам или данным. Если вы запускаете код в браузере, велики шансы, что у вас есть доступ к некоторым методам обертки HTML.
Например, bold — это метод String который добавляет теги < b > .
Это дает нам символы < , > и / .
Она конвертирует строку в URI-совместимый формат, который простые браузеры в состоянии переварить. Эта функция — важная часть нашего квеста, так что нам нужно получить к ней доступ. Мы можем ее написать, но сможем ли мы ее выполнить? Это не типичная функция, как все предыдущие, а функция глобального уровня.
Что представляет собой конструктор функции?
Ответ — function Function() < [native code] >, сам объект Function и есть конструктор.
Используя это, мы можем передать строку кода для создания функции.
Уже этот код мы способны вызвать, просто используя () в конце. Так что теперь мы используем функцию escape следующим образом:
Если мы передаем нашу < описанную ранее в функцию escape, мы получаем %3C . Это заглавная C очень важна, чтобы получить остальные символы, которых нам не хватает.
Используя ее, мы можем написать написать функцию fromCharCode , которая возвращает символы Unicode из заданного десятичного представления. Это часть объектов String , которую мы можем получить точно так же, как делали ранее.
Мы можем проверить любые десятичные представления символов Юникод здесь: Unicode lookup.
Теперь у нас есть возможность вызвать почти любой символ на свете, составить из них код и даже выполнить. Это значит, что мы получаем полноту по Тьюрингу в Javascript, используя всего шесть символов: [ , ] , ( , ) , + и ! .
Хотите доказательство? Запустите этот код в своем браузере:
Если вы читаете это с мобильного, код выше это alert(«wtf»).
Есть даже тулза под названием JSFuck которое автоматизирует преобразование, а тут можно посмотреть, как она переводит каждый символ.
Какое практическое применение?Никакое. Никак! Правда, недавно eBay сделал несколько плохих вещей, благодаря которым продавцы теперь могут внедрять JS-код в свои страницы, используя только эти символы, но это достаточно необычный вектор атаки. Еще некоторые люди вспомнят про обфускацию, но, будем честны, есть способы обфускации получше этого.