Herkese iyi akşamlar.
Bu hafta yaşanan ConnectWise Cloud Backup olayı, SOC tarafında uzun süredir ihmal edilen bir konuyu yeniden gündeme getirdi: servis sorumlusu (service principal) girişleri. Çoğu ekip kullanıcı girişlerini didik didik izlerken, uygulamaların kendi adına aldığı token'lara pek bakmıyor. Bu yazıda Microsoft Sentinel (ya da Log Analytics) üzerinde bu boşluğu nasıl kapatabileceğinizi, sakin sakin ve adım adım anlatmaya çalışacağım.
Önce kavram: Kullanıcı girişi ile servis sorumlusu girişi farkı
Bir kullanıcı giriş yaptığında arkasında bir insan, bir cihaz ve genelde MFA vardır. Bir uygulama ise "client credentials" akışıyla, yani bir secret ya da sertifikayla doğrudan token alır. Ortada MFA yoktur, koşullu erişim politikaları çoğu zaman uygulanmaz. Bu yüzden bir uygulamanın secret'ı çalındığında saldırgan, uygulamanın sahip olduğu her izni miras alır.
Entra tarafında bu girişler ayrı bir yerde tutulur. Sentinel'de karşılık gelen tablo:
AADServicePrincipalSignInLogsAADServicePrincipalSignInLogs
| where TimeGenerated > ago(1d)
| summarize count()Kural 1: Uygulama başına bilinen IP tabanı
Her uygulamanın normalde nereden bağlandığını öğrenmek, en değerli tespittir. Son 14 günün IP'lerini taban olarak alıp, bugün yeni görülen IP'leri çıkarıyoruz:
let taban = AADServicePrincipalSignInLogs
| where TimeGenerated between (ago(14d) .. ago(1d))
| summarize by AppId, IPAddress;
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(1d)
| where ResultType == 0
| join kind=leftanti taban on AppId, IPAddress
| summarize Ilk=min(TimeGenerated), Son=max(TimeGenerated), Adet=count()
by ServicePrincipalName, AppId, IPAddress
| order by Adet descKural 2: Uygulamaya yeni kimlik bilgisi eklenmesi
ConnectWise olayında en kritik adım, saldırganın başka uygulamalara yeni client secret eklemesiydi. Bu, kalıcılık için çok kullanılan bir yöntem. AuditLogs tablosunda şöyle yakalanır:
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName has_any ("Add service principal credentials",
"Update application - Certificates and secrets management")
| extend Yapan = coalesce(tostring(InitiatedBy.user.userPrincipalName),
tostring(InitiatedBy.app.displayName))
| extend Hedef = tostring(TargetResources[0].displayName)
| project TimeGenerated, OperationName, Yapan, Hedef, ResultKural 3: Dizin rolüne kendini ekleyen uygulamalar
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName == "Add member to role"
| where isnotempty(InitiatedBy.app.displayName)
| extend Rol = tostring(TargetResources[0].modifiedProperties[1].newValue)
| project TimeGenerated, Uygulama=tostring(InitiatedBy.app.displayName), Rol, TargetResourcesOperasyonel öneriler
- Bu üç kuralı önce "sadece raporla" modunda bir hafta çalıştırın, sonra alarma çevirin.
- Yüksek izinli uygulamaların bir listesini çıkarın (Directory.ReadWrite.All, Mail.ReadWrite, Files.ReadWrite.All). Alarm önceliğini bu listeye göre belirleyin.
- Microsoft Graph activity log'larını açın. Graph üzerinden yapılan dizin okumaları başka türlü iz bırakmayabilir.
- Olay müdahale planınıza "uygulama secret'ı çalındı" senaryosunu ekleyin. Kullanıcı parolasını sıfırlamak burada işe yaramaz; secret'ı döndürmek ve uygulamayı devre dışı bırakmak gerekir.
Benim deneyimim şu: bu tür kurallar ilk günlerde pek alarm üretmez ve ekip "gerek var mıydı" diye sorar. Ama tam olarak o bir gün geldiğinde, elinizde başka hiçbir iz olmayabilir.
Sorularınızı konu altında sorabilirsiniz, kendi ortamınızda karşılaştığınız yanlış pozitifleri paylaşırsanız birlikte iyileştirebiliriz.
Kaynaklar
- Microsoft Learn - AADServicePrincipalSignInLogs ve AuditLogs tablo referansları
- Microsoft Learn - Entra ID sign-in log türleri
- The Hacker News - ConnectWise Cloud Backup olayı (17 Ekim 2026)