1 คะแนน โดย GN⁺ 1 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • บัญชี npm ของ atool ถูกเจาะเมื่อวันที่ 19 พฤษภาคม 2026 ทำให้มีการปล่อยเวอร์ชันอันตราย 637 เวอร์ชันไปยัง 317 แพ็กเกจโดยอัตโนมัติภายในเวลาประมาณ 22 นาที
  • เพย์โหลดเป็น สคริปต์ Bun แบบ obfuscate ขนาด 498KB และใช้โครงสร้างสแกนเนอร์กับ regex แบบเดียวกับ Mini Shai-Hulud ที่ถูกใช้ในการเจาะ SAP
  • เป้าหมายการขโมยข้อมูลขยายไปถึง ข้อมูลรับรอง AWS, โทเค็น Kubernetes, Vault, GitHub PAT, โทเค็น npm, คีย์ SSH และความลับที่เก็บไว้ในเครื่อง
  • ใน CI มันแลก GitHub Actions OIDC เป็นโทเค็น npm publish และอาศัย ลายเซ็น Sigstore กับการฉีด workflow อันตราย
  • แนวทางรับมือคือตรวจสอบว่ามีการติดตั้งเวอร์ชันที่ถูกเจาะหรือไม่ เปลี่ยนข้อมูลรับรองทั้งหมดที่อาจถูกเข้าถึงได้ และใช้ lockfile·การ pin dependencies พร้อมการตรวจสอบก่อนติดตั้ง

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

  • atool บัญชี npm (i@hust.cc) ถูกเจาะเมื่อวันที่ 19 พฤษภาคม 2026 และมีการปล่อยเวอร์ชันอันตราย 637 เวอร์ชันไปยัง 317 แพ็กเกจในช่วงเวลาประมาณ 22 นาที
  • บัญชีนี้ดูแลแพ็กเกจอยู่ 547 รายการ และผู้โจมตีได้ทำ version bump สองรอบกับมากกว่า 314 แพ็กเกจในนั้น
  • แพ็กเกจที่ได้รับผลกระทบมี size-sensor (ดาวน์โหลด 4.2 ล้านครั้งต่อเดือน), echarts-for-react (3.8 ล้าน), @antv/scale (2.2 ล้าน), timeago.js (1.15 ล้าน) และแพ็กเกจในสโคป @antv อีกจำนวนมาก
  • เพย์โหลดเป็นสคริปต์ Bun แบบ obfuscate ขนาด 498KB และใช้โครงสร้างสแกนเนอร์, regex สำหรับข้อมูลรับรอง และรูปแบบการ obfuscate แบบเดียวกับ Mini Shai-Hulud toolkit ที่ใช้ในการเจาะ SAP เมื่อ 3 สัปดาห์ก่อน
  • ข้อมูลที่ขโมยได้ถูก commit เป็น Git object ไปยังที่เก็บ GitHub แบบสาธารณะ หรือส่งผ่าน HTTPS POST ที่เข้ารหัสด้วย RSA+AES ไปยัง t.m-kosche[.]com

วิธีการปล่อยแพ็กเกจและความเสี่ยงของ semver

  • ระลอกแรกปล่อยประมาณ 317 เวอร์ชันในช่วง 01:39~01:56 UTC ของวันที่ 19 พฤษภาคม 2026 และระลอกที่สองทำ version bump เพิ่มอีกราว 314 เวอร์ชันกับแพ็กเกจชุดเดิมในช่วง 02:05~02:06 UTC
  • แพ็กเกจส่วนใหญ่ 309 รายการได้รับเวอร์ชันอันตรายอย่างละ 2 เวอร์ชัน คือหนึ่งเวอร์ชันต่อหนึ่งระลอก
  • แพ็กเกจ 4 รายการคือ size-sensor, echarts-for-react, jest-canvas-mock, jest-date-mock ได้รับ 3 เวอร์ชัน ซึ่งบ่งชี้ว่าน่าจะถูกใช้สำหรับการทดสอบเบื้องต้น
  • ผู้โจมตีไม่ได้ย้าย dist-tag latest ในแพ็กเกจส่วนใหญ่ แต่การตีความ semver ของ npm จะเลือกเวอร์ชันสูงสุดที่ตรงกับช่วงที่กำหนด โดยไม่ขึ้นกับ latest
  • ตัวอย่างเช่น แม้ latest ของ echarts-for-react จะยังอยู่ที่ 3.0.6 แต่โปรเจกต์ที่ระบุ "echarts-for-react": "^3.0.6" อาจถูก resolve ไปยังเวอร์ชันอันตราย 3.2.7 ในการ clean install ครั้งถัดไป

