EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Security / PHP มีช่องโหว่ RCE ระดับวิกฤตสองตัว (php2shell) อัปเดตด่วนวันนี้
Security

PHP มีช่องโหว่ RCE ระดับวิกฤตสองตัว (php2shell) อัปเดตด่วนวันนี้

โดย Ton-Or ·22 ต.ค. 2026·อ่าน 10 นาที
PHP มีช่องโหว่ RCE ระดับวิกฤตสองตัว (php2shell) อัปเดตด่วนวันนี้

เรื่องนี้ไม่ใช่ข่าวที่อ่านแล้วค่อยวางแผนอัปเดตสิ้นเดือน มี exploit ออกมาแล้ว และ request ที่ยังไม่ต้องล็อกอินก็ยิงได้ก่อนโค้ดของแอปจะทำงานเสียอีก ใครมีเครื่องที่รัน PHP อยู่ให้เช็กวันนี้

พอดีเมื่อวานเห็นประกาศของ PHP Foundation ตามด้วยบล็อกของ DigitalOcean ที่ใช้หัวข้อว่า "Patch Now" ผมเลยนั่งไล่เครื่องที่ดูแลอยู่ทั้งหมดก่อน แล้วค่อยมาสรุปให้เพื่อนๆ ที่มีเว็บ PHP อยู่ในมือ เพราะรอบนี้ไม่ใช่ช่องโหว่ที่ต้องมีปลั๊กอินแปลกๆ หรือตั้งค่าผิดก่อนถึงจะโดน มันอยู่ในตัว PHP เอง และกระทบทุกเครื่องที่ใช้ PHP รับ request เว็บ ไม่ว่าจะเป็น PHP-FPM, mod_php ของ Apache, php-cgi, FrankenPHP หรือ LiteSpeed

ชื่อเล่นที่ Red Hat กับ Ubuntu ใช้เรียกในประกาศคือ php2shell ส่วนชื่อทางการคือ CVE-2026-109618 กับ CVE-2026-109619 ทั้งสองตัวได้คะแนน CVSS v4.0 ที่ 9.2 และมีโค้ดพิสูจน์การโจมตี (proof-of-concept) เผยแพร่แล้ว ตัวเลขนี้แปลเป็นภาษาคนทำงานได้ว่า ใครยังไม่อัปเดตก็มีโอกาสถูกยิงได้ตั้งแต่ตอนนี้เลย

9.2CVSS v4.0 ทั้งสองตัว
2 CVERCE ใน PHP เอง
PoC แล้วexploit สาธารณะ
PHP 5 ขึ้นไปรุ่นที่ได้รับผลกระทบ

ช่องโหว่สองตัวนี้คืออะไร

ประกาศของ PHP เขียนไว้สั้นแต่ชัด ผมสรุปเป็นภาษาไทยตามนี้

  • CVE-2026-109618 (RCE ผ่าน OOM corruption) ตอนที่ PHP หน่วยความจำหมด (out of memory) การจัดการ error จะไปขัดจังหวะ PHP ในช่วงที่ข้อมูลภายในของ request ยังถูกสร้างไม่ครบ พอถึงขั้นเก็บกวาดหลังจบ request ตัว PHP กลับไปใช้ข้อมูลที่ยังไม่สมบูรณ์นั้นเหมือนเป็นข้อมูลถูกต้อง
  • CVE-2026-109619 (RCE ผ่าน stale global state) สถานะภายในบางส่วนที่เป็นของ request หนึ่งยังค้างอยู่หลังจากหน่วยความจำของมันถูกคืนไปแล้ว request ถัดไปที่ worker ตัวเดียวกันรับต่อ อาจไปใช้สถานะที่ค้างอยู่นั้นก่อนที่การเริ่มต้นปกติจะล้างมันทิ้ง

จุดที่ผมว่าน่ากลัวที่สุดคือประโยคนี้ในประกาศ: ทีม PHP ถือว่า mod_php เสี่ยงสูงสุด และมี exploit ที่สาธิตแล้วว่า request ที่ไม่ต้องยืนยันตัวตนเลย ทำให้เกิดการโจมตีได้ ก่อนที่โค้ดของแอปจะได้ทำงานด้วยซ้ำ แปลว่าต่อให้แอปเขียนดีแค่ไหน มี WAF ในแอป มีระบบล็อกอินแน่นหนา ก็ไม่ได้ช่วยอะไรเลย เพราะช่องโหว่อยู่ก่อนถึงโค้ดเหล่านั้น

