Разработчики давно поняли: чтобы сделать программу по-настоящему гибкой и расширяемой, нужно добавить внутрь хороший скриптовый язык. Тем более что придумывать ничего не надо: есть Lua, который прекрасно интегрируется, чем и воспользовались создатели Wireshark, Nmap и даже малвари.
WARNING
Intro
Хакер #179. Интернет вещей — новый вектор атак
После небольшого изучения темы оказалось, что поддержка языка реализована в популярных утилитах, с которыми приходится иметь дело каждый день, при этом я почему-то обходил такую возможность стороной. Пришло время это исправить.
Расширение для Wireshark
В работе мне довольно часто приходится анализировать большие объемы трафика, причем искать в них вполне конкретные паттерны. Поддержка Lua для написания расширений в Wireshark может сэкономить кучу времени (которую можно потратить, скажем, на сон).
При небольшой сноровке можно писать довольно сложные сценарии для поиска паттернов и анализа трафика. Но для примера мы возьмем что-нибудь простое и показательное — напишем простой скрипт для парсинга полей имени хоста и cookies. И параллельно разберем несколько ошибок, которые могут возникнуть в процессе разработки расширения.
Пример выполнения Lua-скрипта в Wireshark
Скрипт также можно выполнить через консоль, используя tshark:
Так как скрипт небольшой, я приведу код сразу, а далее мы его разберем по частям:
Итак, поехали. Сначала для более удобного вывода нужных нам данных создадим отдельное окно для текстовой информации:
В принципе, можно уже выполнить эту строку после запуска Wireshark, и мы получим желанное окно, которое представлено на скриншоте.
Созданное окно с помощью расширения в Wirehark
Далее создадим переменные, которые будут обращаться к полям host и cookie HTTP-протокола:
Таким образом можно обращаться к любым полям любого протокола, а как мы все помним, этот замечательный снифер из коробки знает структуру колоссального числа протоколов и умеет «молотить» их на понятные части на лету (впрочем, для неизвестного протокола легко прописать структуру).
Теперь напишем функцию инициализации приемника пакетов и установим фильтр на HTTP-протокол:
Далее все совсем просто, переводим в строки полученные переменные и добавляем к созданному окну:
Единственный нюанс: объединить переменные с помощью операции объединения строк нельзя, поскольку такие данные имеют тип userdata, а Lua не может напрямую их изменять. Но это можно обойти через метатаблицы.
Теперь выполним наш скрипт и запустим снифинг пакетов.
Работа нашего расширения в Wireshark
Работает! Но как видишь, у расширения есть минус — он выводит одинаковые кукисы, поэтому в идеале надо сделать таблицу, где будут храниться старые значения, и добавить проверку на уникальность. Но это я оставляю тебе в качестве домашнего задания. Если же ты не справишься или ты из тех людей, которые любят готовые рецепты, то полная версия будет ждать тебя на моем GitHub-аккаунте.
В сноске ты найдешь ссылки на вики и API с более подробной информацией по написанию расширений. Помимо этого, приведены примеры рабочих скриптов, один из которых сохраняет VoIP-звонки из пакетов в отдельные файлы и работает с SQL базой данных.
Расширение для Nmap
Разберем стандартную структуру скрипта. В начале каждого сценария добавляются стандартные библиотеки:
Далее идут метаданные:
После этого идет непосредственно код сценария. Для примера напишем простейший скрипт для брута пользователей популярной CMS LiveStreet.
Вначале определим стандартные переменные и подключим недостающие библиотеки:
Далее составим класс Driver для брута из соответствующей библиотеки. Напишем функцию инициализации с указанием хоста, порта и пути до формы логина:
Функции коннекта и дисконнекта оставим без изменений:
Далее пишем проверку, чтобы посмотреть успешность коннекта и наличие поля для пароля:
Ну и самая главная часть — функция логина. Сначала составляем HTTP-запрос:
Далее указываем результат, после которого считается, что пара логин с паролем, переданные в запросе, были правильными:
В случае с сайтами на WordPress значение будет 302. Другой вариант — проверять появление нового HTML-элемента на странице (например, после логина обязательно появится ссылка для выхода пользователя из системы):
Сохраним полученный скрипт с расширением nse и положим рядом два файла: users. txt и passwords. txt — с некоторым количеством имен пользователей и паролей. В случае LiveStreet можно даже напарсить валидные имена пользователей или добавить этот функционал в скрипт.
Теперь запустим полученный сценарий (полный исходник скрипта можно скачать из моего GitHub-репозитория):
Если все пройдет успешно, то ты увидишь отчет о проделанном брутфорсе.
Отчет Nmap-скрипта для брута пользователей LiveStreet CMS
Как видишь, код не такой сложный, что позволяет быстро написать свой скрипт под определенную цель, с которой ты столкнулся в процессе проведения пентеста.
Шпаргалка по Lua
Первое, что надо знать, — Lua — это язык с динамическим определением данных, то есть переменные получают тип на лету в зависимости от своего содержания. Всего используется восемь типов:
Условные операторы и циклы довольны стандартны:
Операции также почти все знакомые:
Функцию мы рассмотрим на примере тестового скрипта в нашей программе. Более подробно про этот язык можешь прочитать по ссылкам в сносках. Для написания программ достаточно будет Notepad++ или Sublime Text.
Outro
Вообще, использование Lua растет и растет. Так, на момент написания статьи была опубликована новость, что появился плагин для Olly Debugger версии 2+, добавляющий поддержку Lua.
Впрочем, язык не ограничивается одними программами для ИБ-сферы и может пригодиться в самых разных ситуациях. Как я уже говорил, его часто используют для игр, например World of Warcraft в своих аддонах (предоставляя API и довольно неплохую документацию по нему). Помимо игровых приложений, его задействуют такие программы, как Setup Factory. На нем написаны многие инсталляторы: Apache, nginx, Adobe Photoshop Lightroom, VLC и многие другие. Там, где нужно реализовать программируемое поведение программы, — интегрированный интерпретатор Lua точно будет очень неплохим вариантом.
Встраиваем компилятор в свою программу
Итак, главная фишка Lua в том, что его можно встроить в любую программу и предложить всем желающим расширить ее возможности. Как это сделать? Я покажу на примере Visual C++ программы. Стоит отметить, что сам Lua написан на чистом C, и это создает некоторые сложности при добавлении в программы с плюсами. На просторах Сети предлагается несколько вариантов добавления поддержки Lua в C++ программы. Кто-то предпочитает скачивать уже прекомпилированные библиотеки или готовые фреймворки, кто-то — компилировать из исходников самому, а кто-то — подключать исходники к своему проекту и, немного разобравшись с настройками в проекте, добиться действительного добавления Lua в свою программу. Мы с тобой как раз и разберем последний вариант.
Создадим новый консольный проект (я пользуюсь Visual Studio 2010). После чего добавим через свойства проекта директорию с исходниками:
где в каждом из разделов добавим в соответствующие поля MARKDOWN_HASHd8a960f245cb01e9a35ba16cf2a54fe6MARKDOWN_HASH путь к исходникам языка. Далее создаем раздел Lua, куда добавляем все файлы из папки MARKDOWN_HASH25d902c24283ab8cfbac54dfa101ad31MARKDOWN_HASH :
Добавление директории с исходниками Lua в проект.
Как я уже упомянул выше, Lua написан на чистом C, поэтому применим небольшой хак — добавим в начало своего файла следующие строки:
Далее инициализируем наш язык и подключим его библиотеки:
Теперь создадим цикл, который будет проверять входящие данные, пока они не закончатся (или не переполнят буфер), и выполнять их:
Теперь можно компилировать проект и запустить.
Работаем в скомпилированном интерпретаторе Lua
В ходе компиляции могут возникнуть ошибки:
Следующий проект создадим аналогичный первому, но теперь будем запускать свои сохраненные скрипты в самой программе, инициализируем Lua и добавим после:
Это и есть обещанный пример объявления и работы с функцией. Теперь загрузим его в память и выполним:
Теперь рассмотрим возможность вызова функций C-программы из Lua-скриптов. И для начала объявим функцию, которая будет вызываться из нашего языка:
Далее регистрируем функцию:
Прописываем функцию, которая загружает по переданному пути скрипт:
И запускаем загруженный скрипт:
Но, если заметил, вызов немного отличается от предыдущего проекта. Константа MARKDOWN_HASH5301b763e99f84122b8de40237a602d5MARKDOWN_HASH используется вместе с функцией MARKDOWN_HASHeb8e633c3ab4c927bba1d52e54fb33c3MARKDOWN_HASH для подсчета стека до и после вызова. Компилируем, не забывая про описанные выше ошибки.
Теперь пропишем вызов этой функции из Lua в отдельный скрипт и назовем его MARKDOWN_HASH21b648082b7edb48f91278eceb83c54aMARKDOWN_HASH :
Вызовем полученный файл из нашей программы:
И получим следующий вывод в консоль:
Вывод вызываемой C-функции из Lua
Готовые исходники проектов ты можешь взять из GitHub, но не забывай, что в них прописаны пути к моей Lua и для экономии места вырезаны функции обработки ошибок Lua-скриптов. Еще я не подробно расписал одну интересную тему — работу со стеком, которая позволяет обмениваться данными с основной программой, хотя главное мы затронули в последнем проекте.
Борис dukeBarman Рютин
Известный реверсер (@dukebarman), докладчик на множестве конференций, постоянный автор «Хакера» и большой фанат вселенных Blizzard. В данный момент работает руководителем проектов в «Digital Security» (@DSecRU).
https://xakep. ru/2014/09/05/lua-for-pentest/