İçeriğe atla
0 XP 0
12 dkderinleşmeyle 14 dk55 XP6 etkinlik

PowerShell ile temel yönetim ve sorgu

Bu derste
  • PowerShell'in Linux'tan farkı metin değil nesne taşımasıdır; bu derste Where-Object ve Select-Object ile bu nesneleri süzmeyi öğreneceksin.
  • Get-WinEvent ile olay günlüğünü sorgulayacak, aynı komutların saldırgan elinde nasıl bir araca dönüştüğünü göreceksin.
  • Betik bloğu günlüğü, AMSI ve çalıştırma ilkesinin neden bir sınır olmadığını da öğrenirsin.

Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +55 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.

Önceki derste hesapları ve grupları whoami ile tek tek sorguladın. Bu iş yüz makinede elle yapılamaz; otomatikleşmesi gerekir. Windows'un bunun için standart aracı PowerShell.

PowerShell'in Linux kabuğundan en büyük farkı, borusunun metin değil nesne taşımasıdır ve bu fark her şeyi değiştirir. Bu derste cmdlet'lerin nasıl adlandığını, Where-Object ve Select-Object ile nesneleri nasıl süzdüğünü, olay günlüğünü Get-WinEvent ile nasıl sorguladığını öğreneceksin. Aynı komutların hem savunan hem saldıran tarafın elinde neden güçlü olduğunu da göreceksin.

Amaç ezber değil; birkaç yapı taşını anlayıp kendi sorgunu kurabilmek. Bir kez kavradığında, komut satırı korkutucu olmaktan çıkıp en hızlı aracın olur.

Cmdlet ve nesne borusu

PowerShell komutlarına cmdlet denir ve hepsi Fiil-İsim biçiminde yazılır: Get-Process, Get-Service, Stop-Process. Fiil ne yaptığını, isim neyle ilgilendiğini söyler; bu düzen sayesinde yüzlerce komutu ezberlemeden adını tahmin edebilirsin.

Asıl fark boruda. Linux borusu bir komuttan ötekine düz metin aktarır. PowerShell borusu ise .NET nesnesi aktarır; her nesnenin adlı alanları vardır ve sonraki komut bu alanları doğrudan okur.

Bir nesnenin hangi alanları ve işlemleri taşıdığını Get-Member ile görürsün: komutu Get-Member'a borular, dönen listede alan adlarını okursun. Bir komutun ne yaptığını ve örneklerini ise Get-Help verir. Bu iki komut, tanımadığın bir cmdlet'i tek başına keşfetmene yarar.

Bir örnekle bağla. Diyelim hiç bilmediğin Get-Service cmdlet'iyle karşılaştın. Önce Get-Help Get-Service -Examples yazıp hazır kullanım örneklerini okursun. Sonra Get-Service | Get-Member ile dönen nesnenin alanlarını görürsün: Status, Name, DisplayName gibi.

Artık bu alanları Where-Object ve Select-Object içinde adlarıyla kullanabilirsin. Bu iki komut, belgeyi açmadan kabuğun içinden öğrenmene yarar ve zamanla komutların adını tahmin etme alışkanlığı kazandırır. Yeni bir cmdlet'le karşılaştığında ilk refleksin bu olsun.

Etkinlik 1 / 6

PowerShell'de Get-Process | Sort-Object CPU yazdığında sıralama neye göre yapılır?

Where-Object ve Select-Object ile süzmek

Where-Object borudaki her nesneyi bir koşuldan geçirir ve yalnız koşulu tutanları bırakır. Koşulun içinde $_ o an işlenen nesneyi temsil eder: Where-Object { $_.CPU -gt 50 } yalnız CPU değeri 50'den büyük süreçleri geçirir. Dikkat: CPU alanı anlık yüzde değildir; sürecin açıldığından beri harcadığı toplam işlemci süresini saniye olarak tutar.

Karşılaştırma işleçleri kendine özgüdür ve kısa adlarla yazılır: -eq eşit, -ne eşit değil, -gt büyük, -lt küçük, -like joker karakterli eşleşme, -match düzenli ifadeyle eşleşme. Select-Object hangi alanların görüneceğini seçer ve -First ile listeyi baştan kısaltır. Bu parçaları boruyla dizersin ve zincir soldan sağa çalışır. Her adım bir öncekinin çıktısıyla beslenir, bu yüzden filtreyi hep başa koyarsın: ne kadar erken süzersen, sonraki adımlar o kadar az veriyle uğraşır ve komut o kadar hızlı biter.