ทำไมแอปเขียนดีแค่ไหนก็ไม่ช่วยrequestไม่ต้องล็อกอินPHP parse requestช่องโหว่อยู่ตรงนี้โค้ดของแอปยังไม่ได้เริ่มทำงานล็อกอิน / WAFในแอปCVE-2026-109618OOM ขัดจังหวะ ข้อมูล request ยังสร้างไม่ครบCVE-2026-109619สถานะค้างข้าม request ใน worker เดิมรันโค้ดบนเครื่องได้ (RCE)
ช่องโหว่อยู่ในขั้นที่ PHP รับ request ก่อนส่งต่อให้โค้ดของแอป ระบบป้องกันที่อยู่ในแอปเลยยังไม่ทันได้ทำงาน

รุ่นไหนปลอดภัย รุ่นไหนต้องเปลี่ยน

ประกาศบอกว่ากระทบทุกรุ่นตั้งแต่ PHP 5 ขึ้นมา รุ่นที่แก้แล้วมีแค่ 4 สายที่ยังได้รับการดูแล

สาย PHP รุ่นที่แก้แล้ว หมายเหตุ
8.5 8.5.12
8.4 8.4.27
8.3 8.3.36
8.2 8.2.35 สาย 8.2 หมดอายุ 31 ธ.ค. 2026
8.1, 8.0, 7.4 ไม่มีรุ่นใหม่ ทีม security ปล่อยแพตช์ source ขั้นต่ำให้ (backport) ต้องคอมไพล์เอง
7.3 ลงไป, 5.x ไม่มีอะไรเลย ต้องย้ายขึ้นรุ่นที่ยังได้รับการดูแล
เลขเวอร์ชันจาก package ของ distro อาจไม่เปลี่ยน

Debian, Ubuntu และ RHEL มักแก้ช่องโหว่โดยนำแพตช์ไปใส่ใน package รุ่นเดิม (backport) php -v เลยอาจยังขึ้นเลขเดิม ต้องเช็กใน changelog ของ package ว่ามีชื่อ CVE ทั้งสองตัวหรือไม่ อย่าสรุปจากเลขเวอร์ชันอย่างเดียว

ขั้นที่ 1 เช็กให้ครบทุกตัวที่มีในเครื่อง

กับดักที่ผมเจอบ่อยที่สุดในเครื่องที่ดูแลหลายเว็บคือ มี PHP มากกว่าหนึ่งรุ่นในเครื่องเดียว แล้วไปเช็กแค่ตัวที่ php บน PATH ชี้ไป ซึ่งมักไม่ใช่ตัวที่เว็บใช้จริง เลยต้องไล่ทุกตัว

# ตัวที่อยู่บน PATH (มักเป็นแค่ CLI)
php -v

# ทุก binary ที่มีในเครื่อง (รวมของ DirectAdmin ที่อยู่ใต้ /usr/local/phpXX)
for p in /usr/bin/php* /usr/local/php*/bin/php; do
  [ -x "$p" ] && echo "$p : $($p -r 'echo PHP_VERSION;')"
done

# PHP-FPM ที่กำลังรันอยู่จริง และรุ่นของมัน
ps -eo pid,args | grep -E 'php-fpm: master' | grep -v grep

ฝั่ง Debian/Ubuntu ให้ดูว่า package ที่ติดตั้งมีแพตช์หรือยังจาก changelog

apt changelog php8.2-fpm 2>/dev/null | grep -E 'CVE-2026-10961[89]' | head

ส่วน RHEL, Rocky, AlmaLinux ใช้คำสั่งนี้

rpm -q --changelog php-fpm | grep -E 'CVE-2026-10961[89]' | head

ถ้าไม่เจอชื่อ CVE ทั้งสองตัว แปลว่ายังไม่ได้แพตช์

ขั้นที่ 2 อัปเดตตามแบบที่เครื่องติดตั้งไว้

  1. Debian / Ubuntu อัปเดต package ของ PHP ทุกตัวแล้วรีสตาร์ต FPM หรือเว็บเซิร์ฟเวอร์
  2. RHEL / Rocky / AlmaLinux / Fedora ตอนที่ประกาศออก แพตช์ของตระกูลนี้ยังทยอยออกอยู่ ให้เช็ก changelog ซ้ำหลังอัปเดต
  3. DirectAdmin อัปเดตรายการเวอร์ชันของ CustomBuild ก่อน แล้วค่อยสั่ง build PHP ใหม่
  4. Docker เปลี่ยน tag ของ base image เป็นรุ่นที่แก้แล้ว build image ใหม่ แล้ว deploy ใหม่ทุก container
  5. Windows ดูหัวข้อถัดไป เพราะแพ็กเกจแบบรวมชุดมักออกช้ากว่าต้นทาง

Debian / Ubuntu:

sudo apt update
sudo apt install --only-upgrade 'php*'
sudo systemctl restart 'php*-fpm' apache2 2>/dev/null

