Blokujeme útočníka
Automatické přídávání pravidel firewallu
Blokujeme zlobivé hochy    

Občas mě něco chytne, dostanu nápad a nemůžu přestat, dokud si neověřím, že bych to dokázal udělat. Co na tom, že to není nic nového, a že nejspíš znovu objevuju kolo. Stejně má člověk potom radost, že se mu povedlo, co chtěl.

Včera mě napadlo, že bych mohl sbírat ze secure logu informace o nepovedených přihlášeních a pokud tam bude z jedné IP adresy víc pokusů, tak bych mohl přístup z té adresy rovnou zablokovat. Není to nic nového, existují aplikace, které se jmenují IP BAN, Windows IP ban a podobně. Nicméně chtěl jsem si to zkusit sám, tak jsem se do toho pustil.

DISCLAIMER jako kráva. Jestli něco fakt nejsem, tak je to programátor. Když někdy vidím, co dokážou lidi s PowerShellem, tak bych to chtěl umět, ale nejsem si jistý, že bych to pobral, i kdybych se tomu věnoval denně. Zkusím každý kousek kódu okomentovat. Kromě toho, že to lépe pochopíte, to slouží hlavně pro mě, protože jednou na to budu koukat a nebudu vědět, jak jsem to udělal.

Co budeme potřebovat? Určitě PowerShell 7. Ten naštěstí může fungovat na stejném stroji se starší verzí PowerShellu, takže ani na Exchange serveru to nic nerozbije. Aktuální veze je: tady.

Další věc, kterou potřebujeme je dostatek logů ke čtení. Security log se přepisuje. Pokud někdo začne zkoušet kombinace jmen a hesel v noci, tak to může udělat tolik záznamů, že ty nejstarší začnou postupně mizet. My je tam chceme mít, takže zvedneme velikost logu. Jde to udělat přes group policy na všech strojích. Vytvořte novou group policy a jděte do nastavení:
Computer Configuration → Policies → Administrative Templates → Windows Components → Event Log Service
Vyberte Security Log
Nastavte novou velikost dle potřeby (Někde mi stačí 50MB, jinde se pohybuju v řádek GB.)
Nastavte retention, já tam mám většinou Overwrite as needed.
Pokud nechcete použít group policy a chcete to zkusit jen na jednom serveru, tak si na něm otevřete Event Viewer, najděte Security Log. Klikněte na něj pravým tlačítkem myši. A z menu vyberte Properties. Vypadá to nějak takhle:
seclog

V PowerShellu 7 se k prohledávání logů používá příkaz Get-WinEvent. Podle toho, kolik máte přístupů k serveru, budete mít i záznamů v logu. Někdo může chtít projít data za celý den. Pro někoho bude i poslední hodina znamenat dlouhé počítání. Takže budeme chtít pouze data za nějaký čas. Nechceme vidět všechny záznamy v Security Logu, zajímají nás jen nezdařená přihlášení, což je EventID 4625. A protože těch dat může být hodně, tak chceme použít filtr při výběru a nikoliv vzít všechno a až pak to pomocí | where-object očesat. Použijeme FilterHashtable, což je něco, s čím jsem se potkal až v PowerShellu 7. Načteme výsledky do $messages. Příkaz bude vypadat nějak takto:

$messages = get-winevent -FilterHashtable @{LogName="Security";ID=4625;StartTime=$start}

Už jsme se o tom bavili. Data bereme ze security logu, potřebujeme pouze EventID 4625 a chceme data jen za nějaké období. StartTime říká, od kterého datumu a času chceme procházet logy a pokud není specifikovaný EndTime, tak to chceme až do nejaktuálnějšího záznamu v logu. A máme tam proměnou, kterou musíme nejdřív naplnit. Aby se to přehledněji upravovalo, tak nejdříve zadávám proměnou $howmuch. Tam dávám, kolik hodin chci jít do minulosti. A tu pak použiju pro výpočet datumu a času do proměnné $start.

$howmuch = 5
$start = (get-date).addhours(-$howmuch)

Podívejme se, co z toho vlastně leze, nás zajímá, co v Message:
secmessage

Je to vlastně jeden dlouhý řádek, pár tabelátorů. Blbě se s tím pracuje. Takže to nejdřív roztrháme na řádky, smažeme prázdné řádky. Budeme procházet všechny záznamy ve foreach ($message in $messages), nejdřív tedy to roztrhání na řádky, aby v tom šlo hledat.

$accountdata = ($message.message).split([Environment]::NewLine, [StringSplitOptions]::RemoveEmptyEntries)

Teď najdeme řádek, kde je Source Network Address: a opět to roztrhneme, tentokrát u dvojtečky. Za ní je ještě tabelátor, takže toho se potřebujeme zbavit, ať se dostaneme k IP adrese. Pro tabelátor je v PowerShellu escape character `t.

$address = $accountdata | select-string -Pattern "Source Network Address:"
$address = ($address -split (":"))[1] -replace "`t";""

