4 คะแนน โดย GN⁺ 2025-12-15 | 3 ความคิดเห็น | แชร์ทาง WhatsApp
  • แพ็กเกจ npm อันตราย Shai-Hulud 2.0 ติดมัลแวร์ลงบนเครื่องของนักพัฒนาและขโมย สิทธิ์เข้าถึง GitHub Organization ของ Trigger.dev
  • การติดเชื้อเริ่มขึ้นเมื่อผู้พัฒนารัน pnpm install แล้ว สคริปต์ preinstall ของแพ็กเกจอันตรายถูกเรียกใช้ โดยใช้เครื่องมือ TruffleHog เพื่อขโมยข้อมูลรับรอง
  • ผู้โจมตี โคลนรีโพ 669 แห่ง ภายใน 17 ชั่วโมง และจากนั้นในช่วง 10 นาทีได้พยายาม force push ไปยัง 199 สาขาและปิด PR 42 รายการ
  • แพ็กเกจและระบบโปรดักชันไม่ได้รับความเสียหาย และตรวจพบการโจมตีภายใน 4 นาทีพร้อมตัดการเข้าถึงบัญชีได้ทันที
  • หลังเหตุการณ์ มีการเสริมความปลอดภัยด้วยการปิดใช้งานสคริปต์ npm, อัปเกรดเป็น pnpm 10, ใช้ การเผยแพร่ npm แบบอิง OIDC, และ บังคับใช้ branch protection กับทุกรีโพ