เส้นทางการทำงานและเพย์โหลด

  • ทุกเวอร์ชันที่ถูกดัดแปลงได้เพิ่ม version bump และ "preinstall": "bun run index.js" ไว้ใน package.json
  • จากเวอร์ชันอันตรายทั้งหมด 637 เวอร์ชัน มี 630 เวอร์ชันที่เพิ่ม @antv/setup: github:antvis/G2#<commit-sha> ลงใน optionalDependencies เพื่อดึงเพย์โหลดสำเนาที่สองมาใช้
  • ฮุก preinstall จะทำงานก่อนการติดตั้ง dependency และต้องอาศัยรันไทม์ Bun
  • แม้ preinstall จะถูกบล็อกหรือถูกข้ามไป สคริปต์ prepare ของ commit ปลอมบน GitHub ก็ยังคงเป็นเส้นทางรันครั้งที่สอง
  • index.js เป็น Bun bundle แบบ obfuscate ขนาด 498KB แบบบรรทัดเดียว และมีข้อกำหนด Bun, การ obfuscate แบบตัวแปร hex, โครงสร้างสแกนเนอร์ที่มี flush threshold 100KB และชุด regex สำหรับข้อมูลรับรองแบบเดียวกับ Mini Shai-Hulud payload ที่ใช้ในการเจาะ SAP
  • การตรวจจับสภาพแวดล้อม CI จะเช็กตัวแปรสภาพแวดล้อมของมากกว่า 20 แพลตฟอร์ม เช่น GitHub Actions, Jenkins, GitLab CI, CircleCI, Travis, Buildkite, Drone, TeamCity, AppVeyor, Bitbucket Pipelines, CodeBuild, Azure DevOps, Netlify และ Vercel

เป้าหมายการเก็บข้อมูลรับรอง

  • เพย์โหลดอ่านตัวแปรสภาพแวดล้อมที่มีชื่อถูกเข้ารหัสมากกว่า 80 รายการ และสแกนเนื้อหาไฟล์ด้วย regex
  • เป้าหมายหลักได้แก่ GitHub token, npm token, GitHub Actions JWT, AWS key, Azure key, DB connection string, Stripe key, SSH key, Docker auth, Vault token, Kubernetes token และ credential ที่ฝังอยู่ใน URL
  • ตัวสแกนไฟล์จะอ่านตำแหน่งเก็บข้อมูลรับรองมาตรฐานในโฮมไดเรกทอรี เช่น .ssh, .aws/credentials, .npmrc, .docker/config.json, .kube/config
  • มันไล่ตามลำดับการ resolve AWS credential ทั้งหมด และดึง IAM role credential จาก EC2 IMDSv2 กับ ECS container credential endpoint รวมถึงพยายามเข้าถึง AWS STS GetCallerIdentity และ Secrets Manager
  • สำหรับ Vault จะตรวจสอบไฟล์ token และค่า VAULT_ADDR, VAULT_TOKEN, VAULT_ROLE เป็นต้น และหากมี credential ที่ใช้ได้ก็จะพยายาม enumerate secret และทำ AWS·Kubernetes authentication
  • สำหรับ Kubernetes จะตรวจสอบ service account token และ KUBECONFIG และหากมี Docker socket ก็จะพยายาม enumerate คอนเทนเนอร์บนโฮสต์และทำ container escape

C2 และการขโมยข้อมูลออก

  • GitHub API ถูกใช้เสมือนเป็น C2 โดยใช้ GET /user เพื่อตรวจสอบ GitHub token ที่ขโมยมา และใช้ GET /user/orgs เพื่อ enumerate องค์กร
  • โทเค็นที่มีสิทธิ์ repo หรือ public_repo เพียงพอ จะถูกใช้เพื่อสร้างที่เก็บสำหรับขโมยข้อมูลของผู้โจมตี
  • คำอธิบายของที่เก็บที่สร้างขึ้นเก็บเป็นสตริงย้อนกลับ niagA oG eW ereH :duluH-iahS ซึ่งเมื่อกลับลำแล้วจะได้ “Shai-Hulud: Here We Go Again”
  • ชื่อที่เก็บจะใช้คำธีม Dune 2 คำร่วมกับตัวเลข เช่น harkonnen-melange-742, fremen-sandworm-315, gesserit-navigator-508
  • ข้อมูลที่ขโมยออกจะถูกบันทึกผ่าน Git Data API ตามลำดับ blob, tree, commit, ref update
  • ตัวส่ง HTTPS แยกต่างหากถูกทำให้ดูเหมือน OpenTelemetry OTLP trace ingestion endpoint ที่ hxxps://t.m-kosche[.]com/api/public/otel/v1/traces
  • เพย์โหลด HTTPS จะเข้ารหัส gzip JSON ด้วย AES-256-GCM และ wrap คีย์ AES ด้วย RSA-OAEP โดยใช้ public key ที่ฝังไว้ตายตัว

การโจมตี CI/CD และสายโซ่ความเชื่อถือ

  • จากที่เก็บ GitHub ที่โทเค็นที่ขโมยมาสามารถเข้าถึงได้ มันจะรวบรวมประวัติการรัน workflow, artifact, ชื่อ secret, รายการ branch และการตั้งค่า Claude Code
  • แม้จะเข้าถึงค่า secret ผ่าน GitHub API โดยตรงไม่ได้ แต่ชื่อ secret ก็เผยให้เห็นว่ามีข้อมูลรับรองใดอยู่บ้าง
  • workflow อันตรายจะถูกฉีดเข้าไปใน .github/workflows/codeql.yml โดยใช้ชื่อ Run Copilot และถูกทริกเกอร์เมื่อ push
  • workflow จะใส่ repository secrets ทั้งหมดลงในตัวแปรสภาพแวดล้อมแบบ JSON ด้วย VARIABLE_STORE: ${{ toJSON(secrets) }} บันทึกเป็น format-results.txt แล้วอัปโหลดเป็น artifact
  • หลังจากทำเสร็จ มันจะดาวน์โหลดไฟล์ zip ของ artifact และลดร่องรอยการฉีดด้วยการลบ workflow run และรีเซ็ต branch ref
  • ใน CI ที่มี GitHub Actions OIDC มันจะพยายามแลกเป็น npm publish token ผ่าน endpoint https://registry.npmjs.org/-/npm/…;
  • เพย์โหลดยังมี implementation สำหรับการลงนาม Sigstore ที่รวมรูปแบบ Fulcio, Rekor และ SLSA provenance ไว้ด้วย ทำให้สามารถสร้าง artifact ที่ลงนามด้วย CI identity ที่ถูกเจาะได้

