- พบว่ามี แพ็กเกจอันตรายสำหรับขโมยข้อมูลรับรองมากกว่า 100 รายการ ถูกอัปโหลดขึ้นสู่รีโพซิทอรี NPM โดยไม่ถูกตรวจพบมาตั้งแต่เดือนสิงหาคม และมียอดดาวน์โหลดรวมมากกว่า 86,000 ครั้ง
- บริษัทความปลอดภัย Koi รายงานว่าแคมเปญโจมตีที่ตั้งชื่อว่า PhantomRaven ได้ใช้ประโยชน์จากฟีเจอร์ Remote Dynamic Dependencies (RDD) ของ NPM เพื่อเผยแพร่แพ็กเกจอันตราย 126 รายการ
- RDD เป็น โครงสร้างที่ทำให้แพ็กเกจสามารถดาวน์โหลดโค้ด dependency แบบไดนามิกจากโดเมนที่ไม่น่าเชื่อถือได้ จึงไม่ถูกตรวจจับโดยเครื่องมือวิเคราะห์แบบสแตติก
- ผู้โจมตีใช้ฟีเจอร์นี้เพื่อ ดาวน์โหลดโค้ดอันตรายผ่านการเชื่อมต่อ HTTP และใน metadata ของแพ็กเกจจะแสดงเป็น “0 Dependencies” ทำให้นักพัฒนาและสแกนเนอร์ด้านความปลอดภัยไม่ทันสังเกต
- ช่องโหว่เชิงโครงสร้างนี้สะท้อนให้เห็นถึง ข้อจำกัดของการดูแลความปลอดภัยในระบบนิเวศ NPM และความเสี่ยงของกลไกการติดตั้งอัตโนมัติ
การแพร่กระจายของแพ็กเกจอันตรายในรีโพซิทอรี NPM
- ผู้โจมตีอาศัย จุดอ่อนเชิงโครงสร้าง ของรีโพซิทอรีโค้ด NPM เพื่ออัปโหลด แพ็กเกจสำหรับขโมยข้อมูลรับรอง มากกว่า 100 รายการตั้งแต่เดือนสิงหาคม
- แพ็กเกจส่วนใหญ่ถูกเผยแพร่โดยไม่ถูกตรวจพบ และมียอดดาวน์โหลดสะสม มากกว่า 86,000 ครั้ง
- บริษัทความปลอดภัย Koi ตั้งชื่อการโจมตีนี้ว่า แคมเปญ PhantomRaven และวิเคราะห์ว่ามีการใช้ประโยชน์จากฟีเจอร์เฉพาะของ NPM
- ตามข้อมูลของ Koi จากแพ็กเกจอันตราย 126 รายการ มีราว 80 รายการที่ยังคงอยู่บน NPM ณ เวลาที่เขียนบทความ
โครงสร้างที่เปราะบางของ Remote Dynamic Dependencies (RDD)
- RDD เป็นฟีเจอร์ที่อนุญาตให้แพ็กเกจ ดาวน์โหลดโค้ด dependency แบบไดนามิกจากเว็บไซต์ภายนอก
- โดยปกติ dependency จะถูกดาวน์โหลดจาก โครงสร้างพื้นฐานที่เชื่อถือได้ ของ NPM แต่ RDD อนุญาตให้ดาวน์โหลดผ่าน การเชื่อมต่อที่ไม่เข้ารหัส เช่น HTTP ได้ด้วย
- ผู้โจมตี PhantomRaven ใช้ฟีเจอร์นี้เพื่อตั้งค่าให้ดาวน์โหลดโค้ดจาก URL อันตราย เช่น
http://packages.storeartifact.com/npm/unused-imports- dependency ลักษณะนี้ มองไม่เห็นทั้งสำหรับนักพัฒนาและสแกนเนอร์ด้านความปลอดภัย และในข้อมูลแพ็กเกจจะแสดงเป็น “0 Dependencies”
- เนื่องจากฟีเจอร์ติดตั้งอัตโนมัติของ NPM ทำให้โค้ด dependency ที่ ‘มองไม่เห็น’ เหล่านี้ถูกเรียกใช้งานโดยอัตโนมัติ
ข้อจำกัดของการตรวจจับโดยเครื่องมือความปลอดภัย
- Oren Yomtov จาก Koi ระบุว่า “PhantomRaven เป็นกรณีที่ใช้ประโยชน์จาก จุดบอดของเครื่องมือความปลอดภัยที่มีอยู่ได้อย่างแยบยล”
- RDD ไม่ถูกตรวจจับโดยเครื่องมือวิเคราะห์แบบสแตติก
- ด้วยเหตุนี้ ผู้โจมตีจึงสามารถ หลบเลี่ยงกระบวนการตรวจสอบความปลอดภัย และเผยแพร่โค้ดอันตรายได้
ปัจจัยเสี่ยงเพิ่มเติม
- Koi อธิบายว่า dependency ที่ดาวน์โหลดผ่าน RDD จะถูก ดาวน์โหลดใหม่จากเซิร์ฟเวอร์ของผู้โจมตีทุกครั้งที่มีการติดตั้ง
- ไม่มีแคชหรือการจัดการเวอร์ชัน ทำให้แม้จะเป็นแพ็กเกจเดียวกันก็ยังมีความเป็นไปได้ที่จะถูกฝัง โค้ดอันตรายที่แตกต่างกันในแต่ละครั้งที่ติดตั้ง
- โครงสร้างการดาวน์โหลดแบบไดนามิกเช่นนี้ทำให้ การตรวจสอบความถูกต้องสมบูรณ์ของแพ็กเกจทำได้ยาก
โครงสร้างและพื้นหลังของ NPM
- NPM เป็น ตัวจัดการแพ็กเกจสำหรับ JavaScript ที่ดูแลโดย npm, Inc. ซึ่งเป็นบริษัทในเครือของ GitHub
- เป็นตัวจัดการแพ็กเกจพื้นฐานของ Node.js และประกอบด้วยไคลเอนต์บรรทัดคำสั่งกับ npm registry
- ภายใน registry มีทั้งแพ็กเกจสาธารณะและแพ็กเกจส่วนตัวแบบเสียเงินเก็บอยู่ และสามารถค้นหาได้ผ่านเว็บไซต์
- เหตุการณ์ครั้งนี้ถูกชี้ว่าเป็น ตัวอย่างที่แสดงให้เห็นว่าโครงสร้างการจัดการ dependency อัตโนมัติของ NPM สามารถถูกนำไปใช้โจมตีได้
ประเด็นอื่นที่กล่าวถึง
- ช่วงท้ายบทความมีการกล่าวถึง ความเห็นที่ว่าควรบล็อกการรัน JavaScript ที่ไม่จำเป็น
- อย่างไรก็ตาม เหตุโจมตีครั้งนี้ถูกชี้ว่าเป็น กรณีที่แม้แต่โค้ด JavaScript ที่จำเป็นก็ยังถูกนำไปใช้ในทางที่ผิดได้
2 ความคิดเห็น
ผมได้ลองทำสคริปต์สแกนเนอร์แบบเรียลไทม์ไว้ครับ
ที่ path ของรีโพซิทอรีที่น่าสงสัย
npx sha1-hulud-scanner
ให้พิมพ์คำสั่งนี้ได้เลยครับ
ซอร์สโค้ด : https://github.com/developerjhp/sha1-hulud-scanner
ความเห็นจาก Hacker News
ช่วงนี้ฉันตั้ง alias ให้รันคำสั่ง
npmภายใน Docker containerแบบนี้จะไม่เผย environment variables ของฉัน, ไม่เข้าถึงไฟล์นอกไดเรกทอรีปัจจุบัน, และไม่สามารถเข้าถึงไฟล์ตั้งค่าอย่าง
.bashrcได้อ้างอิง: Run tools inside Docker
npmก็ไปดาวน์โหลด โค้ดตามอำเภอใจ ที่จะถูกรันทันทีอยู่ดีฉันแนะนำ
pnpmแทน เพราะค่าเริ่มต้นจะไม่รัน lifecycle scripts และสามารถกำหนด whitelist ของสคริปต์ที่อนุญาตได้ถ้าต้องการการป้องกันจริง ๆ ต้องรันทั้งกระบวนการ ไม่ใช่แค่ตอนติดตั้ง ภายใน sandbox
การบล็อกแค่ post-install แบบตอนนี้เป็นมาตรการครึ่ง ๆ กลาง ๆ เท่านั้น การโจมตีซัพพลายเชนกำลังอันตรายขึ้นเรื่อย ๆ
ถ้า
neovimหรือvscodeติดเชื้อ ก็ทำเรื่องอันตรายได้มากพอแล้วภายใต้สิทธิผู้ใช้alias ธรรมดาใช้ได้กับ
node/npmแต่ใช้กับโปรแกรมอื่นยาก เพราะต้อง mount ทรัพยากรที่ container ต้องใช้ฉันสงสัยมานานแล้วว่าทำไมคนถึงรัน
npmบนระบบกันแบบไม่คิดอะไรในฐานะคนที่คุ้นกับ reproducible builds แบบ
makeฉันช็อกมากที่npmดาวน์โหลดของต่างกันทุกครั้งและให้ผลลัพธ์ต่างกันแม้แต่การสร้าง CSS ก็ยังผูกกับ dependency ของ npm ซึ่งดูแปลกมาก เลยเคยลอง freeze environment ของ npm ทั้งก้อนไว้ใน Docker แต่ดูเหมือนเป็นสงครามที่แพ้อยู่ดี
maven,nuget,pip,npmก็เหมือนกันถ้ายังพึ่ง package manager ของดิสโทรแบบเมื่อก่อน ก็คงไม่มี ecosystem ที่ขยับเร็ว แบบทุกวันนี้
แต่อย่างน้อยตอนนี้ก็เริ่มมี package manager รุ่นใหม่ที่เสริมความปลอดภัยมากขึ้น การด่าที่เครื่องมือโดยไม่เข้าใจเหตุผลเบื้องหลังไม่ค่อยถูกนัก
npmไว้ใน Docker แล้ว ก็อยากถามว่าได้ ตรวจสอบ environment นั้นใหม่ทุกครั้งหลังอัปเดต dependency หรือเปล่าจริง ๆ แล้ว
npmกับpnpmก็มี lock file สำหรับตรึง dependency เป็นค่าเริ่มต้นอยู่แล้วnpm install thing” มันง่ายและต้นทุนต่ำเกินไปโอเพนซอร์สจำนวนมากเลยเต็มไปด้วย โค้ดเพื่อใส่ในเรซูเม่มากกว่าคุณภาพจริง และสุดท้ายก็ถูกนำไปใช้สร้างพวกตัวติดตามโฆษณาของบริษัทยักษ์ใหญ่หรือแอปกระเป๋าเงิน
npm installไม่ได้แค่ดาวน์โหลดแพ็กเกจ แต่มัน รันโค้ด ด้วยpreinstall, install, postinstall hooks ใน
package.jsonจะถูกรันจริงมีเหตุผลที่ชอบธรรมอะไรบ้างที่ต้องให้ขั้นตอนติดตั้งสามารถรันคำสั่งตามอำเภอใจได้?
รายงานที่เกี่ยวข้อง: PhantomRaven npm malware
อีกกรณีหนึ่ง: บล็อก Socket.dev
ตัวอย่างเช่นแพ็กเกจ Linux kernel จะรัน post-install script เพื่อ สร้าง initramfs ใหม่และอัปเดต GRUB
แพ็กเกจ DEB/RPM ส่วนใหญ่ก็มีสคริปต์ลักษณะนี้ นี่จึงเป็นปัญหาในระดับการออกแบบ
npmนั้น ใครก็อัปโหลดแพ็กเกจได้ดิสโทร Linux มีระบบ maintainer ที่เชื่อถือได้ และบางครั้งก็สร้าง root of trust แบบ PGP ด้วยตัวเอง
แต่
npm,pip,rubygems,cargoฯลฯ นั้นจริง ๆ ก็เป็นเพียง “curl | bash” เวอร์ชันที่ดูดีขึ้นเท่านั้นpost-install build แบบนี้จำเป็นเพื่อให้ภาระการดูแลรักษาลดลง
Package.swiftจริงเช่นกันแต่ได้ยินมาว่ามีการ sandbox ที่เข้มมาก เลยเอาไปใช้โจมตีได้ยาก
อ้างอิง: เอกสาร SwiftPM, PackageDescription
pnpm v10ปิด lifecycle scripts ทั้งหมดเป็นค่าเริ่มต้น และให้ผู้ใช้เป็นคนอนุญาตเองการพูดคุยที่เกี่ยวข้อง
ดูจากการโจมตี
npmช่วงหลัง ๆ แล้ว ก็เริ่มสงสัยว่าการพัฒนาด้วยnpmยัง ปลอดภัย อยู่ไหมทุกครั้งที่เริ่มโปรเจกต์ React ก็มีแพ็กเกจนับร้อยถูกติดตั้ง ทั้งที่ไม่รู้ด้วยซ้ำว่ามันทำอะไร
ฝั่ง backend ยังระบุแพ็กเกจที่ต้องใช้แบบชัดเจน แต่ฝั่ง frontend เหมือน กล่องแพนโดราของช่องโหว่
npmใหญ่ที่สุดและเป็นข่าวบ่อยกว่าเท่านั้นjjของ Rust แล้วมี 470 แพ็กเกจ ส่วนwan2gpของ Python มี 211 แพ็กเกจ ก็พอ ๆ กันหมดเหมือนกรณี
xzที่ dependency แต่ละตัวผูกอยู่กับ บุคคลสุ่ม ๆ และเราต้องหวังว่าพวกเขาจะไม่โดน social engineeringPyPIก็ไม่ได้ปลอดภัย มีกรณีที่ แพ็กเกจปกติถูกฝังโค้ดอันตราย ผ่านการแฮ็ก GitHub Actions ด้วยทุกครั้งที่พัฒนาด้วยเฟรมเวิร์กอย่าง Angular หรือ Vue ก็รู้สึกกังวล
พอเห็น dependency หลายพันตัวใน
node_modulesก็รู้สึกเหมือน ลางบอกเหตุแห่งหายนะแค่นักพัฒนาโอเพนซอร์สคนหนึ่งโดนฟิชชิงก็อาจติดเชื้อได้ทันที
ecosystem ของ JavaScript พังตั้งแต่รากฐาน การพิมพ์ผิดแค่ครั้งเดียวก็อาจเปิดทางให้โดนโจมตีซัพพลายเชน
NuGet หรือ Maven ก็เป็นได้เหมือนกัน แต่ฝั่งนั้นมี standard library ใหญ่กว่า จึงมี dependency น้อยกว่าและรู้สึกควบคุมได้มากกว่า
ไม่สมบูรณ์แบบ แต่ก็ยังดีกว่าอีกขั้น
ในยอดดาวน์โหลด 86,000 ครั้งนั้น ส่วนใหญ่อาจไม่ใช่ผู้ใช้จริง แต่เป็น สแกนเนอร์อัตโนมัติหรือบอต
พอปล่อยเวอร์ชันใหม่ก็มักถูกดาวน์โหลดหลายร้อยครั้งภายในวันสองวัน แต่ผู้ใช้จริงอาจไม่มีเลยก็ได้
หมายความว่าผู้ใช้ที่ติดเชื้อจริงอาจมีน้อยมาก
ยังมีการโจมตีที่เล็งชื่อแพ็กเกจซึ่ง AI chatbot สร้างขึ้นแบบหลอนข้อมูล ด้วย มันจึงมากกว่าแค่สถิติธรรมดา
ถ้าอยากดูคำอธิบายการโจมตีแบบละเอียดขึ้น ดูได้ที่ บทความของ BleepingComputer
สงสัยว่ามีวิธีตรวจจับหรือกรองแพ็กเกจที่ใช้ HTTP URL เป็น dependency ระหว่าง
npm installไหมเพราะมันสามารถส่ง payload ต่างกันตามผู้ร้องขอได้ ทำให้สแกนเนอร์ทั่วไปตรวจจับได้ยาก
ในฐานะนักพัฒนาแบบงานอดิเรก ฉันกำลังคิดว่าควรเริ่มป้องกัน การโจมตีซัพพลายเชน แบบนี้อย่างไรดี
พอตาม tutorial ดัง ๆ แล้วติดตั้ง dependency ไปเรื่อย ๆ ก็เผลอชินชากับเรื่องความปลอดภัย
ฉันก็รันหลายบริการใน homelab เหมือนกัน เลยกังวลว่าบอตอาจเจาะเข้ามาได้ ควรเริ่มจากตรงไหน?
ไม่ได้การันตีสมบูรณ์แบบ แต่ก็ดีกว่าปล่อยให้ทั้งเซิร์ฟเวอร์ถูกเจาะมาก
การคัดลอกเฉพาะโค้ดที่ต้องใช้มาใช้เองก็เป็นทั้งวิธีเรียนรู้ที่ดีและปลอดภัยกว่า
โครงสร้างแบบนี้ช่วยให้ ยืนยันความน่าเชื่อถือในระดับดิสโทร ได้ โดยไม่ต้องให้ผู้ใช้หลายล้านคนมาตรวจสอบเอง
เช่น Hono, Zod
ช่วงหลังฉันย้ายไปใช้ Bun ซึ่งมีของพื้นฐานอย่าง DB driver หรือ S3 client มาให้ในตัว จึงต้องดาวน์โหลดเพิ่มน้อยลง
โครงสร้างที่ดึง dependency มาจาก lifecycle hooks สามารถกลายเป็น จุดเปลี่ยนของการโจมตี ได้ทุกเมื่อ
วันนี้อาจยังปกติ แต่ภายหลังถ้าเจ้าของบัญชีถูกแฮ็กหรือเปลี่ยนใจ มันก็อาจกลายเป็นโค้ดอันตรายได้
install hooks ในลักษณะนี้จึงเป็น การออกแบบที่ยั่งยืนไม่ได้ ในท้ายที่สุด