PS C:\> Get-Process | Where-Object {$_.CPU -gt 50} | Sort-Object CPU -Descending | Select-Object -First 5 Name,CPU

Name           CPU
----           ---
MsMpEng    1843.52
msedge      912.30
explorer    455.17
svchost     201.75
OUTLOOK      96.40
En çok işlemci süresi harcamış beş süreci listeler.

Zinciri parça parça oku. Get-Process bütün süreçleri üretir. Where-Object yalnız toplam işlemci süresi 50 saniyeyi geçenleri bırakır.

Sort-Object bunları büyükten küçüğe dizer. Select-Object -First 5 son olarak yalnız ilk beş satırı ve iki alanı gösterir. Çıktıda en üstte Defender'ın tarama motoru MsMpEng duruyor; uzun süre açık kalan bir güvenlik servisi için bu olağandır.

Sıralama adımını çıkarırsan sonuç bozulmaz ama anlamı değişir: -First artık en yüksek beşi değil, filtreden hangi beş süreç önce geldiyse onları gösterir. Bu yüzden 'ilk' sözcüğü PowerShell'de 'en büyük' demek değildir; sırayı sen kurmazsan komut sana rastgele görünen bir sıra verir. Bu küçük ayrım, raporuna yanlış beş süreci yazmanla doğrusunu yazman arasındaki farktır. Zincirin her halkasının ne yaptığını tek tek düşünmek, sonucu güvenle yorumlamanın tek yoludur.

Etkinlik 2 / 6

Zincirden Sort-Object CPU -Descending adımı çıkarılırsa Select-Object -First 5 hangi beş süreci gösterir?

Önce kodu oku, sonra çıktıyı seç.

Get-Process | Where-Object {$_.CPU -gt 50} | Select-Object -First 5 Name,CPU

Olay günlüğü ve servisleri sorgulamak

Aynı desen araştırmada da işler. Get-Service makinedeki servisleri döndürür; Get-Service | Where-Object { $_.Status -eq 'Running' } yalnız çalışanları bırakır. Olay günlüğünü ise Get-WinEvent sorgular. -LogName hangi günlüğe bakacağını, -MaxEvents kaç kayıt isteyeceğini söyler: Get-WinEvent -LogName Security -MaxEvents 20.

Büyük bir günlükte bütün kayıtları çekip Where-Object ile süzmek yavaştır. Daha hızlısı -FilterHashtable ile süzmeyi en başta, günlük sağlayıcısının katmanında yapmaktır. Eski Get-EventLog cmdlet'i de benzer iş görür, ama eski bir Win32 API'sine dayanır, yalnız klasik günlükleri okur ve PowerShell 6 ile sonrasında bulunmaz. Yeni betiklerde Get-WinEvent tercih edilir, çünkü hem klasik hem yeni günlük biçimlerini okur.

PS C:\> Get-WinEvent -FilterHashtable @{
>>   LogName   = 'Security'
>>   Id        = 4625
>>   StartTime = (Get-Date).AddHours(-1)
>> } | Select-Object TimeCreated, Id

TimeCreated            Id
-----------            --
12.05.2026 23:41:09  4625
12.05.2026 23:41:04  4625
12.05.2026 23:41:02  4625
FilterHashtable süzmeyi en başta yapar; son bir saatteki başarısız girişleri çeker.

Sorguyu satır satır oku. @{ ... } bir hashtable, yani anahtar ve değer çiftlerinden oluşan bir sözlüktür. LogName hangi günlüğe bakılacağını, Id hangi olay numarasının isteneceğini söyler.

StartTime için (Get-Date).AddHours(-1) şu andan bir saat öncesini hesaplar. Süzme Windows'un olay günlüğü katmanında yapılır, bu yüzden PowerShell'e yalnız eşleşen üç kayıt gelir. Select-Object son adımda iki alanı seçer.

Çıktının sırasına dikkat et: Get-WinEvent en yeni olayı en üste koyar. Zaman sırasıyla okumak istersen zincire Sort-Object TimeCreated eklersin. Bu küçük ayrıntı, bir saldırının adımlarını doğru sırayla anlatmanın ön koşuludur.