การติดเชื้อในสภาพแวดล้อมการพัฒนาและเอเจนต์เขียนโค้ด AI

  • เพย์โหลดมุ่งเป้าไปที่สภาพแวดล้อม Claude Code, Codex และ VS Code ผ่านการฉีดจากระยะไกลด้วย GitHub API และการติดเชื้อในเครื่องผ่านการเขียนลงไฟล์ซิสเต็ม
  • ใช้โทเคนที่ขโมยมาเพื่อคอมมิต .vscode/tasks.json, .claude/index.js, .claude/settings.json, .claude/setup.mjs, .vscode/setup.mjs ไปยังรีโพซิทอรีที่เข้าถึงได้
  • .claude/settings.json ลงทะเบียนฮุก SessionStart เพื่อรัน node .claude/setup.mjs ทุกครั้งที่เริ่มเซสชัน Claude Code
  • .vscode/tasks.json ใช้ "runOn": "folderOpen" เพื่อทริกเกอร์การทำงานเมื่อมีการเปิดโฟลเดอร์โปรเจกต์
  • setup.mjs ดาวน์โหลด Bun v1.3.14 จากรีลีสทางการของ GitHub จากนั้นแตกไฟล์ไบนารีและรันเพย์โหลด
  • เส้นทางการติดเชื้อในเครื่องจะคัดลอกเพย์โหลดไปยัง ~/.claude/package/index.js และ ~/.codex/package/index.js แล้วสแกนไฟล์ตั้งค่าโปรเจกต์เดิมเพื่อฉีดฮุก
  • บน Mac ที่ใช้ Apple Silicon จะข้ามเส้นทางการติดเชื้อในเครื่องนี้ ส่วน Linux, Intel Mac และ Windows จะถูกรัน

ความคงอยู่และ GitHub dead-drop C2

  • เดมอนที่ทำงานต่อเนื่องซึ่งปลอมตัวเป็น kitty-monitor จะถูกติดตั้งเป็นบริการผู้ใช้ systemd บน Linux และเป็น ~/Library/LaunchAgents/com.user.kitty-monitor.plist บน macOS
  • ตัวติดตั้งใช้ systemctl --user enable --now และ loginctl enable-linger เพื่อให้ยังคงอยู่แม้หลังออกจากระบบ
  • เดมอน cat.py จะโพล GitHub Search API ทุก 1 ชั่วโมง และค้นหาคอมมิตที่มีคีย์เวิร์ด firedalazer
  • รูปแบบข้อความคอมมิตคือ firedalazer <base64_url>.<base64_signature> และจะตรวจสอบลายเซ็น RSA-PSS ด้วยกุญแจสาธารณะ RSA ขนาด 4096 บิตที่ฮาร์ดโค้ดไว้ ก่อนรันโค้ด Python จาก URL
  • เดมอน gh-token-monitor อีกตัวหนึ่งจะโพลโทเคน GitHub ที่ขโมยมาในช่วงทุก 60 วินาที โดยมี TTL 24 ชั่วโมง
  • ตัวสแกนหน่วยความจำของโปรเซสจะดัมพ์พื้นที่หน่วยความจำที่อ่านได้ของโปรเซส worker ของ GitHub Actions runner โดยใช้ /proc/pid/maps+mem บน Linux และ ReadProcessMemory บน Windows

คอมมิตปลอมเป็น antvis/G2

  • เวอร์ชันอันตราย 630 จาก 637 เวอร์ชันมีรายการ optionalDependencies ที่ชี้ไปยังคอมมิตเฉพาะในรีโพซิทอรี antvis/G2
    {
      "optionalDependencies": {
        "@antv/setup": "github:antvis/G2#1916faa365f2788b6e193514872d51a242876569"
      }
    }
    
  • เมื่อ npm แก้ความหมาย dependency แบบ github: ก็จะดึงคอมมิตดังกล่าวมา ค้นหา package.json แล้วรัน lifecycle script
  • ในคอมมิตนั้นมี package.json ที่ประกาศ @antv/setup และมีสคริปต์ prepare รวมถึง index.js ขนาด 499KB ที่ออบฟัสเคตเพย์โหลด Shai-Hulud เดิมซ้ำอีกครั้ง
  • && exit 1 ในสคริปต์ prepare ทำให้ optional dependency ล้มเหลว แต่ npm ไม่ถือว่าความล้มเหลวของ optional dependency เป็นข้อผิดพลาดร้ายแรง จึงติดตั้งต่อไปได้
  • Git API แสดงคอมมิต SHA ที่แตกต่างกัน 3 ค่า ซึ่งถูกพุชไปยัง antvis/G2 และทั้งหมดไม่ได้ผูกอยู่กับบรันช์ใด
  • คอมมิตทั้งสามมีเมทาดาทาเหมือนกันคือ author huiyu.zjt <Alexzjt@users.noreply.github.com>, commit message New Package, ไม่มี parent และไม่มีลายเซ็น GPG
  • ผู้โจมตีสามารถสร้าง payload orphan commit ใน fork โดยไม่ต้องมีสิทธิ์เขียนใน antvis/G2 แล้วลบ fork ทิ้ง เพื่อให้ยังเหลือคอมมิตที่ดึงด้วย SHA ได้ภายใต้เนมสเปซของรีโพซิทอรีต้นทาง
  • วิธีนี้เป็นปัญหาคอมมิตปลอมใน GitHub Actions ประเภทเดียวกับที่ Chainguard ได้บันทึกไว้ และในที่นี้ถูกนำมาใช้กับการแก้ dependency แบบ github: ของ npm