RHEL ตระกูลเดียวกัน:

sudo dnf upgrade 'php*'
sudo systemctl restart php-fpm httpd

DirectAdmin กับ CustomBuild 2 ถ้าตั้ง PHP หลายรุ่นไว้ใน options.conf คำสั่ง ./build php จะ build ให้ครบทุกรุ่นที่ตั้งไว้ ก่อนสั่งให้ดูก่อนว่ารายการเวอร์ชันมีรุ่นที่แก้แล้วหรือยัง

cd /usr/local/directadmin/custombuild
./build update
grep -E '^php(82|83|84|85):' versions.txt     # ต้องเห็น 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12
./build php

Docker ให้เปลี่ยนบรรทัด FROM ใน Dockerfile แล้ว build ใหม่โดยไม่ใช้ cache เก่า

FROM php:8.4.27-fpm
docker build --pull --no-cache -t myapp:php-8.4.27 .
อัปเดตแล้วต้องรีสตาร์ตทุก worker

ช่องโหว่ตัวที่สองเกี่ยวกับสถานะที่ค้างใน worker ตัวเดิม ถ้าแค่อัปเดตไฟล์แต่ไม่รีสตาร์ต PHP-FPM หรือ Apache worker เก่าจะยังรันโค้ดเก่าต่อไป ให้รีสตาร์ตแล้วเช็กด้วย ps ว่า master process เป็นตัวใหม่จริง

ขั้นที่ 3 ของที่ชอบหลุดหลังอัปเดต

ที่มักลืม

ยังมีของเก่าค้างอยู่

  • PHP รุ่นเก่าที่ติดตั้งค้างไว้ในเครื่องเดียวกัน เช่น 5.6, 7.4, 8.1 ของลูกค้าบางเว็บ
  • snapshot หรือ backup ของ VM ที่กู้กลับมาแล้วได้ PHP รุ่นเดิมกลับมาด้วย
  • container image ใน registry ที่ยังไม่ได้ build ใหม่
  • เครื่อง dev หรือ staging ที่เปิดออกอินเทอร์เน็ต
ที่ควรทำ

ปิดให้หมดทุกทาง

  • ย้ายเว็บที่ใช้รุ่นที่ไม่มีแพตช์ไปรุ่นที่แก้แล้ว หรือปิดไม่ให้เข้าจากภายนอก
  • build image ใหม่ให้ครบ แล้วลบ tag เก่าที่ยังมีช่องโหว่
  • ไล่เครื่องที่กู้จาก backup แล้วเช็กเวอร์ชันซ้ำ
  • จำกัดการเข้าถึงเว็บที่ไม่จำเป็นต้องเปิดสาธารณะ

ในเครื่อง shared hosting ที่ผมดูแลเอง หลังสั่ง build ใหม่ PHP 8.2, 8.3 และ 8.4 ขึ้นรุ่นที่แก้แล้วครบ แต่ยังเหลือ 5.6, 7.4 และ 8.1 ที่ติดตั้งค้างไว้ให้เว็บเก่าบางตัว รุ่นพวกนี้ไม่มีรุ่นใหม่ให้ build ต่อ ต้องตัดสินใจทีละเว็บว่าจะย้ายขึ้นรุ่นใหม่หรือปิดไป ไม่มีทางลัด

ระหว่างรอแพตช์ ค่าชั่วคราวที่ PHP แนะนำ

ทีม PHP ย้ำว่า ไม่มีค่าไหนแก้ได้สมบูรณ์นอกจากการอัปเดต ค่าข้างล่างเป็นแค่การลดความเสี่ยงสำหรับเครื่องที่ยังอัปเดตไม่ได้ในทันที และต้องตั้งในระดับที่แอปเปลี่ยนเองไม่ได้

; php.ini หรือ pool ของ FPM / config ของ Apache (ห้ามใช้ ini_set() หรือ .user.ini)
memory_limit = -1
post_max_size = 8M          ; ถ้ามีอัปโหลดไฟล์ ใช้ 32M
upload_max_filesize = 6M    ; ต้องน้อยกว่า post_max_size
session.upload_progress.enabled = 0
; PHP 8.5 เท่านั้น
; fatal_error_backtraces = 0

ถ้าเป็น pool ของ PHP-FPM ให้ใช้ php_admin_value เพื่อไม่ให้แอปเขียนทับได้

php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[session.upload_progress.enabled] = 0
อย่าไปจำกัดหน่วยความจำด้วย RLIMIT

ประกาศเตือนตรงๆ ว่าอย่าใช้ RLIMIT_AS หรือ RLIMIT_DATA แทน เพราะการจองหน่วยความจำไม่สำเร็จคือจุดที่ช่องโหว่ตัวแรกถูกกระตุ้นพอดี ตั้งค่าแล้วต้องรีสตาร์ตทุก worker ด้วยเช่นกัน