ภาพรวมการโจมตี

  • วันที่ 25 พฤศจิกายน 2025 ระหว่างการดีบักภายใน Slack พบความผิดปกติว่ามี คอมมิต “init” ในนาม Linus Torvalds ถูกสร้างขึ้นในหลายรีโพ
  • จากการตรวจสอบพบว่าเวิร์มซัพพลายเชน Shai-Hulud 2.0 ได้ติดมัลแวร์ลงบนเครื่องของนักพัฒนาและขโมยข้อมูลรับรอง GitHub
  • มีรายงานว่าเวิร์มนี้แพร่ไปยังแพ็กเกจ npm มากกว่า 500 รายการ และส่งผลกระทบต่อรีโพมากกว่า 25,000 แห่ง
  • แพ็กเกจ npm ทางการของ Trigger.dev (@trigger.dev/*, CLI) ไม่ได้รับการติดเชื้อ

ไทม์ไลน์การโจมตี

  • 24 พฤศจิกายน 04:11 UTC: เริ่มเผยแพร่แพ็กเกจอันตราย
  • 20:27 UTC: เครื่องของนักพัฒนาในเยอรมนีติดเชื้อ
  • 22:36 UTC: ผู้โจมตีเข้าถึงได้เป็นครั้งแรกและเริ่มโคลนรีโพจำนวนมาก
  • 15:27~15:37 UTC (25 พฤศจิกายน): ดำเนินการโจมตีเชิงทำลายเป็นเวลา 10 นาที
  • 15:32 UTC: ตรวจพบความผิดปกติและตัดการเข้าถึงได้ภายใน 4 นาที
  • 22:35 UTC: กู้คืนทุกสาขาเสร็จสมบูรณ์

กระบวนการติดเชื้อ

  • เมื่อผู้พัฒนารัน pnpm install สคริปต์ preinstall ของแพ็กเกจอันตรายจะถูกเรียกใช้ เพื่อดาวน์โหลดและรัน TruffleHog
  • TruffleHog จะสแกนหา GitHub token, ข้อมูลรับรอง AWS, npm token, ตัวแปรแวดล้อม เป็นต้น แล้วส่งออกไปภายนอก
  • พบไดเรกทอรี .trufflehog-cache และไฟล์ที่เกี่ยวข้องบนเครื่องที่ติดเชื้อ
  • แพ็กเกจต้นทางที่ทำให้เกิดการติดเชื้อถูกลบไปแล้ว จึงติดตามย้อนกลับไม่ได้

กิจกรรมของผู้โจมตี

  • หลังการติดเชื้อ มีการทำ กิจกรรมสอดแนม ต่อเนื่องเป็นเวลา 17 ชั่วโมง
    • ใช้อินฟราจากสหรัฐฯ และอินเดียเพื่อโคลนรีโพ 669 แห่ง
    • เฝ้าติดตามกิจกรรมของนักพัฒนาและคงการเข้าถึงไว้ผ่าน GitHub token
    • สร้างรีโพชื่อ “Sha1-Hulud: The Second Coming” ซึ่งคาดว่าใช้เก็บข้อมูลรับรอง
  • จากนั้นในช่วง 10 นาทีได้ทำ การกระทำเชิงทำลาย
    • พยายาม force push ไปยัง 199 สาขาใน 16 รีโพ
    • ปิด PR 42 รายการ โดยบางส่วนถูกบล็อกด้วยการตั้งค่า branch protection
    • ทุกคอมมิตแสดงในรูปแบบ “Linus Torvalds <email> / init”

การตรวจจับและการตอบสนอง

  • ตรวจพบความผิดปกติแบบเรียลไทม์ผ่านการแจ้งเตือนใน Slack
  • ภายใน 4 นาที ได้ตัดการเข้าถึง GitHub ของบัญชีที่ติดเชื้อ และหลังจากนั้นเพิกถอนการเข้าถึงบริการทั้งหมด เช่น AWS, Vercel, Cloudflare
  • จากการวิเคราะห์ AWS CloudTrail log พบว่า มีเพียงการเรียก API แบบอ่านอย่างเดียว และไม่มีการเข้าถึงข้อมูลโปรดักชัน
  • AWS ยังตรวจพบพฤติกรรมต้องสงสัยที่เกี่ยวข้องกับ Shai-Hulud แยกต่างหากและส่งคำเตือนมา

ความเสียหายและการกู้คืน

  • มี รีโพถูกโคลน 669 แห่ง, force push ไปยัง 199 สาขา, และ PR ถูกปิด 42 รายการ
  • แม้การกู้คืนจะยากเพราะ GitHub ไม่มี server-side reflog แต่สามารถใช้ Event API และ local reflog เพื่อกู้คืนทั้งหมดได้ภายใน 7 ชั่วโมง
  • แพ็กเกจ npm และโครงสร้างพื้นฐานโปรดักชันไม่ได้รับความเสียหาย

การรั่วไหลของ GitHub App key

  • ระหว่างการตรวจสอบ พบ GitHub App private key อยู่ในถังขยะของโน้ตบุ๊กนักพัฒนา
  • คีย์ดังกล่าวมี สิทธิ์ read/write ต่อรีโพของลูกค้า และได้มีการ rotate ทันที
  • ฐานข้อมูล (ที่เก็บ installation ID) ไม่ได้รับความเสียหาย จึง ไม่มีหลักฐานว่ามีการเข้าถึงรีโพของลูกค้า แต่ยังไม่อาจตัดความเป็นไปได้นี้ออกได้ทั้งหมด
  • ได้ขอ log เพิ่มเติมจากทีมสนับสนุน GitHub และส่งอีเมลแจ้งลูกค้าแล้ว

การวิเคราะห์ทางเทคนิคของ Shai-Hulud

  • เมื่อรัน setup_bun.js จะติดตั้ง Bun runtime และรัน bun_environment.js แบบเบื้องหลัง
  • ใช้ TruffleHog เพื่อรวบรวมข้อมูลรับรองภายในไดเรกทอรี $HOME
  • ข้อมูลที่รวบรวมได้ (contents.json, cloud.json, truffleSecrets.json เป็นต้น) จะถูกอัปโหลดไปยัง รีโพ GitHub แบบสุ่มในรูปแบบเข้ารหัส base64 ซ้อน 3 ชั้น
  • หากมี npm token อยู่ เวิร์มจะปรับแก้และเผยแพร่แพ็กเกจของบัญชีที่ติดเชื้อใหม่เพื่อขยายการแพร่กระจาย
  • หากไม่พบข้อมูลรับรอง จะมีการ พยายามลบ home directory
  • ไฟล์บ่งชี้การติดเชื้อ: setup_bun.js, bun_environment.js, .trufflehog-cache/ เป็นต้น

มาตรการเสริมความปลอดภัย

  • ปิดใช้งานสคริปต์ npm ทั้งหมด (ignore-scripts=true)
  • อัปเกรดเป็น pnpm 10: ปิดการรันสคริปต์โดยค่าเริ่มต้น และตั้งค่า minimumReleaseAge (3 วัน) เพื่อหน่วงการติดตั้งแพ็กเกจใหม่
  • นำ npm Trusted Publishers แบบอิง OIDC มาใช้เพื่อลบ long-lived token
  • บังคับใช้ branch protection กับทุกรีโพ
  • นำ Granted มาใช้กับ AWS SSO และเข้ารหัส session token
  • เปลี่ยนให้ GitHub Actions ต้องได้รับการอนุมัติก่อนเมื่อรัน workflow ของผู้มีส่วนร่วมภายนอก

บทเรียนสำหรับทีมอื่น

  • โครงสร้างที่เปิดให้มี การรันโค้ดตามอำเภอใจระหว่างการติดตั้ง npm คือพื้นผิวการโจมตีในตัวมันเอง
  • ควรตั้งค่า ignore-scripts=true และดูแล whitelist เฉพาะแพ็กเกจที่จำเป็น
  • ใช้ pnpm minimumReleaseAge เพื่อหน่วงการติดตั้งแพ็กเกจใหม่
  • branch protection และ การดีพลอยแบบอิง OIDC เป็นมาตรการความปลอดภัยที่จำเป็น
  • ห้ามเก็บข้อมูลรับรองระยะยาวไว้บนเครื่องโลคัล และอนุญาตให้ดีพลอยผ่าน CI เท่านั้น
  • สัญญาณรบกวนจากการแจ้งเตือนใน Slack กลับกลายเป็นกุญแจสำคัญของการตรวจจับ

มิติด้านมนุษย์

  • นักพัฒนาที่เครื่องติดเชื้อไม่ได้ทำอะไรผิด เพราะเพียงแค่รัน npm install ก็ได้รับผลกระทบแล้ว
  • ระหว่างการโจมตี พบร่องรอยว่าบัญชีดังกล่าวไปกด ‘star’ รีโพแบบสุ่มหลายร้อยแห่งโดยอัตโนมัติ
  • เหตุการณ์นี้ไม่ได้สะท้อนความผิดพลาดของบุคคล แต่เผยให้เห็น ความเปราะบางเชิงโครงสร้างของทั้ง ecosystem

ตัวชี้วัดสรุป

  • ตั้งแต่ติดเชื้อครั้งแรกจนถึงการโจมตีครั้งแรก: ประมาณ 2 ชั่วโมง
  • ระยะเวลาที่ผู้โจมตีคงการเข้าถึงไว้: 17 ชั่วโมง
  • ระยะเวลาของการกระทำเชิงทำลาย: 10 นาที
  • ใช้เวลา 5 นาทีจนตรวจพบ และ 4 นาทีจนตัดการเข้าถึง
  • ใช้เวลา 7 ชั่วโมงจนกู้คืนทั้งหมดเสร็จสมบูรณ์
  • รีโพที่ถูกโคลน: 669 แห่ง / สาขาที่ได้รับผลกระทบ: 199 / PR ที่ถูกปิด: 42

แหล่งข้อมูลอ้างอิง

  • Socket.dev: Shai-Hulud Strikes Again V2
  • รายงานวิเคราะห์จาก PostHog, Wiz, Endor Labs, HelixGuard
  • เอกสารของ npm Trusted Publishers, pnpm onlyBuiltDependencies, minimumReleaseAge, Granted

3 ความคิดเห็น

 
click 2025-12-15

ดูเหมือนว่าโดยโครงสร้างแล้ว pnpm จะต้องอนุญาต post-install เป็นรายตัวโดยค่าเริ่มต้นอยู่แล้ว แต่สุดท้ายนักพัฒนาก็อาจเผลออนุญาตไปโดยไม่รู้ตัวกันอยู่ดี

 
lamanus 2025-12-16

เข้าใจว่าเนื่องจาก npm ถูกตั้งให้ทำงานเป็นค่าเริ่มต้น จึงเปลี่ยนไปใช้ pnpm และปิดการทำงานค่าเริ่มต้นไว้เพื่อเสริมความปลอดภัยให้แข็งแกร่งขึ้น

 
GN⁺ 2025-12-15
ความคิดเห็นจาก Hacker News
  • การรัน npm install ไม่ใช่ความ ประมาทเลินเล่อ
    ปัญหาคือ ecosystem ที่ อนุญาตให้รันโค้ดตามอำเภอใจ ระหว่างขั้นตอนติดตั้งแพ็กเกจ
    แต่ความล้มเหลวด้านความปลอดภัยที่แท้จริงคือการใช้ package manager ที่เปิดให้บุคคลที่สามยัดโค้ดเข้ามาในผลิตภัณฑ์ของฉันได้โดยแทบไม่มีการตรวจสอบ
    สุดท้ายแล้วเรากำลังพึ่งพาความหวังดีและความสามารถของ package manager และผู้ดูแลมันอย่างไม่มีที่สิ้นสุด
    อีกอย่าง ดูเหมือน OP จะสื่อว่าได้เก็บ credential ไว้ในระบบไฟล์แบบ plain text

    • ฉันคิดว่าทั้งสองอย่างล้วนเป็นปัญหา
      ในระดับภาษา เราสามารถสร้างโครงสร้างที่จำกัดให้โค้ดอ่านอินพุต ใช้ทรัพยากร และ สร้างเอาต์พุตที่ถูกต้องตามชนิดเท่านั้น ได้
      แม้จะไม่ใช่การแก้ปัญหา supply chain แบบสมบูรณ์ แต่ก็ลดขอบเขตความเสี่ยงลงได้มาก
    • ตรรกะแบบนี้วนเป็นวงกลมเกินไป
      ประมาณว่า “ไม่ผิดถ้าคนคนหนึ่งใช้เครื่องมือแบบนี้ แต่ถ้าทุกคนใช้ ecosystem จะมีปัญหา”
      มันพิสูจน์มาหลายครั้งแล้วว่าเครื่องมือสำหรับนักพัฒนาจำนวนมากนั้น มีช่องโหว่ด้านความปลอดภัย
      ถ้าใส่ใจจริง ก็ต้องแสดงออกผ่านการกระทำ
    • ปลั๊กอิน IDE ก็เหมือนกัน
      ตอนใช้ VS Code มันน่ารำคาญที่แค่จะเพิ่มฟังก์ชันเล็กน้อยก็ต้องติดตั้งปลั๊กอินจากคนที่ไม่รู้จักว่าเป็นใคร
    • สงสัยว่าจริง ๆ แล้วจะออกแบบ package manager ที่ป้องกันการรันโค้ดของบุคคลที่สามได้อย่างไร
      ท้ายที่สุดมันก็เป็นโครงสร้างที่ต้องรัน โค้ดที่เลี่ยงไม่ได้ว่าจะต้องเชื่อถือ ไม่ใช่หรือ
    • เครื่องมือบางตัวรองรับแค่ไฟล์ netrc สำหรับการยืนยันตัวตนผ่าน http
      ถ้าใช้ git ผ่าน http เส้นทางการ เปิดเผย credential แบบ plain text ลักษณะนี้แทบจะมีอยู่เสมอ
  • ผู้ดูแล pnpm เคยเสนอไว้ตั้งแต่ปีก่อนว่าให้ “บล็อก post-install script โดยค่าเริ่มต้น
    แม้จะทำให้ฝั่งผู้ใช้ไม่สะดวก แต่เชื่อว่าในระยะยาวจะเป็นการเปลี่ยนแปลงที่ทุกคนจะขอบคุณ
    PR ที่เกี่ยวข้อง: pnpm/pnpm#8897

    • แต่ถึงอย่างนั้นปัญหาเดิมก็ยังเกิดซ้ำอยู่
      สุดท้ายก็เป็นอีกครั้งที่ ความสะดวกชนะความปลอดภัย
  • เขาบอกว่า “ฐานข้อมูลไม่ได้ถูกเจาะ” แต่ถ้าผู้โจมตี เข้าถึง AWS และซีเคร็ตได้ ผมก็ถือว่ามันถูกเจาะไปแล้ว
    ถ้ามีความเป็นไปได้ที่จะเข้าถึงได้ ก็ควรถูกนับว่าเป็นการถูกเจาะ

    • ถ้ามี access log ของทรัพยากรใน AWS และไม่พบร่องรอยการเข้าถึงก่อนจะเพิกถอนสิทธิ์ ก็อาจถือได้ว่าข้อมูลยังปลอดภัย
  • หลังจากมัลแวร์ถูกรันแล้ว แทบจะ เป็นไปไม่ได้เลยที่จะตามรอยต้นตอ
    pnpm install ก็เสร็จสมบูรณ์ตามปกติด้วย จึงตรวจจับได้ยาก
    ถ้ามี EDR อย่าง Sentinel One หรือ CrowdStrike ก็น่าจะมีเบาะแสสำหรับสืบสวนมากกว่านี้

    • ถ้ามี EDR ก็น่าจะมีโอกาสสูงที่จะตรวจจับหรือบล็อก attack chain ของ Trufflehog ได้
  • ส่วนที่บอกว่า “clone repo ไปทั้งหมด 669 แห่ง” สะดุดตา
    บริษัทที่มีพนักงานไม่ถึง 100 คนแต่มี repo มากกว่า 600 อันนี่ถือว่าปกติไหม

    • ปกติเต็มที่ repo เป็นปศุสัตว์ ไม่ใช่สัตว์เลี้ยง
    • องค์กรของเรามี 7 คน แต่มี repo บน GitHub 365 อัน
      จำนวน repo ได้รับอิทธิพลจาก อายุองค์กรและอายุของโปรเจกต์ มากกว่าขนาดทีม
    • ถ้ามีสถาปนิกที่ชอบ microservices ก็ออกมาเป็นแบบนี้ได้
  • pnpm หยุด รัน lifecycle script อัตโนมัติ อย่าง preinstall ไปแล้ว ดูเหมือนพวกเขาจะใช้เวอร์ชันเก่า
    ดู PR ที่เกี่ยวข้อง

    • ท้ายบทความก็บอกว่าอัปเดตเป็น major version ล่าสุดแล้ว
    • ฉันนึกว่านั่นเป็นเหตุผลหลักที่คนเลือกใช้ pnpm เสียอีก เลยงงอยู่เหมือนกัน
    • ถ้าสรุปแล้วคือไปอัปเดต dependency ด้วย package manager เวอร์ชันเก่า แบบนี้ก็เป็นความรับผิดชอบของพวกเขาเอง
    • อาจเป็นไปได้ว่าในตัวโปรเจกต์เองมี script postinstall อยู่
      pnpm บล็อก script ของ dependency แต่ยังคงรัน script ระดับโปรเจกต์อยู่
  • ขอบคุณที่แชร์ post-mortem อย่างโปร่งใส
    กรณีแบบนี้สำคัญต่อทั้งอุตสาหกรรม
    สงสัยว่าทราฟฟิกจากการโจมตีสามารถแยกออกจากทราฟฟิกการพัฒนาปกติได้หรือไม่
    ฝั่งเราก็กำลังพยายามเพิ่ม egress filtering ในสภาพแวดล้อม dev เช่นกัน แต่ติดตรงที่ npm install พังบ่อย

    • ฟีเจอร์ IP allow list ของ GitHub Organizations น่าจะช่วยป้องกันการโจมตีลักษณะนี้ได้
  • กำลังกังวลเรื่องความปลอดภัยของ git บนโน้ตบุ๊กส่วนตัว
    ตอนนี้เก็บ SSH key ไว้ในเครื่องแล้วใช้ push
    อีกทั้งยังมีสิทธิ์ผู้ดูแลระบบด้วย เลยเสี่ยงอยู่ อยากรู้ว่าทำอย่างไรให้ปลอดภัยขึ้นได้

    • แนะนำให้ใช้ 1Password เพื่อจัดการ SSH key และลายเซ็น Git แล้วใช้ GitHub OAuth หรือการล็อกอินผ่าน CLI สำหรับ push/pull
      แบบนี้จะ แยก signing key กับ access key ออกจากกัน ได้ และบัญชีผู้ดูแลก็จัดการแยกต่างหากได้
      เอกสารที่เกี่ยวข้อง: 1Password SSH Agent, Git Commit Signing, GitHub OAuth, GitHub CLI Login
    • ฉันเก็บ SSH private key ไว้ใน TPM แล้วใช้ผ่าน PKCS11 จาก SSH agent
      ข้อดีคือคีย์ไม่สามารถรั่วออกไปภายนอกได้ แต่ถ้ามัลแวร์ทำงานอยู่บนเครื่องของฉันก็ยังอันตรายอยู่ดี
      ตัวอย่างบน Linux, ตัวอย่างบน macOS
    • ถ้าโน้ตบุ๊ก ติดเชื้อ ก็แทบไม่มีวิธีป้องกัน
      อาจทำให้ยากขึ้นได้เล็กน้อยด้วย TPM หรือ Yubikey แต่ป้องกันแบบสมบูรณ์เป็นไปไม่ได้
      งานที่ต้องใช้สิทธิ์ผู้ดูแลควรทำบนเครื่องเฉพาะแยกต่างหากจะปลอดภัยกว่า
    • ยังมีวิธีใส่ GPG key ลงใน Yubikey แล้วใช้ gpg-agent สำหรับยืนยันตัวตน SSH ได้ด้วย
      เวลาจะ push หรือ commit ก็ปลดล็อกด้วยการใส่ PIN
    • ถ้าบล็อกการ push ตรงเข้า branch main และ บังคับใช้ MFA ก็จะทำให้ผู้โจมตีเข้าถึง branch สำหรับ deploy ได้ยากขึ้นทันที
  • commit ที่ใช้ชื่อ Torvalds เป็น สัญลักษณ์ (signature) ที่มักพบหลังติดเชื้อ
    ใน บทวิเคราะห์อย่างเป็นทางการ ของ Microsoft ก็มีการกล่าวถึง
    เวิร์มตัวนี้ส่งเสียงดังมาก และผู้โจมตีบางรายก็นำ credential ที่หลุดมาไปใช้เปิดเผย repo ส่วนตัวเป็นสาธารณะหรือแก้ไข readme เพื่อใช้ประชาสัมพันธ์

  • ถ้าผู้โจมตีไม่ทำลายอะไร แต่ ลอบขโมยข้อมูลอย่างเงียบ ๆ แทน ก็ยังสงสัยว่าจะตรวจจับได้หรือไม่