ตัวบ่งชี้การถูกเจาะ

  • แพ็กเกจที่เผยแพร่โดย atool(i@hust.cc) ระหว่าง 2026-05-19 01:44~02:06 UTC เป็นเป้าหมายที่ต้องตรวจสอบ
  • สคริปต์ preinstall คือ bun run index.js
  • SHA256 ของเพย์โหลดคือ a68dd1e6a6e35ec3771e1f94fe796f55dfe65a2b94560516ff4ac189390dfa1c
  • คอมมิตปลอม antvis/G2 มีดังนี้
    • 1916faa365f2788b6e193514872d51a242876569 — 626 เวอร์ชัน
    • 7cb42f57561c321ecb09b4552802ae0ac55b3a7a — 2 เวอร์ชัน
    • dc3d62a2181beb9f326952a2d212900c94f2e13d — 1 เวอร์ชัน, ถูก garbage collected
  • Network IoC มี hxxps://t.m-kosche[.]com/api/public/otel/v1/traces, EC2 metadata ที่ 169.254.169.254 และคำขอ ECS container metadata ที่ 169.254.170.2
  • Repository IoC มีบรันช์ chore/add-codeql-static-analysis, workflow Run Copilot และ .github/workflows/codeql.yml ที่ดัมพ์ toJSON(secrets) ไปยัง format-results.txt
  • Development environment IoC มีฮุก SessionStart ใน .claude/settings.json, "runOn": "folderOpen" ใน .vscode/tasks.json, .claude/setup.mjs, .vscode/setup.mjs
  • Persistence IoC มี kitty-monitor.service, com.user.kitty-monitor.plist, ~/.local/bin/gh-token-monitor.sh, ~/.local/share/kitty/cat.py, /var/tmp/.gh_update_state

แพ็กเกจตัวอย่างที่ควรตรวจสอบ

  • ตาราง compromised-packages.csv มี 2 คอลัมน์คือ Package และ Compromised Versions และตามตารางแสดงแพ็กเกจ 317 รายการ
  • ควรตรวจสอบใน lockfile ว่ามีแพ็กเกจดังกล่าวและมีเวอร์ชันอันตรายที่เผยแพร่ใน 2026-05-19 หรือไม่
  • แพ็กเกจ @antv ตัวอย่างและเวอร์ชันอันตราย
    • @antv/g2: 5.5.8, 5.6.8
    • @antv/g6: 5.2.1, 5.3.1
    • @antv/g: 6.4.1, 6.5.1
    • @antv/l7: 2.26.10, 2.27.10
    • @antv/x6: 3.2.7, 3.3.7
    • @antv/s2: 2.8.1, 2.9.1
    • @antv/f2: 5.15.0, 5.16.0
  • แพ็กเกจ npm ทั่วไปและเวอร์ชันอันตราย
    • echarts-for-react: 3.0.7, 3.1.7, 3.2.7
    • size-sensor: 1.0.4, 1.1.4, 1.2.4
    • jest-canvas-mock: 2.5.3, 2.6.3, 2.7.3
    • jest-date-mock: 1.0.11, 1.1.11, 1.2.11
    • timeago.js: 4.1.2, 4.2.2
    • timeago-react: 3.1.7, 3.2.7
    • @lint-md/cli: 2.1.0, 2.2.0
    • @lint-md/core: 2.1.0, 2.2.0
    • @lint-md/parser: 0.1.14, 0.2.14