Takže v proměnné $address máme už jen IP adresu pachatele. Nebo aspoň sprostého podezřelého. A chceme vědět, kolikrát se tam ta IP adresa objevila. K tomu se výborně hodí hash table. To je můj oblíbený typ proměnné. Je to něco jako pole, ale místo celočíselného indexu může být indexem cokoliv. V našem případě ideálně IP adresa. A hodnotou pak bude počet, kolikrát se v logu vyskytuje. Jelikož máme cyklus, který několikrát pojede, vždy vytáhne IP adresu, tak pokaždé zjistíme, jestli už se takový index v hasth table vyskytuje. A pokud ano, tak zvedneme hodnotu o jedna.

if ($ipdb.ContainsKey($address)) {
$ipdb[$address] = $ipdb[$address] + 1
}

No a pokud ne, tak takovou položku vytvoříme.

$ipdb.add($address,1)

Jakmile doběhne cyklus, tak máme hash table, kde máme pro každou IP adresu z logu počet, kolikrát se v logu objevila. Rozhodli jsme se, že třeba 20x už je moc a takovou adresu chceme na firewallu zablokovat. Takže uděláme další cyklus, budeme procházet hash table a hledat, která adresa má hodnotu větší nebo rovno 20. V takovém případě, vytvoříme pravidlo na firewallu a zároveň IP adresu přidáme do texťáku, odkud si to může vzít správce síťového firewallu a přidat blokování už na vstupu do firmy. (Pokud se jedná o venkovní adresu.) Nejdřív tedy potřebujeme všechny indexy, které jsou v hash table a uděláme pro ně cyklus foreach.

$indexes = $ipdb.keys
foreach ($index in $indexes) {

No a pokud splňuje podmínku, tak vytvořím nové pravidlo. Pojmenovávám ho jako Exchange banned + IP adresa, ať je rozpoznám. Na konec samotného pravidla přidávám -whatif, to odstraním až na konec.

$name = "Exchange banned " + $index
New-NetFirewallRule -DisplayName $name -Direction Inbound -LocalPort 443 -Protocol TCP -RemoteAddress $index -Action Block -whatif

No a na konci chceme ještě uložit celý obsah hash table do CSV, protože se na to chceme podívat, jestli tam nenajdeme něco zajímavého.

$ipdb.GetEnumerator() | Select-Object -Property key, value | Export-csv -NoTypeInformation -Delimiter "," -Path .\banned_addressses.csv

Než si ukážeme celý script, tak ještě komentář k tomu, jak vlastně funguje. Nerozlišujeme záznamy v secure logu dle služby. Sbíráme všechna nepovedená přihlášení, tedy i ty, které mají s Exchange serverem společné jen to, že se pokusili přistoupit k službě, která je na stejném serveru, jako Exchange. Takže se může stát, že vytváříte pravidlo pro IP adresu, pro kterou už tam blokovací pravidlo je. Sbíráme venkovní i vnitřní adresy. Možná pro ty vnitřní chcete mít trochu jiná pravidla. Ten script se dá použít i jinde než na Exchange serveru. Prakticky na jakémkoliv serveru. Jen můžou být jiné podmínky. Určitě ve vnitřní síti můžete chtít něco jako whitelist. Například v případě aplikačního serveru typu ERP, ke kterému přistupují uživatelé z remote desktop connection serveru hrozí větší množství nezdařených přihlášení. A fakt nechcete uříznout ERP systém všem uživatelům. Berte to jen jako vzorek. Ještě poznámka k proměnným ve scriptu: $howmuch říká, kolik hodin chci do minulosti. $toomuch říká, kolik záznamů v logu stačí na blokování. Změňte podle sebe.

$howmuch = 5
$start = (get-date).addhours(-$howmuch)

$toomuch = 20
$messsages = get-winevent -FilterHashtable @{LogName="Security";ID=4625;StartTime=$start}
$ipdb = @{}

foreach ($message in $messsages) {
$accountdata = ($message.message).split([Environment]::NewLine, [StringSplitOptions]::RemoveEmptyEntries)
$address = $accountdata | select-string -Pattern "Source Network Address:"
$address = ($address -split (":"))[1] -replace "`t";""
if ($address -like "*.*") {
if ($ipdb.ContainsKey($address)) {
$ipdb[$address] = $ipdb[$address] + 1
}
else {
$ipdb.add($address,1)
}
}
}
$indexes = $ipdb.keys

foreach ($index in $indexes) {
if ($ipdb[$index] -gt $toomuch) {
$name = "Exchange banned " + $index
New-NetFirewallRule -DisplayName $name -Direction Inbound -LocalPort 443 -Protocol TCP -RemoteAddress $index -Action Block -whatif
$index >> .\banned_addresses.txt
}
}

$ipdb.GetEnumerator() | Select-Object -Property key, value | Export-csv -NoTypeInformation -Delimiter "," -Path .\banned_addressses.csv

Okomentovaný script ke stažení najdete tady:
http://www.tnx.cz/secure_log.ps1

Poslední aktualizace - Neděle, 16. Května 2021 18:55
 
 
 
Page 1 of 1
© TNX alias Jan Kovář. Původní design stránky byl určen pro CMS Joomla! a vytvořen společností Siteground web hosting