1 คะแนน โดย GN⁺ 2025-12-19 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • ตรวจพบว่า เซิร์ฟเวอร์ Hetzner ส่วนตัวมีการใช้ CPU ผิดปกติ เมื่อตรวจสอบจึงพบว่า โปรแกรมขุดคริปโต Monero (xmrig) กำลังทำงานอยู่
  • สาเหตุมาจาก คอนเทนเนอร์เครื่องมือวิเคราะห์ Umami ถูกโจมตี โดยมี ช่องโหว่รันโค้ดจากระยะไกลของ Next.js (CVE-2025-66478) รวมอยู่ด้วย
  • โชคดีที่คอนเทนเนอร์ดังกล่าวรันด้วย ผู้ใช้แบบ non-root และ ไม่มีการเมานต์โฮสต์หรือการยกระดับสิทธิ์ ทำให้การบุกรุกถูกจำกัดอยู่ภายในคอนเทนเนอร์
  • ผู้โจมตีอาศัย การ deserialize ที่ไม่ปลอดภัยใน Next.js server components เพื่อรันเพย์โหลดอันตรายและติดตั้งตัวขุด
  • กรณีนี้แสดงให้เห็นถึง ความสำคัญของการจัดการ dependency และการตั้งค่าความปลอดภัยของคอนเทนเนอร์ โดยผู้เขียนได้เสริมมาตรการ เช่น เปิดไฟร์วอลล์ เสริมความปลอดภัย SSH และอัปเดตอย่างสม่ำเสมอ

การถูกแฮ็กและการตอบสนองเบื้องต้น

  • ได้รับ อีเมลแจ้งเตือนจาก Hetzner ว่าเซิร์ฟเวอร์มีการสแกนเครือข่ายภายนอก
    • ระบุด้วยว่าหากไม่ดำเนินการภายใน 4 ชั่วโมง เซิร์ฟเวอร์อาจถูกระงับ
  • เมื่อล็อกอินผ่าน SSH เพื่อตรวจสอบ พบว่าโปรเซสที่พาธ /tmp/.XIN-unix/javae ใช้ CPU ถึง 819% พร้อมกับพบโปรเซส xmrig หลายตัว
  • ยืนยันได้ว่ามี การขุดคริปโตต่อเนื่องราว 10 วัน

การสืบหาเส้นทางการบุกรุก

  • โปรเซสอันตรายทั้งหมดรันด้วย ผู้ใช้ UID 1001 ซึ่งตรงกับ คอนเทนเนอร์ Umami
  • พบ ไฟล์รันตัวขุด ในไดเรกทอรี /app/node_modules/next/dist/server/lib/xmrig-6.24.0/
  • คำสั่งที่ใช้รันมีที่อยู่พูลขุด auto.c3pool.org:443 และคีย์ผู้ใช้รวมอยู่ด้วย

ช่องโหว่ของ Next.js และวิธีโจมตี

  • สาเหตุมาจาก ช่องโหว่การ deserialize ของ React Server Components ใน Next.js (CVE-2025-66478)
    • หากผู้โจมตีส่ง HTTP request ที่ถูกดัดแปลงมา ก็จะสามารถรันโค้ดตามอำเภอใจบนเซิร์ฟเวอร์ได้
    • ส่งผลให้ ติดตั้งและรันตัวขุดคริปโต ได้
  • ผู้เขียนเคยคิดว่า “ตัวเองไม่ได้ใช้ Next.js โดยตรง” แต่ภายหลังจึงทราบว่า Umami สร้างอยู่บน Next.js

การยืนยันการแยกตัวของคอนเทนเนอร์

  • ยืนยันว่า /tmp/.XIN-unix/javae ไม่มีอยู่บนไฟล์ซิสเต็มของโฮสต์
    • สิ่งที่เห็นในผลลัพธ์ docker ps เป็นเพียงลักษณะการแสดงผลของโปรเซสคอนเทนเนอร์ และในความเป็นจริงยังคงถูกแยกออกจากกัน
  • ผลจาก docker inspect
    • ผู้ใช้: nextjs
    • Privileged: false
    • Mounts: ไม่มี
  • ดังนั้นมัลแวร์จึง ไม่สามารถเข้าถึงโฮสต์ ลงทะเบียน cron สร้าง system service หรือฝัง rootkit ได้