การตอบสนองและการป้องกัน

  • หากมีการติดตั้งเวอร์ชันที่ถูกเจาะ ควรหมุนเปลี่ยน npm token, GitHub PAT, AWS key, SSH key, ข้อมูลรับรองคลาวด์, รหัสผ่านฐานข้อมูล, Vault token, Kubernetes service account token และค่าความลับในตัวจัดการรหัสผ่านภายในเครื่อง ที่เข้าถึงได้จากสภาพแวดล้อมการบิลด์
  • ควรบล็อก t.m-kosche[.]com ทั้งในระดับเครือข่ายและ DNS
  • ควรตรวจสอบว่ามีการสร้าง public repository ที่ไม่ได้รับอนุญาตภายใต้บัญชี GitHub ที่มี token ซึ่งเข้าถึงได้จากสภาพแวดล้อมการบิลด์หรือไม่
  • ควรตรวจสอบการ publish แพ็กเกจที่ไม่ได้รับอนุญาตและบันทึกการแลกเปลี่ยน npm OIDC token ใน CI pipeline
  • ควรตรวจสอบ Sigstore transparency log ว่ามี signed artifact ที่สร้างขึ้นด้วย CI identity ที่ถูกเจาะหรือไม่
  • ควรตรวจสอบ .claude/settings.json hook, งานรันอัตโนมัติใน .vscode/tasks.json, .claude/setup.mjs และ .vscode/setup.mjs ในโปรเจกต์ Node.js ภายในเครื่อง
  • ควรถอด kitty-monitor systemd user service และ com.user.kitty-monitor LaunchAgent ออก และตรวจสอบการมีอยู่ของ ~/.local/share/kitty/cat.py, /var/tmp/.gh_update_state, ~/.local/bin/gh-token-monitor.sh
  • ควร pin dependency หรือใช้ lockfile เพื่อไม่ให้การตีความช่วง semver นำไปสู่เวอร์ชันอันตราย
  • ควรตรวจสอบการเปิดเผย Docker socket และการเข้าถึง EC2 metadata ใน CI/CD pipeline และพิจารณาจำกัด hop limit ของ IMDSv2
  • Package Manager Guard (pmg) เป็น open-source install proxy ที่ตรวจสอบแพ็กเกจกับ threat intelligence ก่อนรัน preinstall
  • dependency cooldown สามารถปฏิเสธเวอร์ชันที่เผยแพร่ภายในช่วงเวลาที่กำหนดได้ ช่วยลดคลื่นการเผยแพร่ฉับพลันที่ทำให้ช่วง semver ถูกตีความเป็นรีลีสอันตรายตัวใหม่
  • vet สามารถตรวจจับการอัปเดตแพ็กเกจที่ผิดปกติ เช่น preinstall hook ที่ไม่คาดคิด ขนาดที่เพิ่มขึ้นอย่างรวดเร็ว หรือการเปลี่ยน maintainer ก่อนที่จะไปถึง CI/CD pipeline
  • ขอบเขตผลกระทบที่ครอบคลุม 547 แพ็กเกจภายใต้บัญชีเดียว และมากกว่า 314 แพ็กเกจที่ถูกทำให้เป็นอาวุธในเซสชันเดียว เผยให้เห็นจุดอ่อนเชิงโครงสร้างของโมเดลความเชื่อถือของ npm