ถ้าเว็บอยู่หลัง Cloudflare ทาง Cloudflare ออก WAF rule ฉุกเฉินให้แล้ว เปิดไว้ได้ แต่ให้นับเป็นชั้นเสริม ไม่ใช่เหตุผลให้เลื่อนการอัปเดต

เครื่อง Windows กับ XAMPP

ฝั่ง Windows คนส่วนใหญ่ใช้ชุดรวมอย่าง XAMPP ซึ่งออกรุ่นใหม่ช้ากว่าต้นทางมาก เครื่อง dev ของผมเองตอนนี้ XAMPP ยังเป็น PHP 8.2.12 กับ Apache 2.4.58 อยู่เลย เป็นรุ่นจากปี 2023 รอแพ็กเกจรวมชุดออกใหม่ไม่ได้แน่ในเหตุการณ์แบบนี้

ทางที่ผมเลือกคือ build Apache, PHP 8.4.27 และ MariaDB ขึ้นเองจาก source บน Windows ให้ได้รุ่นที่แก้แล้วตั้งแต่วันที่ต้นทางปล่อย และทำให้รอบหน้าแค่เปลี่ยนเลขเวอร์ชันในไฟล์เดียวแล้วสั่ง build ใหม่ได้ ผมเขียนขั้นตอนแยกเป็นซีรีส์ไว้ทีละตอนให้ทำตามได้ที่ Build Apache + PHP + MariaDB เองบน Windows

ถ้าไม่อยาก build เอง เครื่อง dev ที่ไม่เปิดออกอินเทอร์เน็ตอย่างน้อยให้ผูก Apache ไว้ที่ 127.0.0.1 อย่าเปิดให้เครื่องอื่นในวงเข้าได้จนกว่าจะเปลี่ยน PHP เป็นรุ่นที่แก้แล้ว

Listen 127.0.0.1:80

APM รุ่น 1.0.0 ที่ปล่อยแล้วมี PHP 8.4.27 มาในตัวอยู่แล้ว

ไม่อยาก build เอง

ใครอยากได้ APM ไปใช้เลยโดยไม่ต้องคอมไพล์ ผมปล่อยตัวที่ build เสร็จแล้วไว้ที่ github.com/ChonLaWan/APM-Server โหลด APM-1.0.0-win64.zip (ราว 140 MB) จากหน้า Releases เทียบ sha256 กับไฟล์ .sha256 ที่แนบไว้ แตกไว้ที่ไหนก็ได้ แล้วเปิด manager\APMManager.exe ตัว Manager จะเลือกพอร์ต เขียน config ออก cert และสร้างฐานข้อมูลให้เอง มีปุ่ม Check for updates ที่โหลดรุ่นใหม่จาก Releases มาตรวจ sha256 แล้วแทน binary ให้ โดยไม่แตะ config ฐานข้อมูล และเว็บของเรา

เช็กหลังอัปเดตให้มั่นใจ

# รุ่นที่เว็บใช้จริง (วางไฟล์ชั่วคราว เปิดดู แล้วลบทิ้งทันที)
echo '<?php echo PHP_VERSION, " ", PHP_SAPI;' > /path/to/docroot/_v.php
curl -s https://example.com/_v.php ; echo
rm -f /path/to/docroot/_v.php

ผลที่ต้องได้คือเลขเวอร์ชันของรุ่นที่แก้แล้ว และ SAPI ตรงกับที่เว็บใช้ (เช่น fpm-fcgi) ถ้ายังได้เลขเก่า แปลว่าเว็บนั้นยังวิ่งผ่าน PHP อีกตัวที่ไม่ได้อัปเดต ให้กลับไปไล่ขั้นที่ 1 ใหม่

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

ปล. ใครใช้ PHP รุ่นที่หมดอายุไปแล้วอย่าง 7.4 หรือ 8.1 รอบนี้เป็นจังหวะที่ดีที่จะย้ายขึ้นรุ่นที่ยังได้รับการดูแล เพราะรอบหน้าอาจไม่มีแม้แต่แพตช์ source ให้ใช้

ที่มา: ประกาศของ PHP Foundation (php.net/archive/2026.php) และบล็อกของ DigitalOcean "Critical PHP RCE Vulnerabilities: Patch Now" วันที่ 21 ต.ค. 2026 ถ้ามีรายละเอียดเพิ่มหรือ distro ไหนออกแพตช์ตามมา เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ

อยากให้เราทำให้ไหม?

ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า

คุยเรื่องงานกับเรา

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

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

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย