📰 newsreader

hackernews score 0.72 好み 0.00 en

LinkedInの求人案件に潜むバックドア

原題: A backdoor in a LinkedIn job offer

backdoorlinkedingithubnpm installsocial engineeringmalwarecybersecuritynode.js
原文 ↗

日本語訳

# LinkedInの採用オファーに仕掛けられたバックドア

先週、小さなクリプト・スタートアップのリクルーターからLinkedInのメッセージを受け取りました。数日間にわたって何度かメッセージを交わし、彼女は、リードエンジニアを必要としている「壊れたPoC(概念実証)」について説明し、レビューしてほしい公開GitHubリポジトリを私に送ってきました。具体的には、「非推奨になったNodeモジュールの問題を確認してほしい」と依頼されました。

既存のコードベースのレビューを依頼されることは珍しくありませんが、何かがおかしいと感じ、警戒心が働いたため、私はあえて過剰なほど慎重に動くことにしました。

リポジトリをクローンして依存関係をインストールする代わりに、Hetznerで使い捨てのVPSを立ち上げ、そこにリポジトリをクローンしました。そして、ファイル読み取りツールのみを有効にした読み取り専用モードで、AIエージェントのPiをそのリポジトリに向けました。

`pi --tools read,grep,find,ls`

私はエージェントに対し、コードベースをレビューして不審な点があればフラグを立てるよう指示しました。すると、エージェントは `app/test/index.js` でほぼ即座に停止しました。

**バックドア**

そのリポジトリは、ReactのフロントエンドとNodeのバックエンドで構成されているようでした。罠は、テストスイートを装った約250行の `app/test/index.js` に隠されていました。その内部では、断片的な文字列からURLが組み立てられていました。

```javascript

const protocol = "https",

domain = "store",

separator = "://",

path = "/icons/",

token = "77",

subdomain = "rest-icon-handler",

bearrtoken = "logo";

```

これらが組み合わさると、`https://rest-iconのhandler.store/icons/77` となります。

そして、コメントアウトされたテストの山の中に埋め込まれたペイロードが、サーバーから自分のマシンに送られてくるあらゆるものを実行する仕組みになっていました。

**実行の仕組み**

このファイルは、テストの実行を待っているわけではありません。`app/index.js` 自体が `const test = require('./test')` を実行しており、これにより `app/test/index.js` が読み込まれ、実行されます。

さらに、`package.json` が `app/index.js` を起動プロセスに組み込んでいます。

ここで重要なのが `prepare` スクリプトです。`npm install` の後に `npm` は自動的に `prepare` を実行するため、依存関係をインストールするだけでバックドアが実行されてしまうのです。

「非推奨になったNodeモジュールの問題を確認してほしい」という指示は、私に `npm install` を実行させるための餌でした。

サンドボックス内でペイロードを実行させ、第2段階としてサーバーから何が送られてくるかを観察することもできましたが、私はそこで踏みとどまりました。サーバーから渡されたものを何でも実行してしまうリポジトリであるという事実だけで、十分な証拠でした。

**盗用されたアイデンティティ**

リポジトリ内のコミットは、実在する開発者の名前とメールアドレスで行われていました。その人物は、平凡なLinkedInプロフィール、個人サイト、そして長い履歴を持つGitHubアカウントを持つ、フルスタックエンジニアでした。私は、コードベースを引き継いだので実装についていくつか質問があるというふりをして彼にメッセージを送り、どのような反応を示すかを確認しました。

彼は、そのプロジェクトで働いたことは一度もないと答えました。以前にもGitHubでなりすまし被害に遭い、そのせいでリポジトリが削除されたことがあり、今回の件とは一切無関係であるとのことでした。彼は現在、これらのリポジトリを報告している最中だそうです。

**二つ目の盗用されたアイデンティティ**

リクルーターのプロフィールは、実在する、ある有名なアートジャーナリストのものでした。後で調べたところ、その人物は豊かな文化的背景を持っており、技術的な内容は一切含まれていませんでした。私が話を合わせ、「プロジェクトのインストールがうまくいかない」と伝えると、そのジャーナリストは即座にnpmとNodeバージョンのエキスパートへと変貌しました。それは、かなり滑稽な光景でした。