เอกสารอ้างอิง

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

 
GN⁺ 1 시간 전
ความคิดเห็นจาก Hacker News
  • ตอนนี้ควรปิดใช้งาน สคริปต์ lifecycle ของ NPM เป็นค่าเริ่มต้น
    มันเท่ากับฝังการรันโค้ดตามอำเภอใจไว้ในนามของความสะดวก และยังมีผลกับทรานซิทีฟดีเพนเดนซีด้วย การโจมตีแบบเวิร์มของ NPM ที่แพร่กระจายเป็นวงกว้างทั้งหมดก็แพร่ผ่านค่าตั้งต้นนี้เอง ไม่ควรให้การเปิดใช้ครั้งเดียวสำหรับคำสั่งใดคำสั่งหนึ่งทำให้ทรานซิทีฟดีเพนเดนซีทั้งหมดสามารถรันสคริปต์ lifecycle ได้ แต่ควรบังคับให้ระบุอย่างชัดเจนทีละดีเพนเดนซีที่จำเป็นจริง ๆ
    แพ็กเกจ NPM ส่วนใหญ่อย่างท่วมท้นไม่ได้พึ่งสคริปต์พวกนี้ ดังนั้นถ้ายังไม่ได้ทำ ก็ควรปิดมันแบบ global ไว้

    • มี RFC สำหรับเรื่องนี้อยู่: https://github.com/npm/rfcs/pull/868
    • หรือจะใช้ pnpm ไปเลยก็ได้
    • เห็นด้วย หรือไม่ก็ต้องรันใน sandbox แทน ถ้าเป็น post-install script ที่รันคำสั่งตามอำเภอใจในบริบทของแพ็กเกจที่ติดตั้งเองก็ยังพอรับได้ แต่การเอาสคริปต์ตามอำเภอใจกับสิทธิ์ของผู้ใช้มารวมกันคือสูตรหายนะ
      อย่างไรก็ตาม แพ็กเกจก็ยังสามารถรันขยะอะไรก็ได้ที่มันต้องการตอนถูก import ครั้งแรกจากโปรแกรม
  • คำว่า “ไม่มีทางป้องกันได้” จะหลุดออกมาจากตัวจัดการแพ็กเกจเพียงตัวเดียวที่เรื่องแบบนี้เกิดขึ้นเป็นประจำเท่านั้น

    • นอกจากที่ NPM เป็นตัวจัดการแพ็กเกจที่ได้รับความนิยมสูงสุดแล้ว มีปัจจัยอะไรอีกไหมที่ทำให้มันเปราะบางต่อการโจมตีแบบนี้เป็นพิเศษ?
  • ถึงจุดหนึ่งแล้ว การปิด Dependabot และตรึงเวอร์ชันแพ็กเกจ NPM ทั้งหมดไว้แม้แต่ระดับ minor/patch อาจดีกว่าการอัปเดตต่อเนื่องหรือเปล่า
    โดยเฉพาะกับแพ็กเกจฝั่งฟรอนต์เอนด์ ทุกวันนี้เหมือนการแก้ความปลอดภัยที่มีความหมายจะพบได้น้อยกว่าการโจมตีห่วงโซ่อุปทานเสียอีก
    มันเป็นสถานการณ์ที่น่าเศร้า แต่ถ้าเปลี่ยนฟรอนต์เอนด์ให้เป็น BOM แบบคงที่ แล้วเชื่อว่าอย่างน้อย NPM จะรักษาข้อจำกัดเรื่อง “ห้ามเผยแพร่เวอร์ชันเดิมซ้ำอีก” ได้ ก็น่าจะไม่มีเหตุผลอะไรไม่ใช่หรือ?

    • แล้วทีมคอมพลายแอนซ์ก็จะบ่นเพราะมี CVE ที่ CVSS 3.1 ซึ่งมีแพตช์แล้วแต่ยังไม่ได้แก้อยู่ตัวหนึ่ง
    • บังคับใช้ ช่วงเวลาบ่ม ได้ เช่น ไม่ให้พูลรีเควสต์ไหนดึงเวอร์ชันที่ใหม่กว่า 30 วันเข้ามา
      อาจยกเว้นให้กับเวอร์ชันที่ใช้แก้ CVE ที่ทราบอยู่แล้วได้
    • ใช่ นั่นเป็นเหตุผลหนึ่งว่าทำไมระบบนิเวศอื่นถึงเห็นการโจมตีห่วงโซ่อุปทานน้อยกว่า
    • NPM ไม่ได้สร้าง lockfile ที่สมบูรณ์ พร้อมแฮชและทรานซิทีฟดีเพนเดนซีที่ถูกตรึงไว้หรือ?
  • สถานการณ์กำลังบ้าคลั่งขึ้นเรื่อย ๆ ส่วนตัวผมลบ node, python, และตัวจัดการแพ็กเกจทั้งหมด ออกจากเครื่องไปแล้ว และใช้เฉพาะใน devcontainer หรือ VM เท่านั้น
    ถึงแม้ชุมชนนักพัฒนาจะสร้างการป้องกันที่แข็งแรงขึ้นได้จริง ผมก็ยังกังวลว่าอีกสักปีหนึ่ง ความสามารถด้าน social engineering ของโมเดลอาจเก่งพอจนเรายังอยู่ในเกมที่แพ้อยู่ดี

    • ถ้าโมเดลเก่ง social engineering มากจริง ๆ ผมก็ยังไม่เห็นว่าทำไมโดยหลักการแล้วผลกระทบมันจะต้องใหญ่มากขนาดนั้น มันมีผลตอบแทนที่ลดลง และคอขวดใหญ่คือเป้าหมายยังเคลื่อนที่ด้วย ความเร็วมนุษย์
      ตัวอย่างเช่น การแฮ็ก XZ ใช้ความพยายามมหาศาล และเร่งไม่ได้เพราะมันอาศัยการค่อย ๆ บั่นทอนผู้ดูแลเดิมตามกาลเวลา ต่อให้คุณสร้างและส่งข้อความอันตรายที่จำเป็นได้ในไม่กี่วินาที ความเร็วของมนุษย์ที่ต้องอ่านมันก็ไม่ได้เพิ่มขึ้น และถ้ามาพร้อมกันหมดก็ยิ่งชวนให้สงสัย
      อีกทั้งยังมีขีดจำกัดว่าข้อมูลนำเข้าจะโน้มน้าวใจได้มากแค่ไหน คุณอาจหยิบข้อความอันตรายแบบสุ่มที่ส่งถึงผู้ดูแล XZ มาสักอันแล้วทำให้มันร้ายกาจขึ้น แม่นยำขึ้น และสะท้อนจุดอ่อนส่วนตัวกับความกลัวของผู้ดูแลได้ดีกว่าเดิม แต่โดยรวมมันจะได้ผลกว่ามากไหม? ผมว่าไม่น่า หรืออย่างมากก็แค่นิดหน่อย
    • คอนเทนเนอร์ช่วยแก้ปัญหานี้ได้อย่างไร? ถ้ามันต่ออินเทอร์เน็ตอยู่ ซึ่งในทางปฏิบัติก็มักจะต่ออยู่ ตราบใดที่คอนเทนเนอร์ยังอ่านข้อมูลรับรองได้ ปัญหาเดิมก็ยังเกิดขึ้นไม่ใช่หรือ
    • ถ้าไม่มี node แล้วจะควบคุมทรัพยากรคลาวด์อย่างไร? Cloudflare ต้องใช้ wrangler และฝั่ง AWS ก็มี CLI ที่เป็น node อยู่เยอะ
  • ตอนนี้ Zed ออก 1.0 แล้ว ผมอยากย้ายไปใช้เต็มตัว แต่เท่าที่รู้ โมเดลความปลอดภัยของมันมีแค่ทั้งหมดหรือไม่เอาเลย คุณต้องยอมให้มันดาวน์โหลดและติดตั้ง แพ็กเกจ NPM ที่คุณไม่รู้จักได้ตามใจ หรือไม่ก็ต้องปิดฟังก์ชัน LSP ทั้งหมด
    แล้วข่าวแบบนี้ก็ยังโผล่มาเรื่อย ๆ

  • npm น่าจะทำระบบที่หน่วงการอัปโหลดแพ็กเกจอัตโนมัติราว 10 นาที แล้วกระจายมันไประบบนิเวศของ บริษัทตรวจสอบโค้ด ฝั่งที่สามเพื่อตรวจอัตโนมัติในช่วงเวลานั้นได้ไหม
    อาจมีตารางอันดับสาธารณะว่าใครตรวจเจอปัญหาได้เร็วและแม่นที่สุด หรือให้รางวัลเป็นเงินก็ได้

  • รายการนี้ยังไม่ครบ อย่างน้อยก็มีอีกหนึ่งแพ็กเกจคือ ส่วนขยาย nx-console สำหรับ VS Code ที่ติดเวิร์มนี้เมื่อวาน และมียอดดาวน์โหลด 2.2 ล้านครั้ง
    ถ้ามีใครที่เกี่ยวข้องและมีเครือข่ายได้อ่านอยู่ ก็น่าจะคุ้มที่จะไล่ตามสายโซ่ดีเพนเดนซีของมันด้วยเพื่อดูว่ายังมีอีกไหม อ้างอิงอยู่ที่นี่:
    https://github.com/nrwl/nx-console/security/advisories/GHSA-...
    PS: ผมโพสต์เรื่องนี้บน HN เพื่อเตือนคนทันทีหลังการติดเชื้อ แต่เสียดายที่แทบไม่ได้โหวตเลย

  • ถ้ามองทั้งระบบนิเวศ TC39 ควรพิจารณาเพิ่ม ไลบรารีมาตรฐาน ที่ดีกว่านี้ให้กับ JS เอง เพื่อจะได้ลดจำนวนแพ็กเกจแบบหนึ่งบรรทัดลง
    เห็นด้วย ตอนที่เคยทำงานกับ Deno สิ่งที่ดีที่สุดอย่างหนึ่งคือไลบรารีมาตรฐาน[0] และสภาพแวดล้อมการพัฒนาที่ค่อนข้างครบในตัว การที่ runtime มีตัวรันทดสอบแบบรวมและไลบรารียืนยันผลมาให้เลยนั้นเป็นเรื่องที่สมเหตุสมผลมาก
    0 - https://docs.deno.com/runtime/reference/std/

    • เพื่อความเป็นธรรม Node เองก็มีโมดูล node:test [0] และ node:assert/strict [1] ให้มาเป็นค่ามาตรฐานมาหลายรุ่น LTS แล้ว node --test ใช้แทน Mocha ได้ค่อนข้างง่าย และ node:assert/strict ก็ใช้ได้ดี แม้บางครั้ง chai จะสะดวกกว่าเพราะเรื่อง ergonomics แบบ expect Deno ใน @std มีไลบรารียืนยันผลสไตล์ expect อยู่
      ปัญหาคือระบบนิเวศของ Node มีตัวรันทดสอบมากเกินไป และหลายตัวก็แทนที่ได้ไม่ง่ายเหมือน Mocha ดังนั้นการย้ายไปใช้ test harness และ assertion library ที่มีมาให้ในตัวจึงน่าจะช้าแบบเจ็บปวดเป็นธรรมดา ผู้คนชอบความซับซ้อนเกินจำเป็นของ Jest และ Vitest ด้วยเหตุผลหลายอย่าง บริษัทใหญ่ ๆ ก็เคยคิดว่า Karma เป็นความคิดที่ดี ผมยังไม่เข้าใจจนทุกวันนี้ว่าทำไมนักพัฒนาถึงไม่ได้รังเกียจแนวคิดประมาณว่า “ชอบ V8 สำหรับ unit test ใช่ไหม งั้นเราจะเปิด V8 อีกสำเนาหนึ่งซ้อนเข้าไปในสภาพแวดล้อม V8 ที่มีอยู่แล้วให้นะ” มากกว่านี้
      [0] https://nodejs.org/api/test.html
      [1] https://nodejs.org/api/assert.html#strict-assertion-mode
    • ผมไม่ค่อยแน่ใจว่าแพ็กเกจไหนในที่นี้จะควรถูกใส่ไว้ใน “ไลบรารีมาตรฐานที่ดีกว่า”
      มีไลบรารีมาตรฐานของภาษาไหนบ้างที่มีตัวแปลงข้อความแบบ “3 ชั่วโมงก่อน” นั่นคือสิ่งที่ timeago.js ทำ
      ส่วน slice.js ก็แค่เพิ่ม negative indexing แบบ Python เท่านั้น TC39 ก็ทำให้ array.at() และ array.slice() รองรับค่าติดลบไปแล้ว
    • ทุกวันนี้ ไลบรารีมาตรฐานของ Node.js เองก็ขยายใหญ่ขึ้นเรื่อย ๆ และก็ควรพูดถึงว่ามันรวมส่วนสนับสนุน assertion และ testing ที่กล่าวมาข้างต้นด้วย
      https://nodejs.org/api/
  • มีรายงานว่าเพย์โหลดจะตรวจหา Docker socket และถ้ามีอยู่ก็จะพยายาม escape ออกจากคอนเทนเนอร์ ด้วย 3 วิธีแบบลำดับขั้น
    ดังนั้นต่อให้รันใน devcontainer หรือ VM เวิร์มแบบนี้ก็กำลังพยายามหนีออกมาอยู่แล้ว
    ควรตรวจให้แน่ใจว่าใช้ rootless VM engine เช่น podman แทน Docker

    • แม้บางคน รวมถึงคนในวงการความปลอดภัย จะพูดต่างออกไป แต่ Docker ไม่ใช่ขอบเขตความปลอดภัยที่แข็งแรง และไม่ควรถูกปฏิบัติแบบนั้น มันแชร์ระบบที่กำลังรันและเคอร์เนลเดียวกัน
      มันทำให้ผมนึกถึงสมัยก่อนที่คนแจกบัญชี Linux สิทธิ์ต่ำแล้วเชื่อว่าเคอร์เนลจะป้องกันการยกระดับสิทธิ์ให้ได้ Docker ก็แทบจะเป็นเรื่องเดียวกันเป๊ะ ๆ แค่เพิ่มขั้นตอนเข้าไป โดยเฉพาะทุกวันนี้ที่ช่องโหว่ยกระดับสิทธิ์เฉพาะที่ของเคอร์เนลใหม่ ๆ ออกมาราวกับทุก 5 นาที
      Podman ดีกว่านิดหน่อยตรงที่ไม่ได้ส่ง root ให้ผู้โจมตี แต่ตั้งแต่แรกจะให้บัญชีไปทำไม? ใช้ VM จริง ๆ ไปเลยดีกว่า
    • ก็แค่อย่าเมานต์ Docker socket เข้าไปในคอนเทนเนอร์
    • คงจะดีมากถ้าเราได้อะไรที่ใกล้เคียงกับ jails หรือ zones มากกว่านี้ หรือดีกว่านั้นคือเอาคอนเทนเนอร์ไปไว้ข้างใน jail หรือ zone อีกชั้น
      บน Linux มี sandbox แบบครอบคลุมอย่างที่ BSD มีไหม?
    • ทำไมไม่ใช้ เครื่องเสมือน แบบจริงจังไปเลยล่ะ?
    • คนส่วนใหญ่ไม่ได้รัน Docker แบบ rootless กันอยู่แล้วหรือ อย่างน้อยบน Linux? หรือ podman ทำได้มากกว่านั้น?
  • ตอนนี้ผมอยากลงจาก Mr Bones' Wild Ride แล้ว แต่ก็กลัวว่าเรื่องแบบนี้จะยังเกิดขึ้นต่อไป จากที่ผมดูมา กลยุทธ์การตรวจจับเชิงพาณิชย์จำนวนมากโฟกัสที่ตอนโหลดหรือใช้งานแพ็กเกจ โดยปรับระดับตาม repository/อุปกรณ์/นักพัฒนา
    มันดูคล้ายกับวิธีที่เรารับมืออีเมลสแปมหรือมัลแวร์ทั่วไป เพราะฉะนั้นจึงแทบจะมีเป้าหมายที่คุ้มให้ผู้ไม่หวังดีพยายามอยู่เสมอ แต่ต่างจากอีเมล ตัวจัดการแพ็กเกจมีผู้มีอำนาจรวมศูนย์ และปัญหาที่อยู่นอกขอบเขตก็มักจะถูกผลักให้เป็นความรับผิดชอบของนักพัฒนาโดยปริยาย
    ในฐานะคนนอกที่รู้น้อย ผมสงสัยว่าเราอาจต้องถอยออกจากวัฒนธรรมการออกรีลีสเร็วและการจัดการเวอร์ชันแบบหลวม ๆ แล้วหันไปเน้นเวอร์ชันที่เสถียรและผ่านการตรวจอย่างลึกซึ้งในรีจิสทรีแทน ด้วยผลของปริมาณและสเกล ผมอาจคิดผิดก็ได้ แต่การที่ภาษาที่เปลี่ยนแปลงมากได้รับผลกระทบบ่อยกว่าก็ยังเป็นสัญญาณที่น่าสนใจ
    ถ้ามีบทความที่ครอบคลุมภูมิทัศน์ปัจจุบันแบบครบถ้วนก็คงดีมาก

    • ผมสงสัยว่า Mr Bones' Wild Ride เป็นการอ้างอิงถึงหนังปี 1991 เรื่อง Nothing But Trouble หรือเปล่า เลยไปค้นดู แล้วพบว่าผมจำผิด
      ชื่อรถไฟเหาะในหนังเรื่องนั้นคือ Mr Bonestripper: https://www.youtube.com/watch?v=NEZEgd8GjJc
      จริง ๆ แล้วมันมาจาก Roller Coaster Tycoon 2: https://knowyourmeme.com/memes/mr-bones-wild-ride
      ถ้าเทียบกับสแปม เราก็เหมือนจะลงเอยด้วยการปล่อยให้อีเมลแอดเดรสถูกดูดไปจากแทบทุกสภาพแวดล้อมเครือข่ายคอมพิวเตอร์เชิงพาณิชย์และสังคม และทำให้ผู้คนยอมรับสแปมพร้อมเคลือบมันด้วยความชอบธรรมบางอย่าง ในพื้นที่นี้ก็น่าจะเกิดสิ่งคล้ายกันได้มาก อาจออกมาในรูปซอฟต์แวร์แบบเอเจนต์ตรวจสอบไลเซนส์ Oracle ผสมกับการจัดการดีเพนเดนซีอัตโนมัติ คือ “แก้” มัลแวร์ห่วงโซ่อุปทานด้วยการใส่มัลแวร์อีกชนิดลงใน allowlist