LinkedIn: тестовое задание на собеседовании обернулось трояном для кражи криптокошельков и паролей

Друга автора блога codedge.de на LinkedIn нашёл рекрутер с «подходящей» вакансией, частичная удалённая занятость и хорошая почасовая оплата, а описание будто точно совпадало с прошлым опытом. После пары сообщений рекрутер, чтобы «ускорить» процесс найма, сразу прислал тестовое задание, кодовую задачу на TypeScript. Компания, от имени которой шла переписка, к рассылке отношения не имела и о ней не знала; позже она сама опубликовала в LinkedIn пост с предупреждением о фишинговой кампании, а профиль фейкового рекрутера с тех пор удалён. Автор перечисляет тревожные признаки, которые стоило заметить сразу: писавший не значился сотрудником компании в LinkedIn; не было ни одного ознакомительного созвона перед тестом; язык тестового задания мог не совпадать с профилем кандидата; код лежал на Bitbucket, что для таких тестов необычно; а письмо пришло с обычного адреса на @gmail.com, а не с корпоративной почты.
Само задание, доработать логику в TypeScript-проекте примерно на 180 файлов: смесь мёртвого и рабочего кода, без обфускации или минификации, зато с обращениями к внешнему сервису api.jsonbin.io. Функция, которая всегда запускается первой, при обычном npm run dev или npm start, подключает модули Node.js child_process (запуск команд в шелле), fs (чтение и запись файлов на диске, то есть закрепление в системе), net и https (собственный канал для отправки украденных данных) и напрямую читает переменные окружения process.env, в этом проекте это MONGO_URI, JWT_SECRET, SENDGRID_API_KEY, CLOUDINARY_API_SECRET и PAYTM_MERCHANT_KEY.
Ответ от jsonbin.io, по сути, загрузчик для удалённого выполнения кода. Поле record.model в этом ответе, 24 686 символов JavaScript, обфусцированного при помощи obfuscator.io и обёрнутого вокруг небольшого пакета, собранного с помощью webpack. После деобфускации внутри находится ещё один слой обфусцированного кода, который обращается к серверу управления (C2) по адресу 147.189.174.138, для этого нужен заголовок авторизации с JWT-токеном. С этого сервера приходят ещё четыре модуля. Scdata, интерактивный троян удалённого доступа (RAT): node-pty даёт полноценный шелл, ssh2, переход на другие машины и кражу PEM-ключей, screenshot-desktop вместе с sharp, захват экрана, clipboardy, доступ к буферу обмена, @nut-tree-fork/nut-js, эмуляцию клавиатуры и мыши; модуль также определяет, работает он в виртуальной машине или на «железе». Ldata, похититель паролей из браузера и криптокошельков: на всех трёх операционных системах и в каждом профиле Chrome, Edge, Brave и LT он вытаскивает файлы Login Data и Web Data, хранилища расширений Local Extension Settings (LevelDB) и, на macOS, файл login.keychain. Модуль умеет находить 28 расширений-кошельков, среди названных MetaMask, Phantom, Coinbase, Binance, TronLink, Trust, Keplr, Coin98, OKX и Rabby, плюс ещё 18 без указания названий, и работает бесконечным циклом, выгружая украденное примерно раз в минуту. Файловый сборщик обходит домашнюю директорию в поисках приватных ключей, секретных фраз, файлов с metamask, bitcoin, solana в имени.env, *.pem, *.p12, *.pfx, а также документов, изображений и целиком папок .ssh.aws.gnupg и .docker; на Windows он дополнительно перебирает все буквы дисков. Монитор буфера обмена постоянно опрашивает буфер и отправляет содержимое наружу, маскируясь под лог с именем npm-compiler.log. Заражённая машина сама выходит на сервер 147.189.174.138 по порту 7321, сервер лишь видит адрес источника входящего соединения, как любой веб-сервер видит IP посетителя, без сканирования или предварительной регистрации адреса жертвы. Именно поэтому подход, при котором соединение всегда исходящее, работает из-под NAT, CGNAT, корпоративного прокси или домашнего роутера без какой-либо настройки, и не зависит от того, меняется ли IP жертвы.
Всё это работает без единого запроса повышенных прав: вредоносу не нужны ни root, ни UAC, ни sudo, потому что SSH-ключи, учётные данные AWS, профили браузера, файлы криптокошельков и .env изначально принадлежат самому пользователю, процесс, запущенный от имени жертвы, наследует ровно тот доступ, что уже есть у её аккаунта. По сути это то же самое, что выполнить cat ~/.ssh/id_rsa из обычного терминала. На Windows вредонос идёт дальше домашней папки: через PowerShell он перебирает буквы дисков и вызывает функцию поиска (scanDir) на каждом корне, так что в область поражения попадают и подключённые сетевые диски, и дополнительные разделы.
Из мер защиты, которые разбирает автор, ни одна не даёт стопроцентной гарантии. Попросить ИИ проверить проект на аномалии, слабая защита: ассистент способен заметить подозрительное обращение к jsonbin.io, но не видит, что именно возвращает этот адрес; автор отмечает, что Claude Code, когда его просто попросили просканировать кодовую базу на необычные паттерны, ничего подозрительного не нашёл. Запуск кода только внутри Docker, защита получше, но тоже частичная: секреты не утекают, пока в контейнер не подмонтированы данные хоста. Наиболее надёжный вариант, по мнению автора, полностью изолированная виртуальная машина (например, через Vagrant) со снапшотом до запуска и откатом после: без графического окружения самоограничивается модуль, ворующий данные браузера и делающий скриншоты, но троян удалённого доступа и файловый сборщик всё равно продолжают работать. При этом сам RAT-модуль активно проверяет, не запущен ли он в виртуальной машине: вызывает system_profiler, читает /proc/cpuinfo, ищет в них упоминания vmware, qemu или microsoft corporation, и в зависимости от результата помечает свой сигнал как (VM) или (Local). Автор предполагает, это его собственная догадка, а не заявление источника, что эта метка нужна операторам для сортировки жертв: виртуальная машина скорее похожа на песочницу и вряд ли хранит настоящие кошельки, поэтому такую цель могут отложить или обработать осторожнее.
Если компрометация уже произошла, автор советует, формулируя это как «probably should», то есть без гарантий, отозвать и заменить SSH-ключи, сменить пароли, проверить, не остались ли где-то незашифрованные секреты, которые нужно поменять, и на всякий случай переустановить операционную систему.
Ключевые факты
- Фейковый рекрутер писал жертве в LinkedIn от имени компании, которая к рассылке отношения не имела; после пары сообщений вместо ознакомительного созвона сразу пришло тестовое задание, TypeScript-проект примерно на 180 файлов.
- Запуск проекта (npm run dev/npm start) обращается к внешнему сервису jsonbin.io и скачивает обфусцированный загрузчик размером 24 686 символов, который дальше подключается к серверу управления (C2) на 147.189.174.138 и получает ещё четыре модуля.
- Среди модулей, интерактивный троян удалённого доступа (полный шелл, переход на другие машины, захват экрана, эмуляция клавиатуры и мыши) и похититель паролей и данных 28 расширений-криптокошельков (в их числе MetaMask, Phantom, Coinbase, Binance, TronLink), выгружающий украденное примерно раз в минуту.
- Другие модули ищут по всей домашней директории приватные ключи, секретные фразы.env-файлы и папки .ssh/.aws/.gnupg/.docker, а также следят за буфером обмена, маскируясь под лог npm-compiler.log; никаких повышенных прав (root, UAC, sudo) для этого не требуется.
- По словам автора, Claude Code, которого просто попросили поискать аномалии в коде, ничего подозрительного не нашёл; надёжнее оказалась полностью изолированная виртуальная машина, хотя даже там троян удалённого доступа и файловый сборщик продолжают работать, а сам вредонос дополнительно проверяет, не запущен ли он в песочнице.
Почему это важно
История показывает уже применяемую схему социальной инженерии, нацеленную конкретно на разработчиков: злоумышленники используют напряжённость рынка труда и желание получить работу, чтобы жертва сама, добровольно запустила вредоносный код у себя на машине. Задание выглядит как обычный тестовый проект, без обфускации, без запроса особых прав, а вредоносная логика подгружается только во время выполнения через внешний, на первый взгляд безобидный сервис jsonbin.io. Это обходит и поверхностный просмотр кода человеком, и проверку ИИ-ассистентом: в материале отдельно отмечено, что Claude Code, которого просто попросили поискать аномалии, ничего не нашёл, потому что сам код в репозитории не обфусцирован, обфусцирован только payload, который приходит по сети позже.
Кому это важно
В первую очередь, разработчикам и инженерам, которые сейчас в поиске работы и получают неожиданные предложения в LinkedIn с ускоренным процессом найма и тестовым заданием сразу после первых сообщений. Также, всем, кто хранит на рабочей или личной машине SSH-ключи, доступы к облаку вроде AWS, пароли в браузере или расширения криптокошельков: именно эти данные, цель всех четырёх модулей вредоноса. И отдельно, компаниям, чьё имя злоумышленники могут использовать для прикрытия рассылки, не имея к ней отношения: в этой истории компания, от имени которой шла переписка, сама публично предупредила о кампании в LinkedIn.
Как это применить
Из материала можно взять готовый чек-лист тревожных признаков до начала теста: человек, который пишет, не числится сотрудником компании в LinkedIn; нет ознакомительного созвона перед тестовым заданием; язык задания не совпадает с профилем кандидата; код лежит в необычном для таких тестов месте (например, на Bitbucket); письмо пришло с личного адреса вроде @gmail.com, а не с корпоративной почты. Дальше, правило по работе с самим кодом: не запускать чужой тестовый проект напрямую на основной машине. Docker снижает риск, но только если в контейнер не подмонтированы данные хоста; надёжнее, одноразовая виртуальная машина (например, через Vagrant) со снапшотом до запуска и откатом после, желательно без графического окружения. Просьбу к ИИ-ассистенту проверить код на аномалии стоит считать слабым, частичным сигналом, а не полноценным аудитом, он может заметить подозрительный внешний вызов, но не то, что тот вызов реально возвращает. Если запуск уже состоялся на обычной машине, автор советует отозвать и заменить SSH-ключи, сменить пароли, проверить и заменить любые сохранённые секреты и переустановить ОС.
Можно ли доверять
Это рассказ очевидца от первого лица на техническом блоге: автор лично разбирал полученный код, деобфусцировал два слоя JavaScript, зафиксировал точный IP и порт сервера управления, размер полезной нагрузки и список конкретных модулей и npm-пакетов, которые использует вредонос. Ключевую часть истории, что рассылка велась от имени компании, которая на самом деле в ней не участвовала, подтверждает сама эта компания: она опубликовала в LinkedIn собственный пост о фишинговой кампании, о котором упоминает автор. При этом в тексте не назван ни пострадавший друг автора, ни сама компания, ни фейковый рекрутер, ни дата случившегося; нет ни номера CVE, ни данных о числе жертв или суммарном ущербе, перед нами задокументированный разбор одного конкретного случая, а не аудированное раскрытие в промышленном масштабе.
Риски и подводные камни
Главная ловушка в том, что вредонос не показывает ничего подозрительного на старте: 180 файлов без обфускации, ни одного запроса повышенных прав, ведь SSH-ключи, доступы к AWS, данные браузера и криптокошельков и так читаются от имени обычного пользователя. Ждать запроса root или UAC как сигнала тревоги здесь бесполезно, потому что вредонос сознательно об этом не просит. ИИ-проверка кода, включая Claude Code, по описанию автора, не поймала угрозу при простом запросе «поискать аномалии», обфусцирован только payload, приходящий позже по сети, а не сам репозиторий. Даже запуск в изолированной виртуальной машине не убирает риск полностью: троян удалённого доступа и файловый сборщик работают и без графического окружения, а сам вредонос ещё и активно проверяет, не в виртуальной ли он машине (через system_profiler и /proc/cpuinfo), то есть способен вести себя по-разному в песочнице и на реальном железе.
«Вы открыли дверь в ад, хотя всего лишь хотели получить работу.»
— автор блога codedge.de