# PHP'de İki Kritik RCE: CVE-2026-109618 ve CVE-2026-109619

> PHP 8.5.12, 8.4.27, 8.3.36 ve 8.2.35, kimlik doğrulama gerektirmeyen uzaktan kod çalıştırmaya yol açabilen iki açığı kapatıyor. Açıkların nasıl çalıştığı, kimlerin riskte olduğu, sunucunda nasıl kontrol edeceğin ve yama gelene kadar resmi geçici önlemler.

URL: https://iamcanturk.dev/php2shell-acil-php-guvenlik-yamasi/
Yazar: Can Türk · 21 Ekim 2026 · #php #güvenlik #sunucu #cve

---

21 Ekim 2026'da PHP güvenlik ekibi iki kritik açığı kapatan sürümleri yayınladı: **8.5.12, 8.4.27, 8.3.36 ve 8.2.35**. İki açık da kimlik doğrulama gerektirmeden uzaktan kod çalıştırmaya (RCE) yol açabiliyor ve ikisi de CVSS v4.0 ile **9.2** puan aldı. Duyuru bir gün önce yapılmıştı; ayrıntılar aynı gün sızınca yama planlanan saatten biraz önce çıktı.

Bu yazıda iki açığı resmi bildirilere dayanarak açıklıyorum, kimin ne kadar riskte olduğunu ve yama gelene kadar neyin işe yaradığını topluyorum. Sonunda kendi sunucumda adım adım ne yaptığımı da yazdım. Saldırı tarifi yok; her şey savunma tarafından.

## Kısaca