การกู้คืนและการเสริมความปลอดภัย

  • หยุดและลบ คอนเทนเนอร์ Umami ที่ติดเชื้อ แล้วการใช้ CPU ก็กลับสู่ปกติ
  • เปิดใช้ไฟร์วอลล์ UFW เพื่ออนุญาตเฉพาะ SSH, HTTP และ HTTPS
  • หลังรายงานผลการตรวจสอบให้ Hetzner ทราบ ทิกเก็ตก็ถูกปิดภายใน 1 ชั่วโมง

บทเรียนและจุดที่ควรปรับปรุง

  • คำว่า “ฉันไม่ได้ใช้ X” ไม่ได้ครอบคลุม dependency ที่ซ่อนอยู่
    • จำเป็นต้องตรวจสอบว่าเครื่องมือที่ใช้อยู่นั้นประกอบขึ้นจากเทคโนโลยีสแตกใดบ้าง
  • เป็นหลักฐานยืนยัน ประสิทธิภาพของการแยกคอนเทนเนอร์
    • การใช้ผู้ใช้ non-root โหมดไม่สิทธิพิเศษ และไม่ใช้วอลุ่มที่ไม่จำเป็น ช่วยป้องกันความเสียหายลุกลาม
  • ความจำเป็นของ การป้องกันหลายชั้น (Defense in Depth)
    • ไฟร์วอลล์, fail2ban, การมอนิเตอร์ และการอัปเดตสม่ำเสมอเป็นสิ่งจำเป็น
  • เน้นย้ำความสำคัญของ การเขียน Dockerfile เอง และ การลดสิทธิ์ของคอนเทนเนอร์ให้น้อยที่สุด

มาตรการหลังเกิดเหตุ

  • นำ Umami เวอร์ชันล่าสุดกลับมาดีพลอยใหม่ และ ตรวจสอบคอนเทนเนอร์ third-party ทั้งหมด
    • ตรวจดูผู้ใช้ที่รัน การเมานต์ ช่วงเวลาอัปเดต และความจำเป็นในการใช้งาน
  • เปลี่ยนไปใช้การยืนยันตัวตนด้วย SSH key, ปิดการล็อกอินด้วยรหัสผ่าน, และ ตั้งค่า fail2ban
  • เสริมการมอนิเตอร์ผ่าน Grafana และ Node Exporter พร้อม ติดตั้งอัปเดตความปลอดภัยทันที

สรุป

  • ช่องโหว่ของ Next.js ใน Umami ทำให้ถูกนำไปใช้ ขุด Monero นาน 10 วัน แต่
    ด้วยการแยกคอนเทนเนอร์และการรันแบบ non-root จึงทำให้ความเสียหายถูกจำกัดไว้
  • จากประสบการณ์ครั้งนี้ ผู้เขียนได้เรียนรู้ถึง ความสำคัญของการเข้าใจ dependency การตั้งค่าความปลอดภัย และการจัดการอัปเดต
  • เหตุการณ์ได้รับการจัดการเสร็จภายในราว 2 ชั่วโมง และกลายเป็น กรณีตัวอย่างที่พิสูจน์ผลลัพธ์จริงของความปลอดภัยคอนเทนเนอร์

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

 
GN⁺ 2025-12-19
ความเห็นจาก Hacker News
  • เมื่อก่อนผมใช้ UFW แต่ตอนนี้แนะนำ firewalld มากกว่า
    UFW พอนานไปจะจัดการยาก แต่ firewalld ใช้การตั้งค่าแบบ XML เลยเสถียรกว่ามาก
    สามารถตั้งค่า SSH, HTTPS, พอร์ต 80 ฯลฯ ได้ด้วยคำสั่ง firewall-cmd และควรใช้แบ็กเอนด์ nftables
    สำหรับ Docker มักมีกรณีที่มันข้ามกฎไฟร์วอลล์แล้วเปิดพอร์ตเอง ดังนั้นตั้งค่า StrictForwardPorts=yes ใน /etc/firewalld/firewalld.conf จะปลอดภัยกว่า

    • ไม่ควรเปิดพอร์ตแบบ 8080:8080 แต่ควร bind กับ private IP แบบ 192.168.0.1:8080:8080
      ผมรัน Docker บน VM ที่ 10.0.10.11 และให้เข้าถึงได้ผ่าน WireGuard เท่านั้น โดยวาง Caddy เป็น reverse proxy ซึ่งสะดวกดี
    • แนะนำให้ติดตั้ง Podman แทน Docker เพราะ Docker มีปัญหาเรื่องการข้ามไฟร์วอลล์บ่อย
    • ไม่ว่าจะใช้ netfilter frontend ตัวไหน ถ้าไม่มี การจำกัดการเชื่อมต่อขาออก ก็แทบไม่มีความหมาย
      มัลแวร์จะพยายามเชื่อมต่อไปยัง mining pool ภายนอกหรือเซิร์ฟเวอร์ C2 ดังนั้นต้องบล็อกไม่ให้ไบนารีที่ไม่ได้รับอนุญาตเข้าถึงเครือข่าย
      การถอดสิทธิ์รันไฟล์ใน /tmp, /var/tmp, /dev/shm ก็มีประโยชน์เช่นกัน
    • Hetzner มี บริการไฟร์วอลล์ภายนอก ให้ใช้ฟรี จึงใช้เป็นแนวป้องกันชั้นแรกได้
    • ส่วนตัวผมรู้สึกว่าแค่ nftables.conf ก็ง่ายและชัดเจนพอแล้ว
      iptables ถูกเลิกใช้ไปแล้ว จึงไม่จำเป็นต้องมีเลเยอร์เพิ่มอย่าง firewalld เสมอไป
  • นี่น่าจะเป็นปัญหาที่เกี่ยวกับ “React2Shell CVE-2025-55182
    เป็นช่องโหว่ที่มีผลกระทบมานานกว่าหนึ่งปีแต่กลับแทบไม่ได้รับความสนใจ ซึ่งแปลกมาก
    ถ้าคุณ deploy เว็บแอปด้วย Next.js ในช่วง 12 เดือนที่ผ่านมา ก็มีโอกาสสูงที่มันจะกลายเป็นส่วนหนึ่งของ botnet ไปแล้ว
    น่าหงุดหงิดที่วงการยังให้คำแนะนำแค่ระดับ “ใช้ Docker”, “เปิดไฟร์วอลล์”
    ช่วงนี้ผมเริ่มหมดศรัทธากับ ecosystem ฝั่งฟรอนต์เอนด์จนกำลังคิดจะย้ายสายอาชีพไปทาง C++

    • รอบการเปลี่ยนเทคโนโลยี ของฟรอนต์เอนด์ตอนนี้เบาลงกว่าเมื่อก่อนมาก
      ตอนนี้ชุด Next.js, React, Tailwind, Postgres แทบจะกลายเป็นมาตรฐานมา 5 ปีแล้ว
      ถ้าเทียบกับยุคปลาย 2000 ถึงต้น 2010 ที่เฟรมเวิร์กผุดขึ้นเต็มไปหมด ตอนนี้ถือว่าเสถียรกว่า
      ถ้าไม่ชอบกระแสและความเปลี่ยนแปลง สายพัฒนา AI เปลี่ยนเร็วกว่าเยอะ
    • ถึงไม่ใช้ JS framework รุ่นใหม่สุด ก็สร้างเว็บแอปได้สบาย
      ฝั่งแบ็กเอนด์ใช้ technology stack ที่มั่นคง อย่าง .NET, Java, Go แล้วเลือกฟรอนต์เอนด์ได้ตามสะดวก
      แบบนี้จะมี CVE น้อยลงและความล้าทางเทคโนโลยีก็ลดลงด้วย
    • อินสแตนซ์ Pangolin ของผมก็โดนช่องโหว่นี้เล่นงานเหมือนกัน
      GitHub discussion ที่เกี่ยวข้อง
    • ผมเองก็ deploy ฟรอนต์ Next.js ไปราว 100 ตัว แต่โชคดีที่ไม่ได้รับผลกระทบเพราะไม่ได้ใช้ Server Components
  • ถ้าจำกัดการใช้ CPU ของคอนเทนเนอร์ Docker ด้วย --cpus="0.5" ก็จะช่วยให้ บริการที่ทำงานผิดปกติหรือ miner ไม่ลากทั้งระบบลงไปด้วย

    • การรันคอนเทนเนอร์ใน โหมด read-only ก็ช่วยลด attack surface ได้อีก
    • แต่ถ้าจำกัด CPU ต่ำเกินไป การบุกรุกอาจไม่เด่นพอจนทำให้ การตรวจจับล่าช้า
    • ต้องรู้ performance profile ของแอปให้ดี เพราะถ้ามี burst traffic ภายหลังอาจโดน throttle โดยไม่ตั้งใจ
    • ควรกำหนด soft/hard limit ของหน่วยความจำไปพร้อมกันด้วย
  • การรันเซิร์ฟเวอร์โดยไม่มีไฟร์วอลล์ถือว่าเป็นตัวเลือกที่ กล้ามาก
    ถ้าใช้ไฟร์วอลล์ภายนอกของ Hetzner ร่วมด้วย เวลาพลาดก็ยังมีชั้นป้องกันเพิ่มอีกชั้น
    ผมอนุญาต SSH แค่จาก IP บ้าน และถ้าต้องใช้จากภายนอกก็จะเปิดชั่วคราวผ่านเว็บไซต์ของ Hetzner

    • ในกรณีส่วนใหญ่ ไฟร์วอลล์ช่วยอะไรได้ไม่มาก
      สิ่งที่สำคัญจริง ๆ คือ อย่ารันซอฟต์แวร์ที่มีช่องโหว่ RCE
      ที่ Docker ดูเหมือนผู้กอบกู้ จริง ๆ ก็แค่โชคดีเท่านั้น
    • ถ้าเป็นบริการเว็บสาธารณะ ไฟร์วอลล์ก็ไม่ค่อยช่วยมาก
      อีกทางคือใช้ bastion host แล้วควบคุมทราฟฟิกเข้าออกผ่าน HTTP proxy แต่การตั้งค่าค่อนข้างซับซ้อน
    • ตลอด 30 ปีที่ดูแล Linux มีครั้งเดียวที่ผมโดนแฮ็ก คือเวลาที่เปิด พอร์ตที่คนรู้จักกันดี
      แค่ใช้พอร์ตที่ไม่มาตรฐานก็ได้ผลอย่างน่าประหลาด
    • การเปิดใช้การยืนยันตัวตนด้วยรหัสผ่านนั้นเสี่ยง ส่วนตัวผมคิดว่า fail2ban ไม่ได้จำเป็นเสมอไป
    • ผมเปลี่ยนพอร์ต SSH แบบสุ่มเพื่อ หลบ port scanner
      ไม่แน่ใจว่ามันได้ผลจริงแค่ไหน แต่ให้ความรู้สึกเหมือนร่มกันฝนทางใจมากกว่า
  • ผมเคยสงสัยว่าถ้ารันคอนเทนเนอร์ Docker ด้วย root มันจะโจมตีถึงโฮสต์ได้ไหม

    • Docker ไม่ใช่ security boundary มันให้แค่การแยกระดับโปรเซส
      ถ้าจะรันโค้ดที่เชื่อถือไม่ได้ ควรใช้ VM (KVM/QEMU) หรือเทคโนโลยีอย่าง gVisor(https://gvisor.dev/), Firecracker(https://firecracker-microvm.github.io/)
      ควรเข้าใจ Docker ว่าเป็นสภาพแวดล้อมการรันที่แยกออกมา ไม่ใช่ sandbox
    • ผู้โจมตียังใช้ CPU ภายในคอนเทนเนอร์เพื่อ ขุด Monero ได้
      การตั้งค่าเริ่มต้นของ Docker ไม่มีการจำกัด RAM, CPU, หรือการใช้ดิสก์ จึงเสี่ยงต่อ การโจมตีแบบ DoS ด้วย
      แถมคู่มือจำนวนมากยังแนะนำตัวเลือกอันตรายอย่าง --privileged หรือ CAP_SYS_PTRACE
    • ใน privileged mode สามารถเข้าถึง root filesystem ของโฮสต์ผ่าน Docker socket ได้
      และยังสตาร์ตคอนเทนเนอร์ใหม่เพื่อยกระดับเป็นสิทธิ์ root ได้ด้วย
    • ต้องเปิดใช้ user namespace ถึงจะมี security boundary ที่แท้จริง
      Docker ยังไม่ได้เปิดเป็นค่าเริ่มต้น ดังนั้นถ้าไม่ตั้งค่าไว้ ความเสี่ยงในการ escape ก็สูง
      การหนีออกจากคอนเทนเนอร์ส่วนใหญ่ในอดีตสามารถป้องกันได้ด้วย user namespace
    • แม้จะมีการ escape จากคอนเทนเนอร์อยู่จริง แต่ส่วนใหญ่เป็นเพียง การโจมตีขุดเหมืองแบบอัตโนมัติ
      ถ้าเป็นเซิร์ฟเวอร์ที่ไม่ได้เก็บข้อมูลอ่อนไหว การจัดการแค่คอนเทนเนอร์ก็มักเพียงพอ
  • ถ้าจะลด attack surface ก็ควร ไม่เปิดเผยบริการที่ไม่จำเป็นต้องเปิดสู่ภายนอก
    ตัวอย่างเช่น เครื่องมือวิเคราะห์ควรให้เข้าถึงได้ผ่าน WireGuard หรือ SSH SOCKS proxy เท่านั้น

  • ผมก็เคยเจอ การติด Monero miner บนเซิร์ฟเวอร์ Hetzner เหมือนกัน
    โชคดีที่เกิดขึ้นแค่ในคอนเทนเนอร์ LXC ของ Incus และเพราะ priority ของ CPU ต่ำเลยไม่ทันสังเกต
    มีการเพิ่ม SSH key เข้าไปในบัญชี root และติดตั้ง remote management agent ไว้
    สุดท้ายผมทิ้งคอนเทนเนอร์นั้นไป แต่ก็ได้บทเรียนบางอย่าง

    • ต้องตั้งค่า การแยกและการจำกัดทรัพยากร โดยสมมติไว้ก่อนว่าสักวันระบบจะถูกเจาะ
    • ถ้าดูแล ZFS snapshot และแบ็กอัปเป็นประจำ การกู้คืนจะง่ายมาก
    • ทางที่ดีที่สุดคือทิ้งระบบที่ถูกเจาะไปเลย แต่ในบางสถานการณ์ก็อาจ rollback แล้ว harden เพิ่มได้
    • โชคดีที่ miner ตั้งค่าไม่ดีเลยสังเกตเห็นได้ แต่ถ้าแอบเข้ามาเงียบ ๆ ก็อาจไม่รู้ตัว
    • เกิดขึ้นในคอนเทนเนอร์ที่ติดตั้ง Umami
  • ในบทความมี เนื้อหาหลอนของ AI ที่ไม่ได้ผ่านการตรวจทานโดยมนุษย์รวมอยู่ด้วย
    มันย้ำหลายครั้งว่าเป็นช่องโหว่ที่เกี่ยวกับ Puppeteer ทั้งที่ไม่จริง

    • ในย่อหน้าแรกของต้นฉบับระบุชัดว่าเป็น บทถอดเสียงของเซสชัน Claude
  • ช่วงนี้มีการติดตั้ง Monero miner โดยอาศัย ช่องโหว่ของ React 19 อยู่หลายแห่ง
    ผมก็เจอปัญหาเดียวกัน

    • มัลแวร์ขุดเหมือง แบบนี้กลับมีประโยชน์ในอีกแง่หนึ่ง เพราะมันสังเกตเห็นง่ายและความเสียหายไม่มาก
      มันทำงานเหมือน bug bounty อัตโนมัติ ช่วยบอกว่ามีช่องโหว่อยู่
      ถ้ามีการมอนิเตอร์เครือข่ายหรือโปรเซสที่ดี ก็ตรวจพบได้ง่าย
    • เซิร์ฟเวอร์ Umami บน Oracle Cloud ของผมติดเชื้อจนต้อง รีเซ็ตเซิร์ฟเวอร์ใหม่
      แต่ก็กลายเป็นโอกาสดีในการอัปเกรดระบบแบ็กอัปไปด้วย
  • พอเห็นกรณีแบบนี้ก็ยิ่งรู้สึกว่าตัดสินใจถูกแล้วที่ไม่ดูแล VPS เอง
    ผมเคยลองมาก่อน แต่รู้สึกว่าตัวเองก็เป็นแค่ ผู้ดูแลระบบระดับทั่วไป จึงรักษาความปลอดภัยให้ดีได้ยาก
    เพราะงั้นตอนนี้ให้ผู้เชี่ยวชาญดูแลและจ่ายเงินไปเลยสบายใจกว่ามาก