**これは誰にでも起こり得ること**

こうした攻撃については、HN(Hacker News)などで耳にしたことがあり、記事も読んでいましたが、いざ自分に降りかかってくると、やはり少し不意を突かれました。最初の数通のメッセージから疑念は抱いていましたが、もしもっと疲れていたり、急いでいたりする日だったら、深く考える前に `npm install` を実行してしまっていたかもしれません。ですから、もしリポジトリのレビューを依頼するLinkedInのメッセージを受け取ったら、少しの疑いと、適切なセキュリティ・ハイジーン(セキュリティ対策の習慣)を持つことは決して無駄ではありません。

もう一つの教訓は、コードを自分で読むよりも、読み取り専用のエージェントを使ってレビューさせる方が、結果的に効率的だったということです。バックドアは、不慣れな初心者が書いたような雑なコードとして偽装されていましたが、エージェントは数秒でそれを検知しました。

私はGitHubにリポジトリを、LinkedInにリクルーターを報告しました。今のところ、何も変わっておらず、コードはまだ公開されたままです。

原文(英語)を表示

A backdoor in a LinkedIn job offer

Last week, I got a LinkedIn message from a recruiter at a small crypto startup. We exchanged a few messages over a couple of days, she described a broken proof-of-concept they needed a lead engineer for, and then sent me a public GitHub repo to review. Specifically, she asked me to “check out the deprecated Node modules issue.”

It’s not uncommon to ask for a review of an existing codebase, but something felt off and raised an alarm in my head, so I decided to get a bit extra paranoid.

Instead of cloning and installing dependencies, I spun up a throwaway VPS on Hetzner, cloned the repo there, and pointed Pi at it in read-only mode, with only file-reading tools enabled:

pi --tools read,grep,find,ls

I asked the agent to review the codebase and flag anything suspicious. It stopped almost immediately at app/test/index.js

.

The backdoor

The repo felt like a React frontend with a Node backend. The trap was in app/test/index.js

, about 250 lines disguised as a test suite. Inside, a URL is assembled from fragments:

const protocol = "https",

domain = "store",

separator = "://",

path = "/icons/",

token = "77",

subdomain = "rest-icon-handler",

bearrtoken = "logo";

These combine into https://rest-icon-handler.store/icons/77

.

Then, buried between walls of commented-out tests, the payload runs anything the server sends back to your machine.

How it triggers

The file doesn’t wait for the tests to run. app/index.js

itself executes const test = require('./test')

, which loads and runs app/test/index.js

.

package.json

wires app/index.js

into startup:

The prepare

script is the important one. npm runs prepare

automatically after npm install

, so just installing dependencies executes the backdoor.

The instruction to “check out the deprecated Node modules issue” was bait to get me to run npm install

.

I could have let the payload run in the sandbox and watched what the server sent back as the second stage, but I stopped there. A repo that runs whatever a server hands it was enough evidence.

A borrowed identity

The commits in the repo were authored under the name and email of a real developer, a full-stack engineer with an ordinary LinkedIn profile, a personal website, and a GitHub account with a long history. I messaged him, pretending I’d inherited the codebase and had a few implementation questions, to see how he’d react.

He told me he’d never worked for them. He’d been impersonated on GitHub before and had a repo taken down over it, and he had nothing to do with this one. He was reporting these repos too.

A second borrowed identity

The recruiter’s profile belonged to a real arts journalist, a well-known one I looked up later, with a long cultural background and nothing technical on it. When I played along and told her I couldn’t get the project to install, the journalist instantly turned into an expert on npm and Node versions. It was quite amusing, I’d say.

This can happen to anyone

I’ve heard of these attacks and read about them on HN, but when one came after me it still caught me a bit off guard. I suspected something from the first few messages, but on a more tired or rushed day, I could easily have run npm install

before thinking it through. So, if you get a LinkedIn message asking you to review a repo, a bit of paranoia and good security hygiene never hurts.

Another takeaway is that reviewing the code with a read-only agent turned out more productive than reading it myself. The backdoor was dressed up as sloppy beginner code, but the agent flagged it in seconds.

I reported the repo to GitHub and the recruiter to LinkedIn. So far nothing has changed and the code is still up.

← 一覧に戻る