Get-WinEvent her olayı bir nesne olarak döndürür ve en çok kullandığın alanlar bellidir: TimeCreated olayın zamanını, Id Event ID'yi, Message okunabilir açıklamayı taşır. Bir araştırmada çoğu zaman bu üç alanı seçmen yeter. Aynı nesneyi Format-Table ile tabloya, Export-Csv ile bir dosyaya dökebilirsin; böylece bulgunu bir rapora taşırsın.

Nesne tabanlı çıktının değeri burada ortaya çıkar: aynı veriyi hem ekranda okur hem de bir aracın işleyeceği biçime çevirirsin. Metin çıktıyla bunu yapmak için önce ayrıştırmak gerekirdi.

Etkinlik 3 / 6

Security günlüğünden son 20 kaydı çekmek için Get-WinEvent -LogName Security -____ 20 yazarsın.

Servis tarafında da nesneyi süzüp ekrana ne basılacağını en sona bırakırsın. Bir servisin yalnız adını ya da durumunu değil, doğrudan nesnenin kendisini filtrelersin; görünümü Select-Object en sonda belirler. Bu sıra hatayı da azaltır. Erken bir adımda yanlış alan adı yazarsan sonraki adımlar boş nesne üstünde çalışır ve sorun hemen fark edilir.

Araştırmada çalışmayan servisleri aramak sık görülür: beklenen bir servis durmuşsa bu bir arıza, beklenmedik bir servis çalışıyorsa bu bir sinyal olabilir. İki durumu da aynı süzme deseniyle tek komutta çıkarırsın. Aynı mantığı Get-WinEvent çıktısına da uygularsın; araç değişse de süzme deseni hep aynı kalır.

Etkinlik 4 / 6

Çalışmayan (Stopped) bütün servisleri listele.

ogrenci@lab

PowerShell'in iki yüzü

Aynı güç saldırganın da elini güçlendirir. PowerShell her Windows'ta kurulu olduğu için ayrı bir araç indirmeye gerek bırakmaz; önceki derste gördüğün LOLBin kavramının en bilinen örneğidir. MITRE ATT&CK bu kötüye kullanımı T1059.001 olarak izler. Savunan taraf bunu görünür kılar.

Betik Bloğu Günlüğü (Script Block Logging) açıkken çalıştırılan her betik bloğu, gizlense bile Microsoft-Windows-PowerShell/Operational günlüğüne 4104 numaralı olayla düşer. Modül günlüğü (4103) ise boru hattı ayrıntılarını tutar. AMSI (Antimalware Scan Interface) çalışma anında betiği tarayıcıya verir, böylece bellekte açılan gizli komut da taranır. Bir uç noktada normalde görülmeyen bir kullanıcının Invoke-Expression çalıştırması, ilk incelenmesi gereken sinyallerden biridir.

PowerShell'in güvenlik ayarları da yanlış anlaşılır. ExecutionPolicy betiklerin hangi koşulda çalışacağını belirler, ama bir güvenlik sınırı değildir; komutları doğrudan satıra yazan biri onu kolayca atlar. Bu yüzden onu tek başına bir koruma sayma.

Bir başka saldırgan oyunu da sürüm düşürmedir: eski Windows PowerShell 2.0 bu modern günlükleri ve AMSI'yi tanımaz, bu yüzden saldırgan 2.0 kabuğunu çağırıp izlemeden kaçmaya çalışır. Microsoft bu yüzden PowerShell 2.0'ı kaldırıyor; Windows 11 24H2'de Ağustos 2025'ten, Windows Server 2025'te Eylül 2025'ten sonra artık gelmiyor. Savunan taraf eski sürümün hâlâ çağrılabildiği makineleri kapatır.

Görünürlüğü yalnız PowerShell'in kendi günlüğüne bırakmazsın. Bir süreç olarak PowerShell, Güvenlik günlüğüne 4688 numaralı süreç oluşturma olayıyla da düşer; bu denetim açıkken komut satırını da kaydeder.

Sysmon ise 1 numaralı olayıyla her yeni süreci tam komut satırı ve üst süreciyle birlikte yazar. İkisi birlikte sana süreç ağacını verir: hangi program hangisini doğurdu? Bir Word belgesinden doğan bir PowerShell, sonra ağa bağlanan bir alt süreç; bu zincir tek bir satırdan çok daha fazlasını anlatır. Savunma, bu kayıtları bir yerde toplayıp kalıpları aramaktır.

Etkinlik 5 / 6

Bir uç noktada Betik Bloğu Günlüğü kapalıyken çalıştırılan gizlenmiş bir PowerShell komutu neden analiste görünmez?

