Статический анализ исходного кода веб-сайта в браузере

Источник: https://medium.com/@_bl4de/how-to-perform-the-static-analysis-of-website-source-code-with-the-browser-the-beginners-bug-d674828c8d9a

В этом руководстве я (здесь и далее «я» - это автор bl4de) покажу вам как использовать встроенные в веб-браузер инструменты для изучения исходного кода клиентской части веб приложения. Это может показаться странным, поскольку вероятно браузер это не самый лучший выбор для такой задачи, но перед тем как вы углубитесь и начнёте перехватывать HTTP запросы используя Burp Suite или тыкать здесь и там alert(1) для бесконечных XSS, вначале нужно как следует изучить свою цель — это всегда хорошая идея.

Этот пост предназначен преимущественно для начинающих охотников участвующих в bug bounty (поиск ошибок и уязвимостей за вознаграждение) без или с небольшим опытом в анализе HTML и JavaScript кода, но я надеюсь, что более опытные хакеры также найдут что-то интересное 🙂

Идея этого поста родилась в моей голове когда один из моих недавних твитов с совсем простой подсказкой собрал много внимания в сообществе:

Если вы открыли url в браузере и ничего не увидели кроме пустой страницы, не надо думать, что там ничего нет. ВСЕГДА проверяйте исходный код. Там может быть пучок JavaScript или к примеру закомментированный исходный код от сервера:

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

Итак, давайте тогда начнём!

Набор инструментов

Любой современный веб-браузер сегодня имеет набор встроенных инструментов разработчика. Для их включения вы можете использовать Ctrl+Shift+I, CMD+Option+I (macOS), кнопку F12 или просто найдите нужную опцию в меню браузера — это зависит от операционной системы и браузера, которые вы используете. Хотя в этом посте я буду использовать последнюю версию Chromium, нет больших различий (кроме пользовательского интерфейса) если вы используете Firefox, Safari, Chrome или Edge. Это ваш выбор, но я нашёл Chrome Developer Tools самыми функциональными (Chrome Developer Tools или просто DevTools доступны в Chrome, Chromium, Brave, Opera или в любом другом основанном на Chromium браузере). Инструменты разработчика доступны по умолчанию, то есть их не нужноу станавливать отдельно. Если у вас есть веб-браузер — значит у вас уже есть Инструменты разработчика.

Другой инструмент, который неплохо было иметь установленным, это IDE (интегрированная среда разработки) или любой редактор кода с подсветкой синтаксиса HTML и JavaScript. Всё зависит от ваших предпочтений, вы можете выбрать Visual Studio Code. VSCode доступна для основных операционных систем по адресу: https://code.visualstudio.com/. Полностью бесплатная, с открытым исходным кодом IDE — это NetBeans, поддерживает несколько языков программирования, в том числе подсветку синтаксиса HTML и JavaScript. Адрес для скачивания NetBeans: https://netbeans.org/

Также неплохо бы установить NodeJS (и ознакомиться с её работой — для этого буквально тысячи отличных источников в Интернете). Адрес: https://nodejs.org/en/download/package-manager/

Интерпретатор Python это следующая обязательная для меня вещь (если вы используете операционную систему на основе *NIX, то вероятно у вас он уже установлен. Если вы пользователь Windows, то вам нужно самостоятельно установить Python). Возможность программировать на Python бесценна, и я рекомендую каждому попробовать это даже если вы никогда не написали ни одной строчки кода на любом языке программирования.

NodeJS очень полезен для запуска и тестирования кода JavaScript в терминале (этого же самого вы можете достигнуть используя браузер, но мы вернёмся к этому позже и обсудим некоторые плюсы и минусы). Python полезен для создания ваших собственных скриптов, таких как инструменты, быстрые рабочие концепты (Proof of Concept) и настоящие эксплойты — я также позже в этой статье презентую некоторые из моих собственных инструментов. Если вы лучше знакомы с другим интерпретируемым языком (Ruby, PHP, Perl, Bash и так далее), вы также можете использовать их по своему желанию. Главное преимущество таких языков в том, что скрипт может быть запущен без компиляции, прямо из командной строки, они на 100% портативны между различными платформами и имеют множество функций, встроенных в тысячи библиотек или модулей, доступных по всему вебу.

Окей, наконец-то пришло время взяться за работу.

Исследование исходного кода HTML

