"Sadece Resim Siliyor" Dediğimiz Dosyanın Gerçek Hikayesi
16 Satırlık Kod 23 Farklı Şirketi Nasıl Risk Altına Aldı?
"Sadece Resim Siliyor" Dediğimiz Dosyanın Gerçek Hikayesi
Bir web ajansının hazır yazılımında bulduğum zafiyet, 23 farklı şirketin web sitesini aynı anda tehdit ediyordu. Ve hiçbiri bundan haberi yoktu.
YASAL UYARI: Bu hikaye gerçek bir olaya dayanmaktadır ancak gizlilik nedeniyle isimler, şirket adı ve hassas detaylar değiştirilmiştir. Anlatılan penetration test yazılı izinle gerçekleştirilmiştir. İzinsiz test yasa dışıdır.
Prolog: Bir Telefon Görüşmesi
Salı, 10:23
Telefonu açtığımda Muhammet Bey'in sesinde paniği duyabiliyordum.
"Zafer Bey, site çöktü. Tamamen. Hiçbir şey çalışmıyor."
Muhammet Bey, orta ölçekli bir mobilya e-ticaret sitesinin sahibiydi. Geçen hafta onlara penetration test teklifi sunmuştum. "Düşüneceğiz" demişti.
"Ne oldu?" diye sordum.
"Bilmiyorum. Sabah kalktım, site açılmıyor. Admin paneline girmeye çalıştım, o da yok. Hiçbir şey yok!"
"Hosting firmasını aradınız mı?"
"Aradım. 'Sunucu çalışıyor, problem sizde' dediler."
Derin bir nefes aldım. "Dosyalara bakabilir miyim?"
"Elbette! Lütfen! Her şeyimiz o sitede!"
Flashback: Bir Hafta Öncesi - İlk Keşif
Önceki Salı, 14:00
Muhammet Bey'in ofisinde oturuyorduk. Masada kahvelerimiz, önümüzde projeksiyonda benim hazırladığım penetration test teklifi.
"Zafer Bey, kusura bakmayın ama," dedi, "sitede zaten güvenlik var. NBSP MEDYA yaptı, profesyonel bir ajans. SSL var, şifre korumalı admin paneli var. Bunlar yetmiyor mu?"
Klasik. Her müşteriden duyduğum cümle.
"Muhammet Bey, izninizle sitenize bakalım mı? Sadece yüzeysel, 15 dakika?"
"Tabii, buyurun."
Laptopumu açtım. Muhammet Bey'in sitesini açtım ve kaynak kodu görüntüledim (Ctrl+U).
HTML içinde bir şey dikkatimi çekti:
<!-- NBSP MEDYA Web Solutions - www.nbspmedya.com -->
<!-- Template Version: 3.2.1 -->
"NBSP MEDYA..." diye mırıldandım. Tanıdık geldi. Daha önce başka bir müşteride de görmüştüm bu imzayı.
"Muhammet Bey, sitenizi kim yaptı?"
"NBSP MEDYA. İyi bir ajans, 50'den fazla site yapmışlar."
50'den fazla site. Ve hepsi muhtemelen aynı template.
"İzninizle admin paneline bakabilir miyim?"
"Tabii." Muhammet Bey giriş yaptı ve ekranını bana gösterdi.
Admin panelinin URL'sine baktım:
https://techlojistik-demo.com/panel/
"Muhammet Bey, ben paneli biraz inceleyebilir miyim? Kaynak koduna bakmak istiyorum."
"Buyurun."
Tarayıcıda sağ tıklayıp "Inspect Element" yaptım. HTML yapısına baktım. JavaScript dosyalarını inceledim.
Sonra bir şey denedim: Admin panelinin kaynak kodunu görüntüledim.
Ve boom.
Footer'da aynı yorum satırı:
<!-- NBSP MEDYA Panel v3.2.1 -->
<!-- resimkaldir.php, urunekle.php, kategori.php -->
Dosya isimleri açıkta. Bir liste gibi.
"Muhammet Bey, bir dakika..."
Tarayıcıya şunu yazdım:
https://techlojistik-demo.com/panel/resimkaldir.php
Normalde böyle bir dosyayı açtığınızda ya çalışır, ya hata verir, ya da erişim engellenir.
Ama bu sefer farklı bir şey oldu.
Ekranıma PHP kaynak kodu döküldü.
<?php
$yol=$_POST['yol'];
if (!file_exists($yol)) {
echo 'Böyle Bir Dosya Bulunmamaktadır';
} else {
$sonuc=unlink($yol);
if ($sonuc) {
echo 'Dosya Silindi';
} else {
echo 'Dosya Silinemedi';
}
}
?>
16 satır.
Hiçbir güvenlik kontrolü yok.
Muhammet Bey ekrana baktı. "Bu ne?"
"Bu," dedim, "sitenizin dosya silme scripti. Admin panelinde ürün resimleri yüklersiniz, sonra istemediğinizi bu dosya ile silersiniz, değil mi?"
"Evet, tam olarak. Sorun ne?"
"Sorun şu Muhammet Bey: Bu dosyada hiçbir güvenlik kontrolü yok."
Discovery: Daha Büyük Bir Sorun
Aklımdan bir şey geçti. "50'den fazla site" demişti. NBSP MEDYA'nın portfolyosunu buldum. Web sitelerinde referanslar bölümü vardı.
18 tane aktif müşteri görüyordum:
- TeknoMakina (endüstriyel ekipman)
- Verde Natural (organik ürünler)
- MarineSupply Global (denizcilik)
- Lezzet Durağı (restaurant)
- GlobalGümrük (gümrük hizmetleri)
- EcoSolutions (çevre teknolojileri)
- SmileCare Diş (diş kliniği)
- Megadrive Sürücü Kursu (eğitim)
- HairPlus Klinik (estetik)
- YapıMühendislik (inşaat)
- TechStore (elektronik)
- VintageMarket (e-ticaret)
- SesTeknoloji (işitme cihazları)
- DekorMetal (metal işleme)
- BeautySkin (kozmetik)
- ArtStudio (sanat atölyesi)
- TexFabric (tekstil)
- ShopCenter (e-ticaret)
...ve devamı.
Hepsinin URL'lerini not aldım.
Sonra aynı testi her birinde yaptım.
Test: Kaynak Kod Kontrolü
Her sitenin kaynak kodunu açtım (Ctrl+U):
<!-- NBSP MEDYA Web Solutions -->
<!-- Template Version: 3.2.1 -->
Aynı template.
Test: Admin Panel Erişimi
Her sitede /panel/resimkaldir.php dosyasını kontrol ettim:
https://site1.com/panel/resimkaldir.php
https://site2.com/panel/resimkaldir.php
https://site3.com/panel/resimkaldir.php
Sonuç:
18 sitenin 18'inde de aynı kod. 18 sitenin 18'inde de aynı zafiyet.
Hiçbir güvenlik kontrolü. Hiçbir kimlik doğrulama. Hiçbir yetkilendirme.
Muhammet Bey'e döndüm. Yüzüm ciddi olmuştu.
"Muhammet Bey, problem sadece sizde değil."
"Ne demek?"
"NBSP MEDYA'nın yaptığı 18 site inceledim. Hepsinde aynı zafiyet var. Bu bir template zafiyeti. Yani bir kere yazılmış, 18 kere kopyalanmış."
"Yani..."
"Yani bir saldırgan bu zafiyeti öğrenirse, sadece sizin sitenizi değil, 18 farklı şirketin sitesini birden hackleyebilir."
Muhammet Bey'in yüzü bembeyaz oldu.
Technical Deep Dive: Zafiyet Analizi
"Muhammet Bey, şimdi size bu kodun ne kadar tehlikeli olduğunu göstereyim."
Ekran paylaşımı yaptım. Kodu satır satır inceledik.
<?php
$yol=$_POST['yol'];
"Bu satır ne yapıyor?" diye sordu.
"Bu satır, kullanıcıdan gelen dosya yolunu alıyor. $_POST['yol'] demek, internet üzerinden gönderilen 'yol' parametresini oku demek."
"Tamam."
"Ama burda sorun şu: Bu 'yol' değeri hiç kontrol edilmiyor. Kullanıcı ne gönderirse o kabul ediliyor."
if (!file_exists($yol)) {
echo 'Böyle Bir Dosya Bulunmamaktadır';
"Bu satır iyi aslında," dedim. "Dosya var mı diye kontrol ediyor. Ama yeterli değil."
} else {
$sonuc=unlink($yol);
"İşte kritik nokta burası. unlink() fonksiyonu dosyayı siler. Kalıcı olarak. Geri getirilemez."
"Ve hiçbir şey kontrol etmiyor," dedi Muhammet Bey. Anlamaya başlamıştı.
"Tam olarak. Şimdi eksik olanları sayalım."
Eksik Güvenlik Katmanları
1. Kimlik Doğrulama (Authentication)
→ Kullanıcı giriş yapmış mı?
2. Yetkilendirme (Authorization)
→ Kullanıcı admin mi?
3. Input Validation
→ Gelen yol geçerli mi?
4. Path Sanitization
→ "../" gibi tehlikeli karakterler var mı?
5. Whitelist Kontrolü
→ Sadece uploads/ klasöründeki dosyalar mı?
6. CSRF Token
→ İstek gerçekten bizim formumuzdan mı geliyor?
7. Rate Limiting
→ Aynı kişi 5000 kere istek yapabilir mi?
8. Logging
→ Kim, ne zaman, hangi dosyayı sildi?
"8 tane güvenlik katmanı. Hiçbiri yok," dedim.
Saldırı Simülasyonu
"Muhammet Bey, izninizle bir test yapalım. Size göstereyim nasıl çalışıyor."
Terminal açtım. Basit bir cURL komutu yazdım:
curl -X POST https://techlojistik-demo.com/panel/resimkaldir.php \
-d "yol=../test.txt"
"Şimdi size bir şey göstereceğim. İzninizle sitenizde test.txt adında bir dosya oluşturalım."
FTP'den hızlıca bir test dosyası oluşturdum.
"Dosya oluştu. Şimdi bu komutu çalıştırıyorum."
Enter'a bastım.
Ekranda: Dosya Silindi
FTP'yi yeniledim. test.txt gitmişti.
Muhammet Bey'in yüzündeki ifade değişti. "Siz... siz şimdi dosyayı sildiniz? Ama giriş yapmadınız, admin değilsiniz..."
"Tam olarak."
"Peki bu test dosyasıydı. Önemli bir dosyayı silemezsiniz değil mi?"
Başka bir komut yazdım:
curl -X POST https://techlojistik-demo.com/panel/resimkaldir.php \
-d "yol=../index.php"
"Muhammet Bey, bu komutu çalıştırsam, sitenizin ana sayfası silinir. Müşterileriniz siteyi açamaz. 404 hatası alırlar."
Yüzü bembeyaz olmuştu.
"Ama yapmayacağım," diye devam ettim hızlıca. "Çünkü bu yasal değil. Ben sadece gösteriyorum. Ama kötü niyetli biri? Yapabilir."
Sessizce oturdu.
"Ve unutmayın Muhammet Bey: Bu sadece sizin siteniz değil. 18 farklı şirket, aynı kod, aynı zafiyet."
The Scale: 23 Site, Aynı Problem
O akşam ofise döndüğümde, NBSP MEDYA'nın yaptığı tüm siteleri listeledim.
Portfolyolarında 18 site vardı. Ama araştırmayı derinleştirdiğimde (aynı footer signature, aynı CSS pattern'leri, aynı JavaScript dosya isimleri) 5 site daha buldum.
Toplam: 23 site.
Her birinde aynı template. Her birinde aynı resimkaldir.php. Her birinde aynı zafiyet.
Sektör Dağılımı:
• E-ticaret: 7 site
• Kurumsal: 5 site
• Restaurant/Cafe: 3 site
• Sağlık: 2 site
• Eğitim: 2 site
• Lojistik: 2 site
• Diğer: 2 site
Risk altındaki şirketler:
- TeknoMakina (endüstriyel ekipman)
- Verde Natural (organik ürünler)
- MarineSupply Global (denizcilik)
- HairPlus Klinik (estetik)
- SesTeknoloji (işitme cihazları)
- BeautySkin (kozmetik) ...ve 17 tane daha.
Toplam potansiyel hasar:
- 23 site × ~80,000 TL (ortalama) = ~1,840,000 TL
Bir saldırgan, tek bir zafiyet bilgisiyle, 1.8 milyon TL'lik ekonomik zarara sebep olabilirdi.
Present: Kriz Anı
Salı, 11:15
Telefonda Muhammet Bey'e geri döndüm.
"Muhammet Bey, o resimkaldir.php dosyasını NBSP MEDYA'ya söylediniz mi? Düzeltmelerini istediniz mi?"
"Ya... ben söylemedim. Siz söyleyin demişsiniz sanıyordum..."
İçimden ağır bir 'of' çektim.
"Muhammet Bey, saldırgan tam olarak o dosyayı kullanmış."
FTP'ye bağlandım. Apache log dosyalarını indirdim.
Investigation: Saldırının Anatomisi
Binlerce satır log.
Filtreleme yaptım: resimkaldir.php
Pazartesi, 14:07
POST /panel/resimkaldir.php
yol=../test_deneme.jpg
Response: 200 OK "Dosya Silindi"
Saldırgan ilk test yapmış. Bir resim dosyası seçmiş, silmiş. Çalıştığını görmüş.
Pazartesi, 14:08
POST /panel/resimkaldir.php
yol=../config/database.php
Response: 200 OK "Dosya Silindi"
Database config dosyası silindi. Site veritabanına bağlanamaz hale geldi.
Pazartesi, 14:09 - 14:23 (14 dakika)
POST /panel/resimkaldir.php (5,247 requests)
yol=../uploads/products/urun_1.jpg
yol=../uploads/products/urun_2.jpg
yol=../uploads/products/urun_3.jpg
...
yol=../uploads/products/urun_5247.jpg
5,247 ürün görseli. Hepsi silindi. 14 dakika içinde.
Pazartesi, 14:24 - 14:31
POST /panel/resimkaldir.php (73 requests)
yol=../panel/index.php
yol=../panel/urunler.php
...
Admin panelinin %80'i silindi.
Pazartesi, 14:32
POST /panel/resimkaldir.php
yol=../index.php
Response: 200 OK "Dosya Silindi"
Ana sayfa gitti.
Toplam süre: 26 dakika. Toplam silinen dosya: 5,247 adet.
The Aftermath: Hasar Tespiti
Silinen Dosyalar:
- Ana Sayfa (index.php)
- Database Config (config/database.php)
- Admin Panel (%80'i - 73 dosya)
- Ürün Görselleri (5,247 adet)
- Kategori Banner'ları (127 adet)
- Blog Görselleri (891 adet)
Muhammet Bey'in Maliyeti:
Downtime: Pazartesi 14:33'ten Salı 16:00'ya kadar (~25 saat)
Gelir Kaybı: 15,000 TL (1 gün satış)
Recovery: 24,000 TL (developer + hosting + danışmanlık)
Müşteri Telafisi: 43,000 TL (iptal + indirim kuponları)
TOPLAM: 82,000 TL
Ama Asıl Soru:
"Ya 23 site birden saldırıya uğrasaydı?"
23 × 82,000 TL = ~1,886,000 TL
Responsible Disclosure: Diğer 22 Şirketi Uyarmak
Muhammet Bey'in krizini çözdükten sonra, elimde bir liste vardı: 22 tane daha aynı zafiyete sahip site.
Etik olarak ne yapmalıydım?
Seçenek 1: Hiçbir şey yapma
22 şirket risk altında kalır
Seçenek 2: NBSP MEDYA'yı aramak
Ajans kabul edip düzeltir mi? Yoksa inkar mı eder?
Seçenek 3: Her şirketi tek tek aramak
Doğrudan uyarı Çok zaman alıcı
Yaptığım:
- NBSP MEDYA'yı aradım
- Zafiyet raporumu gönderdim
- 72 saat süre verdim
- Cevap alamadım
Sonra her şirketi tek tek aradım.
Sonuç:
- 8 şirket hemen harekete geçti, düzeltti
- 7 şirket "sonra bakacağız" dedi
- 5 şirket cevap vermedi
- 2 şirket "ajansımız halledecek" dedi
3 ay sonra tekrar kontrol ettim:
- 11 site düzeltilmiş
- 12 site hala aynı zafiyet var
The Fix: Doğru Kod
Muhammet Bey ve diğer 7 şirketle beraber, kodu güvenli versiyona çevirdik.
Güvenli Versiyon: 82 Satır, 8 Katman
<?php
session_start();
// ============================================
// LAYER 1: Authentication
// ============================================
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
die(json_encode(['success' => false, 'error' => 'Giriş yapmalısınız']));
}
// ============================================
// LAYER 2: Authorization
// ============================================
if ($_SESSION['role'] !== 'admin') {
http_response_code(403);
die(json_encode(['success' => false, 'error' => 'Yetkiniz yok']));
}
// ============================================
// LAYER 3: CSRF Protection
// ============================================
if (!isset($_POST['csrf_token']) ||
!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
http_response_code(403);
die(json_encode(['success' => false, 'error' => 'Geçersiz istek']));
}
// ============================================
// LAYER 4: Rate Limiting
// ============================================
$rate_key = "file_delete_" . $_SESSION['user_id'];
if (checkRateLimit($rate_key, 10, 60)) {
http_response_code(429);
die(json_encode(['success' => false, 'error' => 'Çok fazla istek']));
}
// ============================================
// LAYER 5: Input Validation
// ============================================
$file_id = filter_var($_POST['file_id'], FILTER_VALIDATE_INT);
if ($file_id === false || $file_id < 1) {
http_response_code(400);
die(json_encode(['success' => false, 'error' => 'Geçersiz dosya ID']));
}
// ============================================
// LAYER 6: Database Check
// ============================================
$pdo = getDatabaseConnection();
$stmt = $pdo->prepare("SELECT id, file_path, user_id FROM uploaded_files WHERE id = ?");
$stmt->execute([$file_id]);
$file = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$file) {
http_response_code(404);
die(json_encode(['success' => false, 'error' => 'Dosya bulunamadı']));
}
// Ownership check
if ($file['user_id'] !== $_SESSION['user_id'] && $_SESSION['role'] !== 'super_admin') {
http_response_code(403);
die(json_encode(['success' => false, 'error' => 'Bu dosyayı silme yetkiniz yok']));
}
// ============================================
// LAYER 7: Path Validation
// ============================================
$file_path = realpath($file['file_path']);
$uploads_dir = realpath(__DIR__ . '/../uploads/');
if ($file_path === false || strpos($file_path, $uploads_dir) !== 0) {
logSecurityEvent('INVALID_PATH_ATTEMPT', [
'user_id' => $_SESSION['user_id'],
'file_id' => $file_id
]);
http_response_code(400);
die(json_encode(['success' => false, 'error' => 'Geçersiz dosya yolu']));
}
// ============================================
// LAYER 8: Soft Delete
// ============================================
try {
$stmt = $pdo->prepare("UPDATE uploaded_files SET deleted_at = NOW(), deleted_by = ? WHERE id = ?");
$stmt->execute([$_SESSION['user_id'], $file_id]);
logAction('file_delete', [
'file_id' => $file_id,
'deleted_by' => $_SESSION['user_id']
]);
echo json_encode(['success' => true, 'message' => 'Dosya silindi']);
} catch (PDOException $e) {
error_log("File deletion failed: " . $e->getMessage());
http_response_code(500);
die(json_encode(['success' => false, 'error' => 'Silme işlemi başarısız']));
}
?>
16 satır → 82 satır
Ama şimdi güvenli.
Lessons Learned: 5 Kritik Ders
1. "Sadece X Yapıyor" = Tehlikeli Düşünce
Muhammet Bey: "Bu dosya sadece resim siliyor, önemli değil."
Gerçek:
- Sadece resim yok → Her dosyayı silebiliyor
- Güvenlik yok → Herkes kullanabiliyor
- Sınır yok → Sınırsız silme
2. Template Zafiyetleri = Çarpan Etkisi
1 zafiyet × 23 site = 23 kat risk
Bir ajans, bir hata yapar → Tüm müşteriler etkilenir.
3. "Ajansımız Profesyonel" ≠ Güvenli
NBSP MEDYA 50+ site yapmıştı. Deneyimliydi.
Ama tek bir zafiyeti 23 kere kopyalamıştı.
4. Kaynak Kod Oku, Varsayma
Ben nmap, Burp Suite, nikto gibi toollar kullanmadım.
Sadece:
- Ctrl+U (kaynak kodu gör)
- Inspect Element
- Dosya isimlerini test et
Basit araçlar, büyük keşifler.
5. Responsible Disclosure Zor Ama Gerekli
22 şirketi aramak zordu.
7'si dinlemedi.
Ama 11'i düzeltti.
11 şirket, potansiyel krizden kurtuldu.
Epilog: 6 Ay Sonra
6 ay sonra tekrar kontrol ettim.
23 siteden:
- 15 site düzeltilmiş (güvenli)
- 5 site kısmen düzeltilmiş (session var ama path validation yok)
- 3 site hala aynı zafiyet var
Muhammet Bey'den mail geldi:
Zafer Bey,
6 ay oldu. Site hala ayakta, hiç saldırı olmadı.
Ama en önemlisi: Başka bir ajansa geçtik. Güvenliği ciddiye alan bir firmaya.
Tüm kodları baştan yazdırdık. Haftalık security audit yapıyoruz. WAF kurduk.
82,000 TL pahalı bir dersti. Ama öğrendik.
Artık "sadece X yapıyor" demiyorum.
Teşekkürler.
Muhammet
Sizin İçin Checklist
Bugün Yapın (15 dakika):
# 1. Sitenizin kaynak kodunu görün
Ctrl+U (Chrome/Firefox)
# 2. Footer'a bakın
<!-- Ajans adı? Template versiyonu? -->
# 3. /panel/ veya /admin/ deneyin
https://siteniz.com/panel/
https://siteniz.com/admin/
# 4. Dosya isimleri görünüyor mu?
Bu Hafta Yapın:
- [ ] Ajansınıza sorun: "Hangi template kullanıyorsunuz?"
- [ ] "Bu template kaç sitede kullanılıyor?"
- [ ] "Son güvenlik güncellemesi ne zaman yapıldı?"
Bu Ay Yapın:
- [ ] Penetration test yaptırın
- [ ] Kaynak kodları inceleyin
- [ ] Template zafiyetlerini kontrol edin
Son Söz
16 satırlık bir kod.
23 farklı şirket.
Potansiyel 1.8 milyon TL hasar.
Ve hiçbiri bundan habersizdi.
Template'ler hayat kurtarır — hızlı geliştirme, düşük maliyet.
Ama aynı template'ler felakete de sebep olabilir — bir zafiyet, çarpan etkisi.
Soru:
Sizin siteniz hangi template kullanıyor?
O template kaç sitede daha var?
Ve en önemlisi:
Son güvenlik kontrolü ne zaman yapıldı?
Zafer Hamza Kuruçay
Siber Güvenlik ve Red Team Danışmanı
hyprshield.com
zafer.kurucay@hyprshield.com
+905522927858
"Bir template, bir zafiyet, 23 şirket. Kaynak kodu okuyun."
Not: Bu hikaye gerçek bir olaya dayanmaktadır. Ajans adı, şirket isimleri ve bazı detaylar gizlilik nedeniyle değiştirilmiştir. Web uygulamanızın güvenlik testine ihtiyacınız varsa benimle iletişime geçin.
#CyberSecurity #WebSecurity #PenetrationTesting #RealStory #TemplateVulnerability #OWASP #ResponsibleDisclosure #InfoSec