Linux'tan PowerShell'e

Çoğu PowerShell komutunun bir Linux karşılığı vardır ve ikisi aynı işi görür: listele, süz, alan seç. Fark yöntemdedir. Linux araçları metin üstünde çalışır, bu yüzden awk ve grep ile sütun sayar, alan ayırırsın.

PowerShell nesne üstünde çalışır, bu yüzden alanı adıyla çağırırsın. Bu dersin desenini bir sonraki derste tek bir makineye değil, bir günlüğün tamamına uygularsın. auth.log'u grep ile süzdüğün gibi, Windows olay günlüğünü de belirli Event ID'lere göre süzer ve bir kaba kuvvet denemesini oradan çıkarırsın.

Bu farkın pratik bir sonucu var. Nesne borusu, tek seferlik bir komutu kolayca yeniden kullanılabilir bir araca çevirir. Bugün elle yazdığın Where-Object zincirini yarın bir betiğe koyar, bir parametre eklersin ve aynı sorgu yüz makinede çalışır.

Savunan ekiplerin günlük işi büyük ölçüde budur: sık sorulan soruları birer küçük betiğe dönüştürmek. Bir sonraki derste bu beceriyi olay günlüğüne yöneltecek ve bir saldırının izini sayılarla okuyacaksın. Komutlar aynı kalır; yalnız baktığın veri ve süzdüğün Event ID değişir.

Etkinlik 6 / 6

PowerShell cmdlet'ini en yakın Linux karşılığıyla eşleştir.

Soldan bir tane seç, sağdan eşini seç. Kullanılmış bir seçeneği yeniden seçersen yeni öğeye taşınır.
Derinleş:ExecutionPolicy bir güvenlik sınırı değildir

Yeni başlayanlar ExecutionPolicy'yi bir güvenlik duvarı sanır. Değildir. Microsoft bunu açıkça söyler: çalıştırma ilkesi bir güvenlik sınırı değil, derinlemesine savunmanın bir katmanıdır.

Amacı, kullanıcının bir betiği yanlışlıkla çalıştırmasını zorlaştırmaktır, kararlı bir saldırganı durdurmak değil. Çünkü ilke bir betiği engellediğinde, aynı komutları doğrudan komut satırına yapıştırmak ya da pwsh.exe'yi -ExecutionPolicy Bypass ile başlatmak onu anında atlar. Varsayılan ilke sürüme göre değişir.

Windows PowerShell 5.1 istemcide Restricted, sunucuda RemoteSigned ile gelir; PowerShell 7 ise iki tarafta da RemoteSigned kullanır. RemoteSigned'da yerelde yazdığın betikler çalışır, internetten inenler imzasızsa çalışmaz. Gerçek sınırı uygulama denetimi ve en az yetki kurar, çalıştırma ilkesi değil.

İlkeyi bir engel değil, bir güvenlik ağı say. AppLocker ve WDAC gibi araçlar, hangi programın çalışabileceğini imzaya ve yola göre gerçekten kısıtlar; sert sınır işte orada çizilir. ExecutionPolicy ise yalnız dikkatli kullanıcıyı yanlış bir tıklamadan korur.

Derinleş:4104 şüpheli blokları kendiliğinden kaydeder

Betik Bloğu Günlüğü'nün ilginç bir yanı var. Bu ayarı hiç açmasan bile, PowerShell şüpheli bulduğu belirli betik bloklarını yine 4104 olayına, bu kez uyarı düzeyinde yazar. Yani gizlenmiş, kodu çözülerek çalıştırılan ya da bilinen saldırı kalıplarını taşıyan bir blok, günlük kapalıyken de iz bırakabilir. Bir istisna var: ilkeyi açıkça Devre Dışı yaparsan bu kendiliğinden kayıt da durur.

Tam görünürlük için ayarı açıkça etkinleştirirsin; o zaman her blok ayrıntısıyla kaydedilir. Kayıtların kendisi hassas veri taşıyabildiği için Microsoft, betik günlüğüyle birlikte Korumalı Olay Günlüğü'nü de açmanı önerir; bu, kayıtları bir ortak anahtarla şifreler ve çözmeyi yalnız gizli anahtarın bulunduğu güvenli bir toplayıcıya bırakır. Bu yüzden şüpheli bir uç noktada 4104 uyarılarını, günlük hiç açılmamış olsa bile ilk bakılacak yerlerden biri say.

Bu derste bir hata mı gördün? Bildir