Давайте не на долго вернёмся к моему твиту, которы я процитировал ранее. Как вы можете заметить, скриншот демонстрирует веб-сайт, который выглядит как будто на нём нет контента, только белая, пустая страница.

Но если вы взгляните на исходный код (используя CTRL+U или CMD+Option+U на macOS) вы можете увидеть изобилие кода (к сожалению, я не могу дать url сайта со скриншота, поскольку он взят с приватной программы bug bounty (награждение за найденные уязвимости). Почему эти элементы не могут быть видны в браузере?

Нужно понимать, что некоторые HTML тэги не рендерят (не показывают) какой-либо контент на странице. Их несколько, но самые распространённые это <html>, <head>, <body>, <style> или <script>. Также CSS позволяет скрывать элементы (например, устанавливая их ширину и высоту в CSS свойствах на 0 или устанавливая display на none).

Рассмотрим следующий пример:

<html>
<head>
    <title>Move along, nothing to see here!</title>
    <style>
    /* note to myself: add CSS from Bob's repo: https://verysecurecompany.com/__internal__/repo/bob/specs.git */
    * {
        font-size:16px;
        color: #c0c0c0;
    }
    </style>
</head>
<body>
    <iframe src="https://verysecurecompany.com/__internal__/loginframe.html" style="width:0;height:0" frameborder="0" id="you-cant-see-me"></iframe>
    <script>
        // a hidden feature
        console.log('Diagnostic message: username is admin and password is password :)');    
    </script>
</body>
</html>

Если вы откроете эту простую HTML в браузере, то не будет показано никакой контент и вы ничего не увидите внутри неё. Но если вы взгляните на исходный код, то всё становится намного интереснее (используя CTRL+U или CMD+Option+U (macOS) вы можете видеть исходный код веб-сайтов):

Здесь так много ценной информации: URL на внутренние ресурсы, «скрытый» frame с формой входа и даже диагностическое сообщение с некоторыми учётными данными, которые будут напечатаны в консоли инструментов разработчика. Ни один из этих элементов не является видимым на странице. Конечно, не ожидайте, что вы будете находить подобную информацию на каждом веб-сайте, тем не менее, закомментированные фрагменты кода JavaScript очень распространены и иногда они раскрывают реальные конечные точки API, которые ещё доступны на стороне сервера этого приложения.

Но опция Посмотреть код страницы не даёт полной картины, поскольку в ней присутствует только текущий HTML документ. Что намного интереснее так это все дополнительные ресурсы, загруженные <iframe>,<script> или подобными тэгами. Вы можете увидеть эти ресурсы в панели Sources в Инструментах разработчика (Chrome Developer Tools) (вкладка Sources позволяет вам видеть все ресурсы, загруженные вебсайтом):

В дереве вы можете увидеть узел (index) — это главный HTML документ, который вы также можете увидеть с опцией Просмотр исходного кода страницы. Все другие ресурсы присутствуют как стандартное дерево папок и файлов. Если вы кликните на любой из этих файлов, с правой стороны вы увидите его содержимое. На скриншоте вы можете увидеть файл jquery.min.js — это обычная практика делать минимизированные версии всех файлов JavaScript (это хорошо с точки зрения производительности веб приложения). Но если вы кликните на маленькую иконку {} внизу, то DevTools покажут код в намного более читаемом виде.

Некоторые вебсайты используют специальную функцию, известную как карта ресурсов — source map (карта ресурсов используется для сопоставления имён минимизированных функций, переменных и объектов с их «реальными» именами, а также позицией в оригинальном исходном коде — о карте ресурсов вы можете найти больше здесь. Карты ресурсов делают форматированный код ещё более читаемым предоставляя осмысленные наименования для всех объектов, вместо сокращённых идентификаторов от минимизатора JavaScript.

Другой очень мощной возможностью, доступной в этой вкладке, является глобальный поиск. Давайте предположим вы обратили внимание на определение какой-нибудь интересной функции и вы хотите узнать, вызывается ли она где-нибудь ещё в коде. Может быть это функция содержит вызов eval() с параметром, передаваемым в URL, который затем может быть использован для выполнения произвольного кода JavaScript. Для выполнения поиска по всем файлам, перечисленным во вкладке Sources, вы можете использовать сокращение CTRL+Shift+F (CMD+Option+F на macOS). В следующем примере я пытаюсь найти все отсылки к функции getAccount() из файла AppMeasurement.js. Как вы можете увидеть, эта функция вызывается только один раз, в том же самом файле, но если бы были какие-либо другие совпадения искомой строки в любом из файлов — мы бы их увидели в списке результатов:

Иногда вы обнаружите, что результаты поиска были найдены в очень, очень длинной строке (обычно минимизированный файл JavaScript). Вы можете просто кликнуть на нём и когда DevTools откроет этот файл, кликните иконку {} и она покажет вам удобно читаемую версию с вашим результатом — прямо в нужном месте, даже если этот файл содержит тысячи строк.

Вторая вкладка, где вы можете исследовать исходный код, называется Elements. Имеется одно, но очень важное различие между тем, что может быть увидено в файле (index) вкладки Sources (и от того, что вы можете увидеть используя опцию Просмотр исходного кода) и содержимым, представленным в Elements.

(index) и «Просмотр исходного кода» показывают загруженный с сервера файл HTML. А Elements показывают вам текущее дерево DOM со всеми элементами, созданными и добавленными кодом JavaScript. Для понимания разницы, я покажу другой маленький пример, но сначала теория.

DOM (Document Object Model - «объектная модель документа») - это действительное представление всех HTML узлов вебсайта. Это дерево с единым корневым элементом (<html>), с двумя главными дочерними элементами: <head> и <body>. Все другие элементы являются дочерними либо для <head> (как <title> или <meta>), либо <body> (<div>, <p>, <img> и так далее).

Когда в своём браузере вы открываете URL, в начале загружается HTML файл и код разбирается движком браузера. Когда браузер находит теги <script> или <style> (или любой другой тэг с аргументом src, такой как image или video файл), он останавливает разбор HTML и загружает этот файл. Если этот файл является исполнимым JavaScript, то он исполняется немедленно. Если это таблица стилей, CSS правила применяются сразу, как только они разбираются CSS парсером. Всё вместе выглядит как на этой картинке (это сильно упрощённая, но достаточно хорошая для понимания концепции):

Источник: https://www.sitepoint.com/optimizing-critical-rendering-path/

Но какое это имеет отношение к различиям между содержанием Elements и Sources? Рассмотрим следующий пример, где JavaScript добавляет элемент в DOM:

<html>
<head>
    <title>Dynamic P Application</title>
    <style>
    * {
        font-size:18px;
        font-weight:bold;
        color: #2e2e2e;
    }
    </style>
</head>
<body>
    <div id="container">

    </div>
    <script>
        const el = document.getElementById('container')
        const dynamic_paragraph = document.createElement('p')
        const dp_content = document.createTextNode('Hello from dynamically added <P>aragraph!')

        dynamic_paragraph.appendChild(dp_content)
        el.appendChild(dynamic_paragraph)
    </script>
</body>
</html>

Когда вы открываете эту страницу в браузере и исследуете исходный код, вы увидите в точности то же самое, что и представлено на скриншоте выше:

Это весьма простой пример как JavaScript добавляет новый элемент в дерево DOM. Чтобы увидеть различие, используйте вкладку Elements в DevTools:

Когда вы сравните что присутствует в Elements с версией, которую можно увидеть используя Посмотреть исходный код страницы, вы с лёгкостью обратите внимание на разницу: в первом случае виден элемент <p>, добавленный как дочерний узел элемента <div id="container">. Ранее вы не могли видеть этот элемент в исходном коде, поскольку он не существует как часть исходного кода.

Если вы имеете дело с Single Page Application (Приложением в одну страницу) которое использует одну из платформ, такую как AngularJS, React, Vue.js, Ember.js и так далее, во вкладке Elements вы увидите множество динамически генерируемого контента. Этот контент может различаться, но обычно там формы, динамические таблицы или списки с сортировкой и пагинацией, функции поиска и изобилие элементов где, к примеру, можно найти DOM-based XSS или пользовательский ввод может обрабатываться в шаблонных выражениях (наподобии {{ }} в AngularJS).

Такие приложения часто используют параметры передаваемые через GET или POST запросы или сохраняют в кукиз или хранилищах браузера для построения контента вебсайта. Также они создают множество своего собственного контента. Здесь всегда есть шанс найти уязвимость.

Перед тем, как мы перейдём к самому JavaScript, ещё одна важная вещь, которую стоит упомянуть здесь — всегда читайте все HTML комментарии, которые вы найдёте в исходном коде, поскольку они также не выводятся на странице. Вы удивитесь, как много вы можете найти здесь ценной информации.

Исследование кукиз и Хранилища браузера

Следующее что вы можете делать с Developer Tools это проверять, сохраняет ли веб сайт какую-либо информацию на клиентской стороне. Есть пара мест, которые веб приложение может использовать. Самое частое и популярное — это cookies (кукиз) — маленькие кусочки данных, идентифицируемые по имени (вы можете представлять кукиз как простые пары ключ → значение) и обмениваемые между сервером и браузером в HTTP запросах и ответах.

Browser Storage (Хранилище браузера) — это другое место, где вы можете найти множество ценной информации. Имеется два вида Хранилища: Local Storage (Локальное хранилище) и Session Storage (Сессионное хранилище). Различия между ними в том, что содержимое Сессионного хранилища теряется когда закрывается приложение (при закрытии вкладки браузера или самого браузера). Локальное хранилище способно хранить данные так долго, пока оно не будет очищено явным образом (у данных нет времени экспирации — то есть времени «протухания», автоматического удаления после определённого периода).

Для просмотра всей информации, сохранённой в различных местах, вы можете использовать вкладку Application в DevTools:

Используя вкладку Application вы можете не только просматривать контент, вы также можете его модифицировать, удалять или добавлять свои собственные пары ключей и соответствующих значений и смотреть, способны ли вы вызвать неожиданное поведение или даже уязвимость. Это самый простой способ проверить, допустим, с помощью изменения маркера сессии (session token) получится ли выдать себя за другого пользователя — просто поменяйте значение куки, которое ответственно за хранение идентификатора сессии (это только пример поскольку многие современные веб приложения используют несколько способов идентификации пользователей и в большинстве случаев изменение значения одной куки недостаточно для перехвата (hijack) сессии другого пользователя):

На скриншоте выше значение куки TS0105bb97 изменено непосредственно в Application → вкладка Cookies в DevTools.

Есть ещё одно место в этой вкладке, где вы можете найти исходный код JavaScript относящийся к веб приложению: Service Workers. Это одно из многих введений в Service Workers которые вы можете найти в сети: https://developers.google.com/web/fundamentals/primers/service-workers/

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

Исследование JavaScript

Теперь давайте перейдём к той части кода, которая на самом деле обеспечивает работу всего веб приложения (HTML и CSS ответственны только за визуальную часть, поскольку они не могут содержать какой-либо бизнес логики. Кроме некоторых небольших исключений как CSS выражения, которые способны запускать JavaScript код и таким образом могут быть источником XSS уязвимости если в одной из них используется пользовательской ввод — в целом, вы не особо много можете сделать с обычным HTML и CSS).

Есть несколько способов выполнить анализ кода JavaScript. Давайте вначале остановимся на браузере. Мы уже открыли для себя вкладку Sources и как возможность {} используется для преобразования минимизированный исходный код в удобочитаемый. Но с DevTools вы можете делать ещё больше и одна из лучших вещей это JavaScript debugger (отладчик).

Использование отладчика DevTools debugger

Если вы не знакомы что такое отладка, то в целом это возможность приостановить выполнение программы на определённой строке кода. Это позволяет вам видеть текущее значение переменных, какие функции сейчас выполняются и как эти функции были вызваны (это доступно благодаря стеку вызова — отладчик может показывать вам точный порядок вызова функций: как функция a() была вызвана функцией b(), а функция b() была вызвана другой функцией c() - это просто вам для примера). Также отладчик позволяет вам запускать код шаг за шагом (одна инструкция за раз) что даёт возможность следовать каждому изменению программы и её состоянию. Последнее в этом списке, но не последнее по значению, отладчик позволяет модифицировать программу «на лету», что означает, что вы можете видеть, что происходит когда вы модифицируете значения переменных или даже логику самой программы. Возможность использовать отладчик эффективным способом — это одно из самых важных умений, которое должен иметь программист.

С точки зрения охотника за багами (bug bounty hunter) отладка позволяет вам лучше понимать, как работает приложение и напрямую тестировать полезную нагрузку в месте, где она была внедрена. Вы с лёгкостью можете изолировать уязвимую часть программы и сфокусироваться на её тестировании со всеми преимуществами, которые даёт вам отладчик. В качестве примера представьте, что вы нашли уязвимую функцию редиректа (перенаправления), но каждый раз, когда вы пытаетесь увидеть, что именно происходит, вызывается эта функция и браузер выполняет перенаправление на внешний источник и вы не можете видеть какой код JavaScript остался на предыдущей странице, поскольку вы сразу оказываетесь на новой странице.

Установка точки остановки (breakpoint) в начале функции редиректа не позволит браузеру выполнить перенаправление и вы можете теперь прочитать исходный код функции для понимания, как он работает, узнать, как вы можете внедрить вашу полезную нагрузку, есть ли там какая-либо реализация кодирования и так далее.

Ну хватит теории, пришло время практики.

Это пример реализации функции редиректа:

<html>
<head>
    <title>Redirection</title>
</head>
<body>
    </div>
    <script>
        
        // представьте, что url считывается из  GET параметра...
        // но для упрощения мы просто записали её здесь в коде 
        const url = 'https://hackerone.com'
        

        function redirect() {
            // I will redirect you! Now!
            if (url) {
                location.href = url
            } else {
                location.href = 'https://company.com/__internal__/supersecretadminpanel'
            }
        }


        setTimeout( redirect, 10000 )
    </script>
</body>
</html>

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

Откройте Developer Tools в браузере и переключитесь на вкладку Sources, затем откройте приведённый выше файл HTML. Теперь у вас есть 10 секунд для установки точки остановки в строке номер 16 ( if (url) { ). Чтобы это сделать, просто кликните 16 в левом баре с номерами строк. Когда пройдёт 10 секунд, браузер вызовет redirect() и немедленно остановит выполнение на строке, где установлена точки разрыва (наше простое приложение с редиректом остановлено в отладчике):

Голубая строка — это та самая строка кода, которая будет выполнена когда мы продвинемся далее (она ещё не выполнена! Это очень важно знать). Слева вы можете видеть панель отладки — она показывает где вы сейчас (Call stack) и если вы раскроете узел Script, вы увидите значения всех переменных, которые были определены в текущей области выполнения (в нашем случае это только url).

Теперь вы можете потратить так много времени, сколько вам нужно для чтения и понимания кода. Как мы уже знаем, мы будем перенаправлены на вебсайт HackerOne, но только если условие в строке 16 вернёт true (истина).

Тогда давайте поменяем значение url. Мы поменяем её на что-то, что вернёт false (ложь) в JavaScript чтобы условие не выполнялось (это может быть пустая строка, 0, булевое false или любое другое выражение, которое сводится к false). Остановимся на false (для изменения значения переменной, просто кликните на ней и введите своё собственное значение):

Теперь давайте проверим, что изменилось. Мы будем продвигаться далее шаг за шагом. Взгляните на иконки сверху панели отладчика:

Первая иконка — это просто продолжение работы программы, вторая позволяет выполнять код шаг за шагом (мы будем использовать именно её). Следующая иконка позволяет перепрыгнуть в функцию, вызываемую в определённой строке (отладчик не заходит в функцию сам пока там не установлена точка разрыва, он просто выполняет строку с вызовом функции и переходит на следующую строку), затем там есть иконка выхода назад из выполняемой функции и возврат к точке, где она была вызвана.

Теперь кликните по второй иконке (убедитесь, что вы поменяли значение переменной url на false и вы заметите, что следующая строка для выполнения это 19 (это потому что на строке 16 результатом условия стало наше false):

Мы только что поменяли поток выполнения приложения.

Если вы нажмёте иконку первой кнопки на панели отладки (иконка воспроизведения-паузы) вы заметите, что в этот раз приложение попытается перенаправить вас на внутренний url в company.com.

Это даёт нам идею, что url параметр может быть уязвимым, поэтому теперь вы можете попытаться эксплуатировать эту особенность для поиска Open Redirection или Reflected XSS или углубиться в случае если вы ожидаете, что значение из url может сохраняться где-то на стороне сервера (вы можете узнать является ли это так продолжая исследовать логику программы).

Выполнение JavaScript используя Snippets

Иногда вы можете захотеть выполнить только определённый фрагмент кода приложения. Это может быть трудно и потребует много времени, особенно когда должна быть сдела предварительная работа, чтобы получить точку, которая представляет интерес. В этом случае вы можете использовать Snippets и запускать только код, который хотите. Но помните, что это не всегда возможно, например, когда вы пытаетесь запустить часть кода, которая имеет зависимости, такие как переменные, передаваемые из других частей или фрагменты кода, которые вы хотите запустить, используют другие функции, определённые в других файлах.

Но давайте предположим, что вы идентифицировали функцию которая проверяет, является ли предоставляемая величина верной, и вы хотите сфокусироваться только на её логике.

Во вкладке Sources вы найдёте панель под названием Snippets:

Когда вы на неё кликните, вы увидите список снипетов (если вы уже создали их) и опцию по созданию нового. Кликните на эту опцию и в средней панели вы найдёте простой редактор кода с подсветкой синтаксиса. Каждый код JavaScript, который вы разместите здесь, может быть запущен и результат будет немедленно выведен в консоль, которая появится снизу как только вы запустите ваш снипет. Для этого вы можете использовать иконку «Воспроизвести» внизу слева этой панели или просто нажмите CMD+Enter на macOS или CTRL+Enter на других системах).

Вы можете отредактировать ваш код и запускать его так много раз, как вы хотите. Но, как я упомянул в начале этого поста, есть некоторые «За» и «Против».

Снипеты запускаются в контексте загруженной во вкладке страницы где вы открыли DevTools и создали и запустили ваш снипет. Также каждый раз, когда вы запускаете ваш снипет, он работает используя тот же контекст. Это означает, что все переменные, которые вы задали ранее и не изменили их значения — находятся по прежнему здесь и хранят последнее значение, которое у них было.

Почему это важно?

Рассмотрим этот пример: снипет с константой запущен первый раз:

В JavaScript когда вы определяете константу используя ключевое слово const, вы должны инициализировать её значением и это значение не может позднее измениться. Как вы можете видеть, снипет работает как ожидалось, но если вы попытаетесь теперь изменить значение, которое использовалось для инициализации SOME_CONST и повторно запустите снипет, вы получите синтаксическую ошибку причиной которой является инициализация константы второй раз в том же самом контексте выполнения:

Причина ошибки в том, что в этом контексте SOME_CONST уже была инициализирована. DevTools просто «думает» что вы продолжаете выполнять код в том же контексте выполнения и не имеет значения, что вы отредактировали код.

Поэтому если вы с отладчиком, например, поставили на пузу запущенную программу (все определённые кодом приложения переменные, объекты и функции будут теперь существовать в контексте выполнения) и попытаетесь создать снипет в той же вкладке используя один из существующих идентификаторов, то вы либо перезапишите что-нибудь в оригинальном коде веб приложения или получите ошибку если используете идентификатор или что-то, что не может быть повторно инициализировано как в случае с константой). Чтобы была возможность повторно запустить снипет, вам нужно в начале перезагрузить страницу в браузере для получения свежего, пустого контекста выполнения (браузер не запоминает состояние веб приложения между перезагрузками, поскольку перезагрузка приводит к полному процессу перезагрузки ресурсов, построению дерева DOM и т. д.).

Чтобы избежать таких проблем, вместо использования Снипетов (которые могут быть весьма полезным инструментом поскольку вы можете просто открыть новую вкладку с DevTools и создать там ваш собственный снипет) вы можете запускать ваш код используя среду выполнения JavaScript NodeJS.

Чтобы это сделать, просто поместите код, который вы хотите запустить, в новый файл JavaScript и запустите его с NodeJS (убедитесь, что в начале вы установили её) используя терминал:

На скриншоте код запущен три раза, каждый раз с новым значением используем в инициализаторе SOME_CONST.

Я запускал этот код три раза, каждый раз меняя значение SOME_CONST. Как вы можете видеть, ошибок нет и каждое выполнение было успешным и вывело правильный результат.

Это поведение проистекает из природы NodeJS — каждый раз, когда вы запускаете код JavaScript используя его, он создаёт новый контент выполнения, поэтому нет возможности запускать тот же самый код дважды, используя одинаковый контент выполнения.

Источники и воронки выполнения

Когда вы смотрите на код JavaScript, есть две главные вещи, на которых вы должны фокусироваться в первую очередь.

Первое — это источники, этот термин описывает все точки, где полученный от пользователя ввод встречается с кодом приложения. Это в бизнес логике приложения может быть аргумент переданный через GET в url, куки считанное приложением или содержимое Локального хранилища.

Второе называется воронка выполнения (execution sink). Этот термин означает все элементы синтаксиса JavaScript или функции HTML API которые приводят к выполнению кода, переданного им в качестве аргумента. Один очивидный пример это JavaScript функция eval(код_для_выполнения) которая оценивает (выполняет) код переданный в неё как аргумента. Другой пример это setTimeout(функция_для_выполнения, таймаут_в_миллисекундах) которая выполняет функцию, переданную в качестве первого аргумента по прошествию определённого времени, указанного во втором аргументе.

Процесс обнаружения уязвимости в веб приложении — это поиск связей между источником и воронкой выполнения, которая в действительности обрабатывают этот источник. В примере где я показал как использовать отладчик, передаваемый как аргумент url (источник) был напрямую использован в location.href (воронка выполнения). Другим примером здесь может быть функция, которая выполняет данные, переданные пользователь в поле ввода HTML формы (JavaScript может читать их используя DOM API, к примеру document.getElementById(‘input_id’).value, и назначая их переменной, которая будет в этом случае источником) и затем передавать эту величину в другой метод элемента innerHTML(), обновляя текущий DOM в окне браузера (и это будет воронка выполнения).

Автор рекомендует видео https://www.youtube.com/watch?v=ZaOtY4i5w_U, но оно на английском языке. Суть видео: есть пользовательский ввод, а есть функции, в которых он может выполняться. При определённых условиях это можно использовать во вред — это и вызывает уязвимость, например, уязвимость выполнения проиозвольных команд.

Может быть множество источников и воронок выполнения в веб приложении в зависимости от его бизнес логики (от полей формы, URL параметров, кукиз, хранилища браузера, WebSockets etc.). Но самая важная вещь — используются ли они в качестве воронок выполнения. Их изобилие, включая свойства location, такие как hash, window.open(), document.write() или методы DOM как innerHTML или appendChild. Они все могут использоваться для выполнения произвольного кода, делать редиректы или выполнять другие типы инъекций.

Для простой идентификации таких образцов исходного кода я (в смысле bl4de) создал инструмент nodestructor. Он проверяет файлы JavaScript (как единичные файлы, так и все JavaScript файлы в директории, указанной как аргумент), находит образцы, которые хорошо известны как воронки выполнения (или источники). Не надо думать, что каждая строка кода, которую нашёл nodestructor, сразу или легко может быть эксплуатируемой — всё зависит от того, что между источником и воронкой выполнения (санация, кодирование, парсинг, трансформация данных в объект, манипуляция строкой и т. д.). Главная цель этого инструмента — предоставить простой в использовании и быстрый способ найти все паттерны в больших объёмах кода.

Давайте посмотрим быстрый пример использования. В начале нам нужен файл JavaScript для анализа. Файл, который я собираюсь проверить, это AppMeasurement.js с сайта GM.com, видимый нами ранее. Я просто копирую его из браузера (вначале переведя его в читаемый вид) и вставляю в редактор кода, затем сохраняю на локальном хранилище в папке /tmp.

В терминале я запускаю nodestructor в отношении файла AppMeasurement.js (с опцией -H для включения также поиска различных паттернов HTML5 API):

Как вы могли заметить, инструмент идентифицировал пару потенциальных воронок выполнения. Многие из них являются ложными срабатываниями, но для целей презентации давайте сфокусируемся на втором пункте, который выглядит как инициализация переменной domain с результатом функции location.hostname.toLowerCase().

Было бы неплохо иметь возможность проследовать по всем вхождениям этой переменной в файле для определения, используется ли она в дальнейшем в качестве воронки выполнения. Вы можете использовать встроенные в Studio Code функции такие как Find all references или просто искать по строке domain.

Автор работает над ещё одним инструментом, который пока не готов, но который задумывается для автоматизации описанной задачи. Результат анализа для определения использования переменной domain:

В дальнейшем автор расчитывает сделать этот инструмент способным выполнять более сложный анализ, такой как поиск использования переменной в разных сферах выполнения (например, если она передана в качестве аргумента какой-либо функции или фактически используется в качестве аргумента для любого воронки выполнения).

Заключения

Веб-браузер — это очень мощный инструмент. Иногда это единственный инструмент, который нужен для чтения исходного кода и полного понимания, как работает приложения, идентификации его слабостей, выполнения некоторых текстов для поиска уязвимостей или просто для поиска как оно работает или изучения чего-то нового.

Надеюсь этот пост помог вам узнать как использовать очень мощные возможности заключённые в Developer Tools. О том, как использовать полную мощь, вы можете найти множество отличных источников здесь: https://developers.google.com/web/tools/chrome-devtools/

Рекомендуется Вам:

Добавить комментарий

Ваш e-mail не будет опубликован.