| | |
|---|---|
| **Açıklar** | [CVE-2026-109618](https://www.cve.org/CVERecord?id=CVE-2026-109618) ([GHSA-w8hw-w9fx-j79w](https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w)): RCE via OOM corruption<br>[CVE-2026-109619](https://www.cve.org/CVERecord?id=CVE-2026-109619) ([GHSA-6wmf-4rv5-p3rp](https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp)): RCE via stale global state |
| **CVSS v4.0** | 9.2 (ikisi de) · `AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N` |
| **CWE** | CWE-416 (use after free), CWE-755, CWE-824, CWE-1325, CWE-1419 |
| **Etkilenen** | 8.2.35, 8.3.36, 8.4.27 ve 8.5.12 öncesi bütün sürümler |
| **Düzeltilen** | **8.2.35 · 8.3.36 · 8.4.27 · 8.5.12** |
| **Kimler riskte** | Web isteği karşılayan bütün PHP kurulumları: PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed. En yüksek risk mod_php'de. |
| **Tek tam çözüm** | Yeni sürüme güncellemek. Geçici önlemler sadece belirli yolları kapatır. |

## Zaman çizelgesi

| Zaman | Ne oldu |
|---|---|
| 20 Ekim | php.net "Upcoming Critical PHP Security Release" duyurusunu yayınladı: iki kritik açık, CVSS v4.0 9.2, yayın 21 Ekim 14:00 UTC. |
| 20 Ekim | Ayrıntılar sızdı. [Sansec](https://sansec.io/research/php2shell) ve [Huntress](https://www.huntress.com/blog/php2shell-leaked-critical-php-vulnerabilities) aynı gün uyarı yayınladı; sızıntı "php2shell" adıyla dolaştı. |
| 21 Ekim 13:34 UTC | İki GitHub Security Advisory yayınlandı. |
| 21 Ekim 13:48 UTC | PHP CNA, iki CVE kaydını yayınladı. |
| 21 Ekim | 8.5.12, 8.4.27, 8.3.36 ve 8.2.35 yayınlandı. Linux dağıtımları ve LTS sağlayıcıları yamaları önceden almıştı. |

## CVE-2026-109618: OOM sırasında yarım kalan istek belleği

PHP bellek yetmediğinde (out-of-memory, OOM) çalışan bağlamdan `longjmp()` ile çıkıyor ve isteğin işlenmesini kesiyor. [Advisory](https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w)'ye göre bazı bağlamlarda istek belleği tam başlatılmadan erişilebilir hale geliyor. Tam o anda herhangi bir bellek ayırma başarısız olursa PHP bu erken çıkışı hesaba katmıyor ve istek belleği geçersiz durumda kalıyor. Başarısız olan ayırmanın o bellekle ilgili olması bile gerekmiyor. Sonra isteği kapatan kod (request shutdown) bu geçersiz veriyi okuyor ya da ona göre dallanıyor.

Saldırganın işi, isteği PHP'nin görünen sınırları içinde kalırken OOM'a sürüklemek. Advisory multipart ayrıştırmada bunu mümkün kılan birkaç yol sayıyor:

- Kullanılabilir bir `Content-Length` olmayan multipart istek ilk `post_max_size` kontrolünü geçip gövde göndermeye devam edebiliyor.
- Bir multipart parçası sınırsız sayıda header taşıyabiliyor. Bu header'ların PHP içindeki karşılığı, kablodaki boyutlarından çok daha fazla bellek tutuyor.
- Boş dosya alanları `max_file_uploads` sınırına sayılmıyor ama yine dosya bilgisi dizileri oluşturuyor. İç içe alan adları bu işi katlıyor.

### SAPI'ye göre fark

Burada hangi SAPI'yi kullandığın belirleyici:

- **Apache mod_php:** İstek başlatma (request startup) başarısız olsa bile shutdown her zaman çalışıyor. Otomatik gövde ayrıştırması bellek sınırını tam o geçersiz anda aşabiliyor ve bütün bu süreç **uygulama kodu çalışmadan önce** oluyor. Tam RCE, varsayılan PHP girdi ve bellek sınırlarıyla mod_php üzerinde HTTP üzerinden doğrulanmış.
- **PHP-FPM:** Startup başarısız olunca FPM child sürecini kapatıyor; geçersiz durum kullanılmadan süreç havuzdan çıkıyor. Ama normal startup'tan sonra da OOM'a ulaşılabiliyor. Kaynak kod analizi FPM'in de etkilendiğini gösteriyor, sadece FPM için tam bir exploit gösterilmemiş.
- **FrankenPHP:** mod_php gibi shutdown'ı koşulsuz çağırıyor, bu yüzden uygulamadan bağımsız saldırılara muhtemelen açık.
- **LSAPI:** FPM'e benziyor; uygulamaya özel exploit'ler muhtemel ama doğrulanmamış.

Yama `longjmp()` ile yapılan OOM çıkışını kaldırıyor. Yerine bir sonraki güvenli noktada devreye giren yumuşak bir OOM ve bellek ikinci, daha yüksek bir eşiğe ulaşırsa `_exit()` ile anında sonlanan sert bir OOM geliyor.

## CVE-2026-109619: İstekten isteğe taşınan bayat durum

PHP'nin bir worker süreci birden çok isteği sırayla karşılıyor. Bu sırada hata, session, nesne ve extension gibi bazı global durumlar worker'da kalıyor ama bir isteğin belleğini gösteriyor. [Advisory](https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp)'ye göre bu durum, istek belleği bırakılmadan önce sıfırlanmalı. Sıfırlanmazsa bir sonraki istekte normal başlatmadan önce çalışan bazı işlemler serbest bırakılmış belleğe erişiyor: klasik bir use after free.

Açığın çalışması için iki parça gerekiyor:

1. **Üretici (producer):** İsteğe ait durumu global bir yere yazan ve fatal error ya da OOM yüzünden sıfırlamayı atlayan bir işlem. Parse hataları, yakalanmamış hatalar, başarısız `require`, `Maximum execution time exceeded` bu kesintiye örnek.
2. **Tüketici (consumer):** Aynı worker'ın sonraki isteğinde, normal başlatmadan önce bu bayat değeri okuyan bir işlem.

Advisory iki tüketiciyi doğruluyor:

- **İstek başlamadan önce gelen INI uyarıları.** Geçersiz ya da kullanımdan kalkmış bir ayar başlatma sırasında uyarı üretirse bayat hata durumu kullanılıyor. Uyarının gösterilmesi ya da loglanması gerekmiyor. `error_reporting`, `@` ya da `display_errors` bu yüzden koruma sağlamıyor.
- **Session upload progress.** Upload progress, session'ı normal başlatmadan önce işliyor ve kesintiye uğramış bir isteğin bıraktığı session durumunu kullanabiliyor.

Doğrulanmış iki somut durum var:

- **PHP 8.5'te `fatal_error_backtraces`:** Açıkken fatal error raporlaması isteğe ait bir backtrace saklıyor ve shutdown bunu hiç sıfırlamıyor. Doğrulanmış mod_php exploit'i bunun üzerine kurulu.
- **Session upload progress yaşam döngüsü:** Mod_php'de üretici ve fatal error multipart startup sırasında oluşabiliyor. FPM'de doğrulanmış yol farklı: durum uygulama çalışırken yazılıyor, fatal error geliyor ve upload progress erken tüketici oluyor. Normal dinamik bir FPM havuzunda çalışıyor, ama uygun bir uygulama işlemine bağlı.

### FPM'de INI yolu ne zaman açık?

Bu kısım FPM kullananlar için önemli. Advisory'ye göre `php.ini` ve FPM pool değerleri worker başlarken uygulanıyor; `.user.ini` ise normal başlatmadan sonra. Bunlar erken tüketici sağlamıyor. Ama nginx ya da Apache `mod_proxy_fcgi`, FPM'e `PHP_VALUE` / `PHP_ADMIN_VALUE` parametresi gönderiyorsa ve bu değerler başlatmadan önce bir uyarı üretiyorsa FPM de bu yoldan açık.

CloudPanel gibi paneller her siteye `fastcgi_param PHP_VALUE` ile `memory_limit`, `post_max_size`, `date.timezone` gibi değerler gönderiyor. Bu değerler geçerliyse uyarı oluşmuyor. Kullanımdan kalkmış ya da geçersiz bir değer varsa risk büyüyor.

## Kimler riskte?

- **En yüksek risk:** Apache mod_php ve FrankenPHP. Saldırı uygulama kodundan önce ve uygulamadan bağımsız çalışabiliyor.
- **Orta risk:** PHP-FPM ve LSAPI. Doğrulanmış yollar var, ama çoğu uygulamaya ya da yapılandırmaya bağlı. Bu, güncellemeyi erteleme sebebi değil.
- **8.1 ve öncesi:** PHP projesi artık bu sürümlere güncelleme yayınlamıyor. Bu kez istisna olarak 7.4, 8.0 ve 8.1 için kaynaktan derleyenlere küçük backport'lar verildi ([PHP-7.4](https://github.com/php/php-src/tree/PHP-7.4-CVE-2026-109618-109619), [PHP-8.0](https://github.com/php/php-src/tree/PHP-8.0-CVE-2026-109618-109619), [PHP-8.1](https://github.com/php/php-src/tree/PHP-8.1-CVE-2026-109618-109619) dalları). Dağıtım paketi kullanıyorsan sağlayıcına sor ya da desteklenen bir sürüme geç.
- **8.2:** 31 Aralık 2026'da destek dışı kalıyor. Bu yamayı kur, sonra yükseltmeyi planla.

Etki, web worker'ının hesabı ile kod çalıştırma. Yani saldırgan, PHP'nin çalıştığı kullanıcının yetkileriyle sunucunda komut çalıştırabilir.

## Sunucunda nasıl kontrol edersin?

```bash
# Kurulu sürümler ve paket sürümleri (Debian/Ubuntu)
ls /etc/php/
php -v
dpkg -l | grep -E '^ii  php[0-9.]+-(fpm|cli|common) '

# Gerçekten çalışan FPM servisleri
systemctl list-units --type=service 'php*-fpm*'

# Apache mod_php var mı? (en yüksek risk)
apache2ctl -M 2>/dev/null | grep -i php

# Web sunucusu FPM'e PHP_VALUE gönderiyor mu?
grep -rn "PHP_VALUE\|PHP_ADMIN_VALUE" /etc/nginx/ /etc/apache2/ 2>/dev/null

# Depoda yeni sürüm var mı?
apt-get update && apt-cache policy php8.4-fpm
```

CLI ile FPM'in ayarları farklı olabilir. FPM'in gerçekte ne kullandığını görmek için `php-fpm8.4 -i` kullan.

## Güncelleme

Advisory'ye göre Linux dağıtımları ve LTS sağlayıcıları yamaları önceden aldı, yani dağıtım paketleri hızlı gelmeli. Bildiride iki not daha var: gönüllülerin yönettiği **rpms.remirepo.net deposu bugün güncellenmeyecek**, bu depoyu kullanıyorsan dağıtım paketlerine geç ya da kaynaktan derle. Windows ikili dosyaları php.net indirme sayfasında.

```bash
# Önce sitelerinin durumunu kaydet
for d in site1.com site2.com; do echo "$d $(curl -s -o /dev/null -w '%{http_code}' https://$d/)"; done

# Sadece kurulu PHP paketlerini güncelle, ayar dosyalarını koru
sudo apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install --only-upgrade -y \
  -o Dpkg::Options::=--force-confold $(dpkg -l | awk '/^ii  php/{print $2}')

# Doğrula
php8.4 -v
sudo php-fpm8.4 -t && systemctl is-active php8.4-fpm
```

**Docker:** Resmi `php` imajlarında etiketi yeni sürüme çek (`php:8.4.27-fpm` gibi) ve konteynerleri yeniden oluştur. `8.4-fpm` gibi kayan etiketler ancak `docker pull` sonrası güncellenir.

**Paylaşımlı hosting:** Erişimin yoksa firmana bugün yaz: hangi sürümde olduğunu ve yamanın ne zaman kurulacağını sor.

## Güncelleyemiyorsan: resmi geçici önlemler

[php.net bildirisi](https://www.php.net/archive/2026.php#2026-10-21-5) açık: bunlar sadece belirli saldırı yollarını kapatır, kurulumu güvenli yapmaz.

**CVE-2026-109618 (OOM) için:**

- `memory_limit=-1` ayarla ve uygulamanın değiştiremeyeceği bir yerde zorla, örneğin FPM pool'da `php_admin_value`. Hiçbir pool, dizin ya da uygulama ayarının sınırı geri getirmediğini kontrol et. Bu, kontrol edilebilir OOM'u kaldırıyor ama alttaki bir `mmap()` hatası yine aynı yola gidebilir. Ayrıca sınırsız bellek, paylaşımlı bir sunucuda kendi başına bir denial of service riski demek; bunu bilerek yap.
- `post_max_size`'ı düşük tut: dosya yüklemeyen uygulamalar için 8M, yükleme gerekiyorsa 32M. `upload_max_filesize` her zaman bunun altında kalsın.
- Web sunucusunda ya da proxy'de çözülmüş gövde boyutu, alan sayısı, multipart parça sayısı, header sayısı ve iç içe geçme derinliği için sınır koy. Bu sınırlar `Content-Length` olmayan istekleri de kapsamalı. Uygulaman multipart form kullanmıyorsa bu istekleri PHP'ye ulaşmadan engelle.
- Konteyner ya da cgroup bellek sınırı bir emniyet çizgisi olabilir. `RLIMIT_AS` ya da `RLIMIT_DATA` **kullanma**: bunlar tam da açığın tetiklendiği ayırma hatasını üretir.

**CVE-2026-109619 (bayat durum) için:**

- `session.upload_progress.enabled=0` ayarla ve **bütün worker'ları yeniden başlat**. Ayarı php.ini'ye, FPM pool'a ya da istek ayrıştırılmadan önce uygulanan sunucu yapılandırmasına yaz. `ini_set()` ya da FPM `.user.ini` işe yaramaz. Bu, doğrulanmış session yollarını kapatır, diğer varyantları kapatmaz.
- PHP 8.5'te `fatal_error_backtraces=0` ayarla ve bütün worker'ları yeniden başlat. Bu sadece doğrulanmış backtrace yolunu kapatır.
- İstek başına uygulanan geçersiz ya da kullanımdan kalkmış ayarları kaldır (`.htaccess`, Apache `php_value`, nginx `PHP_VALUE`).
- Saldırganın tetikleyebildiği fatal error ve zaman aşımlarını düzelt: callback'ler, yükleme ve içe aktarma işleri, cleanup.

```ini
; /etc/php/8.5/fpm/conf.d/99-gecici.ini
session.upload_progress.enabled = 0
fatal_error_backtraces = 0   ; sadece PHP 8.5
```

```bash
sudo php-fpm8.5 -t && sudo systemctl reload php8.5-fpm
php-fpm8.5 -i | grep -E '^(session.upload_progress.enabled|fatal_error_backtraces)'
```

### Cloudflare kuralı işe yarar mı?

Sızıntıdan sonra paylaşılan bir Cloudflare WAF kuralı, gövdesinde `PHP_SESSION_UPLOAD_PROGRESS` alanı geçen istekleri engelliyor:

```
any(http.request.body.form.names[*] == "PHP_SESSION_UPLOAD_PROGRESS")
or http.request.body.raw contains "PHP_SESSION_UPLOAD_PROGRESS"
```

Action: **Block**. Ama bu kural sadece upload progress tüketicisini hedefliyor; OOM açığını ve INI yolunu hiç kapsamıyor. İki sınırı daha var:

- Cloudflare gövdenin sadece ilk kısmına bakıyor. Alan daha geride gelirse kural görmüyor.
- Sunucuna IP adresiyle doğrudan ulaşılabiliyorsa saldırgan Cloudflare'den hiç geçmiyor.

İkincisini kendi sunucumda denedim ve kendi sitem dahil bütün siteler doğrudan IP'yle açılıyordu. İlk denemede ana sitem kapalı görünmüştü, çünkü sunucuda self-signed sertifika var ve `curl` bağlantıyı reddetti. `-k` ile sertifika kontrolünü atlayınca 200 döndü. Saldırgan da sertifikaya bakmaz:

```bash
curl -sk -o /dev/null -w "%{http_code}\n" --resolve siten.com:443:SUNUCU_IP https://siten.com/
```

200 dönüyorsa origin'in açık. Çözüm: sunucuda sadece [Cloudflare IP aralıklarını](https://www.cloudflare.com/ips/) kabul et ya da Authenticated Origin Pulls kullan.

## Kendi sunucumda ne yaptım?

Sunucum Ubuntu 26.04 üzerinde CloudPanel. Bütün siteler nginx arkasında PHP-FPM ile çalışıyor; Apache ya da mod_php yok.

| Saat (TSİ) | Adım |
|---|---|
| 11:26 | Envanter: 7.1'den 8.5'e on sürüm kurulu, ama sadece 8.4 ve 8.5'in FPM servisi çalışıyor. Sürümler 8.4.22 ve 8.5.9. |
| 11:26 | Asıl sürpriz: 8.4'te acil yama gelmeden önce bile üç güvenlik sürümü gerideydim (8.4.23, 8.4.24, 8.4.26). |
| 11:59 | Bütün sitelerin HTTP durumunu kaydettim, sonra eldeki son sürümleri kurdum: 8.4.26 ve 8.5.11 (188 paket, yarım saatten uzun sürdü). Siteler aynı cevabı verdi. |
| 12:01 | 8.4 ve 8.5'te `session.upload_progress.enabled=0`, FPM yeniden yüklendi. |
| 16:48 | Advisory'leri okudum. nginx'in her siteye `PHP_VALUE` gönderdiğini gördüm; değerlerin hepsi geçerli, `open_basedir` yok, yani INI uyarısı üretmiyorlar. |
| 16:48 | 8.5'te `fatal_error_backtraces` açıktı. Kapattım, FPM'i yeniden yükledim, siteleri tekrar kontrol ettim. |
| 17:29 | 8.5.12 CloudPanel deposuna düştü ve kuruldu. FPM yeniden başladı, geçici önlemler açık kaldı, bütün siteler aynı cevabı verdi. |
| bekliyor | 8.4.27 depoda henüz yok. Düştüğü anda kuracağım ve bu satırı güncelleyeceğim. |

`memory_limit=-1` önlemini uygulamadım. Sunucuda birden çok site var ve sınırsız bellek tek bir kötü isteğin bütün sunucuyu yavaşlatmasına yol açabilir. FPM için tam exploit gösterilmemiş olması ve paketin yakında gelecek olması bu kararı verdirdi. mod_php kullansaydım farklı düşünürdüm.

Bunu kısa bir video olarak da anlattım: [Pixel Shorts · PHP'ye acil yama](/shorts/php-acil-yama/).

## Teşekkür

Advisory'lere göre açıkları lcfr-hax, sagitzadik-wiz, tomash-areion ve amyb-oai bildirdi. Düzeltmeleri arnaud-lb, iluuu1994, bukka ve ndossche geliştirdi.

## Kaynaklar

- [php.net: Security advisory, critical remote code execution vulnerabilities (21 Ekim 2026)](https://www.php.net/archive/2026.php#2026-10-21-5)
- [GHSA-w8hw-w9fx-j79w: RCE via OOM corruption (CVE-2026-109618)](https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w)
- [GHSA-6wmf-4rv5-p3rp: RCE via stale global state (CVE-2026-109619)](https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp)
- [CVE-2026-109618 kaydı](https://www.cve.org/CVERecord?id=CVE-2026-109618) · [CVE-2026-109619 kaydı](https://www.cve.org/CVERecord?id=CVE-2026-109619)
- [PHP 8 ChangeLog](https://www.php.net/ChangeLog-8.php)
- [php.net: Upcoming Critical PHP Security Release (20 Ekim 2026)](https://www.php.net/archive/2026.php)
- [Sansec: php2shell](https://sansec.io/research/php2shell)
- [Huntress: php2shell, leaked critical PHP vulnerabilities](https://www.huntress.com/blog/php2shell-leaked-critical-php-vulnerabilities)
- [Cloudflare Rules language: http.request.body.form.names](https://developers.cloudflare.com/ruleset-engine/rules-language/fields/reference/http.request.body.form.names/)

---

*Bu yazıyı Claude Code ile birlikte hazırladım.*
