Posted in: Windows 10, Windows 11, Windows 8 a 8.1, Windows server, Základy PowerShellu

Jak stará je vaše instalace Windows a jak dlouho systém běží?

Nedávno jsem četl na Živě.cz článek: Jak stará je vaše instalace Windows a jak dlouho systém běží? Zjistíte to jednoduchým příkazem, který se zaobíral dobou běhu OS a datumem posledního upgrade OS. Ten běh OS tam napsali pěkně PowerShellově:

(gcim Win32_OperatingSystem).LastBootUpTime #datum a čas spuštění
#náže době běhu
(Get-Date) - (gcim Win32_OperatingSystem).LastBootUpTime | select Days, Hours, Minutes, Seconds

Ale co v tom článku měli již nepěkně, tak je datum a čas instalace, resp. posledního upgrade. Oni to tam parsovali textově z příkazu systeminfo, ale to je zbytečné a takováto data se špatně dále zpracovávají. Lepší cestou je využít WMI třídu Win32_OperatingSystem a vytáhnout to standardně PowerShellem:

(gcim Win32_OperatingSystem).InstallDate

což krásně vrátí objekt typu System.DateTime, takže je možné nyní si nechat spočítat stáří OS:

(get-date) - (gcim Win32_OperatingSystem).InstallDate | select days, hours, minutes
Posted in: Vývoj počítačů, Windows 10, Windows 11, Windows server, Základy PowerShellu

Výpis pravidel Windows Firewall

Určitě každý admin někdy narazí na to, že něco nefunguje kvůli chybám v síťové komunikaci. Někdy za to může nastavení Firewallu. Originální konzole je přehledná a super, ale ve velkém množství pravidel se špatně hledá, respektive jsou tam filtry, které fungují, ale fultextové vyhledání je problém. Zkuste využít předností PowerShellu a jeho výstupu GridView k hledání aktivních pravidel.

Get-NetFirewallRule | select Name, DisplayName, DisplayGroup,
@{Name='Protocol';Expression={($PSItem | Get-NetFirewallPortFilter).Protocol}},
@{Name='LocalPort';Expression={($PSItem | Get-NetFirewallPortFilter).LocalPort}},
@{Name='RemotePort';Expression={($PSItem | Get-NetFirewallPortFilter).RemotePort}},
@{Name='RemoteAddress';Expression={($PSItem | Get-NetFirewallAddressFilter).RemoteAddress}},
Enabled, Profile, Direction, Action | Out-GridView

Další zajímavou možností je odnést si seznam běžících pravidel a jejich konfigurace v textovém souboru, který si člověk může v klidu analyzovat, nebo takto porovnávat změny.

Get-NetFirewallRule | select Name, DisplayName, DisplayGroup,
@{Name='Protocol';Expression={($PSItem | Get-NetFirewallPortFilter).Protocol}},
@{Name='LocalPort';Expression={($PSItem | Get-NetFirewallPortFilter).LocalPort}},
@{Name='RemotePort';Expression={($PSItem | Get-NetFirewallPortFilter).RemotePort}},
@{Name='RemoteAddress';Expression={($PSItem | Get-NetFirewallAddressFilter).RemoteAddress}},
Enabled, Profile, Direction, Action | Out-File -FilePath C:\Firewall.txt
Posted in: Vývoj počítačů, Windows 10, Windows 11, Windows 8 a 8.1, Windows server, Základy PowerShellu

Hash textu v PowerShellu

PowerShell nativně již nějaký pátek umí vypočítat hash souboru, což je velmi užitečné pro hledání duplicit, ověření po síti přeneseného souboru, ověření shody dvou souborů apod., takže super funkce.

Někdy je také potřeba spočítat hash z textu a nemusí jít zrovna jen nutně o heslo…. Na to PowerShell již vestavěnou funkci nemá, pro hash využívaný v AD již funkci implementoval Michael Grafnetter ve svém modulu DS Internals, já tedy zde uvedu funkci, která počítá především SHA, ale zvládne také MD5.

Function Get-StringHash {
    [cmdletbinding()]
    [OutputType([String])]
    param(
        [parameter(ValueFromPipeline, Mandatory = $true, Position = 0)][String]$String,
        [parameter(ValueFromPipelineByPropertyName, Mandatory = $true, Position = 1)]
        [ValidateSet("MD5", "RIPEMD160", "SHA1", "SHA256", "SHA384", "SHA512")][String]$HashName
    )
    begin {

    }
    Process {
        $StringBuilder = New-Object System.Text.StringBuilder
        [System.Security.Cryptography.HashAlgorithm]::Create($HashName).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))| foreach-object {
            [Void]$StringBuilder.Append($_.ToString("x2"))
        }
        $output = $StringBuilder.ToString()
    }
    end {
        return $output
    }
}

Funkce přijímá 2 povinné parametry, text k výpočtu hash a požadovaný algoritmus.

Posted in: Vývoj počítačů, Windows 10, Windows 11, Windows 8 a 8.1, Windows server, Základy PowerShellu

Čtení velkých textových souborů PowerShell

Určitě nejsem sám, kdo narazil u velkých souborů na to, že rutina Get-Content zabírá příliš mnoho RAM, u cca 150 MB txt souboru i více jak 4 GB RAM, pokud potřebuji zpracovávat soubor po jednotlivých řádcích. Obecně se doporučuje využít .NET rutiny, jak popisuje Yusuf Ozturk » PowerShell Performance Tips for Large Text Operations – Part 1: Reading Files

Z výše uvedeného se mi v praxi nejvíce osvědčilo:

$LogFilePath = Read-Host -Promt "Zadej cestu k souboru"
$Lines = [io.file]::ReadAllLines($LogFilePath)
[int]$LineNumber = 0;
 
# Read Lines
foreach ($Line in $Lines)
{
	$LineNumber++
        Write-Host $Line #vypíše aktuální řádek
}

V proměnné Line se nachází aktuální řádek souboru, takže je možné s nám dále pracovat.

Posted in: Základy PowerShellu

Hodiny do PowerShellu

Dnes je to jen malý kousek kódu, který se ovšem může hodit. Na liště OS máme běžně hodiny, ale někdy se může stát, že potřebujeme promítnout někam aktuální čas, ne z nějakého jiného důvodu chceme v konzoli vidět běžící čas. Nyní přidám jednoduché jádro, digitální hodiny. Jednoduchý kód využívající nekonečného cyklu while:

while($true)
{
    cls #vyčištění konzole
    $cas = Get-Date | select Hour, Minute, Second #uložení času do proměnné
    Write-Host $cas.Hour ":" $cas.Minute ":" $cas.Second #výpis času v obvyklém formátu
    Start-Sleep -Seconds 1 #uspání procesu na 1 sekundu
}

Ručičkové hodiny do konzole

Vykreslení ručičkových hodin s pomocí grafických knihoven .NET už na internetu a jde vytvořit v libovolném jazyce s využitím trochy goniometrie. Osobně jsem chtěl simulovat ručičkové hodiny přímo v konzole pomocí vykreslování znaků. Hodiny vykreslují polohu konců ručiček, které se pohybují spíše po nějakém kosém čtverci, nežli po kruhu, protože k vykreslení dochází vypsáním pole 30 x 30 znaků. Stejně jako digitálky výše, čtou čas z operačního systému, ze kterého následně vypočítají x a y souřadnici konce ručičky každé ručičky. Problémem je relativně pomalé překreslování příkazové řádky, takže běh nevypadá úplně hezky, ale jdou a přečíst se nechají 😉

  • kolečko je hodinová ručička
  • + je minutová ručička
  • * je sekundová ručička
function get-sekunda{
    param (
        $s
    )
    #výpočet polohy sekund
    if($s -le 15)
    {
        $y = $s + 15
        $x = $s
        $plocha[$x, $y] = '*'
    }
    elseif(($s -gt 15) -and ($s -le 30))
    {
        $y = 30 - ($s - 15)
        $x = $s
        $plocha[$x, $y] = '*'
    }
    elseif(($s -gt 30) -and ($s -le 45))
    {
        $y = 15 - ($s - 30)
        $x = 30 - ($s - 30)
        $plocha[$x, $y] = '*'
    }
    elseif(($s -gt 45) -and ($s -le 60))
    {
        $y = $s - 45
        $x = 30 - ($s - 30)
        $plocha[$x, $y] = '*'
    }
}

function get-minuta{
    param (
        $m
    )
    #výpočet polohy minut
    if($m -le 15)
    {
        $y = $m + 15
        $x = $m
        $plocha[$x, $y] = '+'
    }
    elseif(($m -gt 15) -and ($m -le 30))
    {
        $y = 30 - ($m - 15)
        $x = $m
        $plocha[$x, $y] = '+'
    }
    elseif(($m -gt 30) -and ($m -le 45))
    {
        $y = 15 - ($m - 30)
        $x = 30 - ($m - 30)
        $plocha[$x, $y] = '+'
    }
    elseif(($m -gt 45) -and ($m -le 60))
    {
        $y = $m - 45
        $x = 30 - ($m - 30)
        $plocha[$x, $y] = '+'
    }
}

function get-hodina{
    param (
        $h
    )
    #převedení na 12 hodin režim
    $h = $h % 12
    #výpočet polohy hodin
    switch($h)
    {
        0 {$plocha[0, 15] = 'o'}
        1 {$plocha[5, 20] = 'o'}
        2 {$plocha[10, 25] = 'o'}
        3 {$plocha[15, 30] = 'o'}
        4 {$plocha[20, 25] = 'o'}
        5 {$plocha[25, 20] = 'o'}
        6 {$plocha[30, 15] = 'o'}
        7 {$plocha[25, 10] = 'o'}
        8 {$plocha[20, 5] = 'o'}
        9 {$plocha[15, 0] = 'o'}
        10 {$plocha[10, 5] = 'o'}
        11 {$plocha[5, 10] = 'o'}
        12 {$plocha[0, 15] = 'o'}
    }
}

$plocha = New-Object 'object[,]' 31,31

while($true)
{
    cls #vyčištění konzole
    $cas = Get-Date | select Hour, Minute, Second #uložení času do proměnné
    #Write-Host $cas.Hour ":" $cas.Minute ":" $cas.Second #výpis času v obvyklém formátu
    #inicialoizace plochy
    for($i = 0; $i -le 30; $i++)
    {
        for($j = 0; $j -le 30; $j++)
        {
            $plocha[$i,$j] = ' ' #uložení mezery do pole plochy
        }
    }
    #nastavení ciferníku
    $plocha[0,15] = '12'
    $plocha[15,30] = '3'
    $plocha[30,15] = '6'
    $plocha[15,0] = '9'
    $plocha[15,15] = '*'
    #výpočet polohy konců ručiček
    get-hodina -h ($cas.Hour) #ručička zobrazená kolečkem
    get-minuta -m $cas.Minute #ručička zobrazení plusem
    get-sekunda -s $cas.Second #ručička zobrazená hvězdičkou
    #vykreslení plochy
    for($i = 0; $i -le 30; $i++)
    {
        for($j = 0; $j -le 30; $j++)
        {
            Write-Host $plocha[$i,$j] -NoNewline
        }
        Write-Host ""
    }
    
    Start-Sleep -Seconds 1 #uspání procesu na 1 sekundu
}
Posted in: Základy PowerShellu

Funkce v PowerShellu

Dnes po dlouhé době opět píši něco pro ty, kteří chtějí s PowerShellem začínat a již nějak zvládli základní syntaxi. Pro dnešní práci budeme potřebovat nějaký nástroj k psaní skriptů, viz článek.

Jak jsem se již určitě zmínil v některém předešlém článku, skript je posloupnost příkazů, které se vykonají v daném pořadí. Skript může obsahovat, stejně jako program v některém programovacím jazyce, cykly, podmínky a další řídící příkazy, které vedou na opakování nějakého bloku, nebo naopak přeskočení nějakého bloku kódu. Stejně jako v programování, i při psaní skriptů, můžeme potřebovat opakovat nějaký blok, kus kódu, na různých místech skriptu. Prvním nápadem může být, prostě daný blok kódu zkopírovat a vložit jej na požadovaná místa. Toto je nevýhodné, protože se zkopírovaný blok špatně opravuje, rozšiřuje apod. Proto máme lepší řešení, to jsou funkce, které si umíme volat.

Jmenná konvence

Když si vzpomenete na základní syntaxi PowerShellu, příkazy mají základní podobu:
sloveso-PodstatnéJméno
Tento formát pojmenování je potřeba dodržet i u našich funkcí. Mnohé z vás by mohlo napadnou použít vlastní kombinaci jako například:
Vypis-PCinfo
sice syntakticky vyhovíme, ale není to zcela správné. PowerShell definuje rovněž sadu sloves, která máme využívat a uživatelé jazyka je očekávají a očekávají, že budou mít daný význam. Seznam podporovaných sloves vrátí příkaz:

Get-Verb | Sort-Object -Property Verb

Výstup této funkce rovněž uvádím v článku o syntaxi.

Obecná syntaxe

function Sloveso-PodstatneJmeno
{
    #vlastní kód (tělo) funkce
}

Příklad:

function Get-WinInfo
{
    cls
    Write-Host "Základní informace o počítači"
    Write-Host ""
    $info = Get-ComputerInfo | select WindowsProductName, WindowsCurrentVersion, CsModel, CsName
    Write-Host "Název OS      :" $info.WindowsProductName
    Write-Host "Verze jádra OS:" $info.WindowsCurrentVersion
    Write-Host "Model zařízení:" $info.CsModel
    Write-Host "Název zařízení:" $info.CsName
}

Funkci spustíme tak, že napíšeme její jméno, tedy pro náš příklad by volání vypadalo:

Get-WinInfo

Výše uvedená je nejsprostější verze, samozřejmě i základní funkce může mít parametry, případně vracet hodnoty.

Funkce s parametrem

Výše popsaný základ je dobrý, ale pořád se jedná o poměrně hloupé funkce. Výše popsaná syntaxe slouží k deduplikaci kódu a umí modifikovat globální proměnné, vypisovat na obrazovku, komunikovat s uživatelem atd. Bohužel nemůžeme takto definované funkci předat hodnoty ke zpracovaní. Pojďme se podívat na to, jak tento problém řešit.

Obecná syntaxe:

function Sloveso-PodstatneJmeno
{
     param(
       $NázevParametru,
       $NázevDruhéhoVolitelnéhoParametru
       )
     #vlastní kód (tělo) funkce
}

Čárkou se oddělují jednotlivé parametry, tudíž se uvádí pouze tam, kde za parametrem následuje další parametr. Pojďme si uvést příklad funkce s jedním parametrem.

function Get-OSInfo { 
    param (
        $ComputerName
    ) 
    Get-WmiObject -ComputerName $ComputerName -Class Win32_OperatingSystem | select Caption, Version, SystemDrive | Format-Table 
}

Nyní je na čase si ukázat, jak tuto funkci volat. Obecně voláme funkci v podobě:

Sloveso-PodstatneJmeno -NázevParametru hodnota -NázevDruhéhoVolitelnéhoParametru hodnota

Pojďme si to zkonkrétnit na našem příkladu s funkcí Get-OSInfo pro lokální počítač:

Get-OSInfo -ComputerName localhost

Toto vše je krásné, ale občas bychom potřebovali, aby vstup, který nám dává parametr, byl určitého datového typu, například číslo. Pojďme si na příkladu jednoduché kalkulační funkce ukázat, jak určit datový typ, který parametr bude akceptovat.

function Get-Vysledek { 
    param (
        [double]$cislo1,
        [double]$cislo2,
        [switch]$soucet,
        [switch]$soucin,
        [switch]$rozdil,
        [switch]$podil
    ) 
    if($soucet)
    {
        $vysledek = $cislo1 + $cislo2
        Write-Host "Soucet je:" $vysledek
    }
    if($rozdil)
    {
        $vysledek = $cislo1 - $cislo2
        Write-Host "Soucet je:" $vysledek
    }
    if($soucin)
    {
        $vysledek = $cislo1 * $cislo2
        Write-Host "Soucet je:" $vysledek
    }
    if($podil)
    {
        if($cislo2 -gt 0)
        {
            $vysledek = $cislo1 / $cislo2
            Write-Host "Soucet je:" $vysledek
        }
        else
        {
            Write-Host "Nulou nelze dělit"
        }
    }
}

Datový typ, kterého musí daný parametr nabývat, určíme tak, že název datového typu uvedeme do hranatých závorek před požadovaný parametr. Jedinou výjimkou, která funguje pouze u parametrů, nelze ji uplatnit na proměnné v jiných částí kódu je „datový typ“ switch. Pokud je nějaký parametr typu switch, pak daný parametr nabyde hodnoty True (logické jedničky) tím, že dojde k uvedení daného parametru při volání procedury. Úplné informace o datových typech jsou v dokumentaci. Někdy příště, se na vybrané datové typy podíváme. Příklad volání naší ukázkové funkce Get-Vysledek:

Get-Vysledek -cislo1 6.6 -cislo2 3 -podil

Návratová hodnota

Od PowerShell verze 5 nám funkce může vrátit návratovou hodnotu, stejně jako je tomu v běžných programovacích jazycích. Na rozdíl do programovacích jazyků ovšem PowerShell nevrací hodnotu, ale výraz. Na rozdíl od programovacích jazyků u funkce nedefinujeme datový typ návratové hodnoty. Stejně jako v případě běžných programovacích jazyků máme k dispozici příkaz return, jehož syntax je jednoduchá:

return vyraz

Pojďme si to ukázat na příkladu převzatém z dokumentace a lehce upraveném.

function Get-Kalkuace {
    param ($hodnota)

    $hodnota ="Prosím vyčkejte, počítám...`n" #znak `n je zalomení řádku
    $hodnota += 73
    return $hodnota
}

Takovouto funkci lze volat napřímo, nebo její výstup, který vrací příkaz return přiřadit do proměnné. Pojďme si ukázat verzi, kdy hodnotu navrácenou funkcí (v mé modifikaci strinng) přiřadíme do proměnné, kterou rovnou vypíšeme:

Write-Host ($a = Get-Kalkuace 14)

Závěr

Toto je vše z dnešního úvodu do funkcí. Funkce toho umí dalece více, ale o tom až někdy později u tzv. pokročilých funkcí. Funkce jsou klíčovou součástí jazyka PowerShell, protože moduly jazyka PowerShell vždy funkce obsahují a uživatel modulu volá právě funkce v něm obsažené.

Posted in: Základy PowerShellu

Podepisování kódu v PowerShellu

Dnes to bude trochu bezpečnostní téma i když jen tak na půl. Podepisování skriptů rozhodně souvisí s jejich validací a bezpečností. Pokud se někdo pokusí modifikovat podepsaný skript, podpis se stane neplatným a modifikovaný skript se nevykoná, což samo o sobě může působit bezpečně.

Problém je, že jde obcházet ExecutionPolicy, takže jde spustit i nepodepsaný skript a teoreticky i skript s nevalidním podpisem. Na druhou stranu se musí uznat, že je rozhodně bezpečnější skripty podepisovat a pro běh daný podpis vyžadovat, nežli se na to vykašlat, protože tím útočníkovi zvedáme cenu útoku.

Problémem je, kde vzít správný certifikát. Patřičný certifikát lze vystavit pomocí interní certifikační autority v podnikové síti, ale musí být správně nastaven a správně ošetřeno, komu a s jakou platností se certifikát vydává. V domácím prostředí to nelze důvěryhodně udělat.

Nyní již ke skriptu, který umí podepsat zvoleným certifikátem libovolný soubor.

Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert
$cert = Read-host -promt „Zadej Thumbprint podposového certifikátu“
$cesta = Read-Host -promt „Zadej cestu k souboru“
Set-AuthenticodeSignature -FilePath $cesta -Certificate (Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | where Thumbprint -eq  $cert) -TimestampServer http://timestamp.comodoca.com

Časový server doporučuji zaměnit za interní, ale i takto s veřejnou certifikační autoritou půjde podepsat certifikátem od autority interní.

Posted in: Základy PowerShellu

Operátory v PowerSellu

Operátory porovnání

OperátorVýznam
-eqrovnost
-nenerovnost
-ltmenší než
-lemenší nebo rovno
-gevětší nebo rovno
-gtvětší než

Další operátory níže v přehledu

Matematické operátory

OperátorVýznam
-eqrovnost
-nenerovnost
++inkrementace
dekrementace
-notnegace
%modulo
+součet (zřetězení pro texty)
rozdíl
/dělení
*násobení

Přehled operátorů

OperátorVýznamDetail
[]Ohraničení typuJakýkoli znak umístěný uvnitř těchto příloh se považuje za součást názvu doslovného typu. Obsah není vyhodnocen jako výraz.
” “Dvojité uvozovky řetězců 
‘ ‘Jednoduché uvozovky řetězců 
@” “@Přílohy s dvojitým uvozovkami 
@’ ‘@Přílohy s jednoduchými uvozovkami 
{}Script block 
()Vnořený výraz 
@()Pole subvýrazů či hodnot 
$()Subvýraz 
.Operátor přístupu k vlastnostem a funkcím 
::Operátor přístupu ke statickým vlastnostem 
[]Index operator 
[int]Operátor datového typuVíce sousedních operátorů v tomto řádku má vyhodnocení zprava doleva.
-split (unary)Operátor rozdělení (unární)Tyto operátory lze použít jako unární nebo binární operátory. Jejich přednost se liší v závislosti na tom, jak jsou použity.
-join (unary)Operátor sloučení (unary) 
,Oddělovač prvků poleTento operátor je oddělovač prvků pole. Může být použit jako unární nebo binární operátor.
++InkrementaceTyto unární operátory lze použít před nebo po proměnné nebo vlastnosti. Při použití před proměnnou nebo vlastností (jako operátor předpony) je hodnota nejprve zvýšena nebo snížena a výsledek je předán do výrazu, ve kterém je obsažen. Při použití za proměnnou nebo vlastností (jako operátor postfixu) je hodnota předána do výrazu, ve kterém je obsažena, a pak je proměnná nebo vlastnost okamžitě zvýšena nebo snížena.
– –Dekrementace 
NegaceMultiple adjacent operators in this row have a right-to-left evaluation.
-notNot operator 
!Not operator 
-bnotBitový not operator 
..Rozsah 
-fFormat operator 
*Násobení operator 
/Dělení operator 
%Modulo operator 
+Sčítání operator 
Odčítání operator 
-splitRozdělení operator (binary)S výjimkou operátora spojení a operátorů typu (-is, -isnot a –as) má každý z operátorů v tomto řádku variantu bez rozlišování velkých a malých písmen. Varianty citlivé na malá a velká písmena jsou předponovány písmenem c (např. -Ceq) a varianty citlivé na malá a velká písmena jsou předponou i (např. –Ireplace).
-isplit  
-csplit (binary)  
-join (binary)Sloučení operator (binary) 
-isType is operator 
-isnotType is not operator 
-asType as operator 
-eqRovná se 
-ieq  
-ceq  
-neNerovná se 
-ine  
-cne  
-gtVětší než 
-igt  
-cgt  
-geVětší rovno 
-ige  
-cge  
-ltMenší než 
-ilt  
-clt  
-leMenší rovno 
-ile  
-cle  
-likepodobný 
-ilike  
-clike  
-notlikenepodobný 
-inotlike  
-cnotlike  
-matchMatch operator 
-imatch  
-cmatch  
-notmatchNot match operator 
-inotmatch  
-cnotmatch  
-containsobsahuje 
-icontains  
-ccontains  
-notcontainsNeobsahuje operator 
-inotcontains  
-cnotcontains  
-replaceNahrazení operator 
-ireplace  
-creplace  
-bandBitový and operator 
-borBitový or operator 
-bxorBitový exclusive or operator 
-andLogický and operator 
-orLogický or operator 
-xorLogický exclusive or operator 
.Zpřístupnění proměnných a funkcí ze skriptů a modulůUnární operátory, které jsou platné pouze na začátku výrazu, vnořeného výrazu nebo subexprese.
&Volání spustitelného souboru či kódu 
=přiřazeníMultiple adjacent operators in this row have a right-to-left evaluation.
+=Přiřazení přičtením 
-=Přiřazení odečtením 
*=Přiřazení násobením 
/=Přiřazením dělením 
%=Přiřazení modulem 
  • Na pole lze použít operátory sčítání s konstantou (přidá prvek do pole) a násobení konstantou (zduplikuje pole tolikrát, kolikrát uvádí konstanta)
Posted in: Základy PowerShellu

Základní zabezpečení PowerShellu

Zabezpečení Powershellu začíná u zabezpečení operačního systému. Powershell je opravdu dvousečnou zbraní (nejlepším hackerským nástrojem i nejlepším přítelem obránců a správců), nicméně jsou určité postupy, které reálně snižují potenciální rizika spojená s přítomností PowerShellu. Nežli si dále povíme něco o zabezpečení PowerShellu jako takového, pojďme se podávat na základy. Doporučené body pro bezpečnou práci:

  1. Nepracuji pod účtem s admin oprávněním
  2. Nezpouštím pozornost z odemčeného PC
  3. Pravidleně instaluji bezpečnostní aktualizace
  4. Nespouštím skripty z internetu, které jsem celé nezkontroloval a nerozumím jim na 100%
  5. Moduly a další SW instaluji vždy z důvěryhodných zdrojů (oficiální stažení z webu vydavatele, ověření kontrolních součtů a vše pouze legálně)
  6. Všechny úlohy spouštím s minimálním nutným oprávněním
  7. Vždy mám na systému aktivní Firewall a zabezpečení
  8. Využívám komplexní hesla
  9. Dodržuji všechny bezpečnostní doporučení výrobce OS i jednotlivých aplikací
  10. Navštěvuji jen důvěryhodné weby a nic nedávám do výjimek bezpečnostních SW
  11. PowerShellu nedávám oprávnění administrátora, pokud není nezbytné
  12. S právy admina nespouštím skripty z internetu

Dalším doporučením je, aby práce vždy probíhala v aktuální verzi PowerShellu, verze 5.1 není problémem, pokud nechybí bezpečnostní aktualizace. Starší verze PowerShellu by měly být z PC odebrány, a pokud je potřebuje nějaký SW, mělo by být jejich spouštění logováno a omezeno na daný SW např. pomocí Applocker.

Execution Policy

V PowerShellu od verze 3 najdeme tzv. Execution Policy, což je nastavení, které říká, jaké skripty je povoleno spouštět. Jde o první obrannou linii PowerShellu. Ve výchozím stavu je na Windows Serveru hodnota RemoteSigned, na systémech Windows 10 je výchozí hodnotou Restricted. Co které nastavení znamená?

  • AllSigned Vyžaduje, aby všechny skripty a konfigurační soubory byly podepsány důvěryhodným vydavatelem, včetně skriptů napsaných v místním počítači.
  • Bypass Spustí jakýkoliv skript bez varování a výzev.
  • Default Nastaví na Windows serveru RemoteSigned a na Windows Restricted
  • RemoteSigned Všechny skripty a konfigurační soubory stažené z internetu musí být podepsány důvěryhodným vydavatelem.
  • Restricted Nenačte konfigurační soubory a nespustí skripty.
  • Undefined Pro obor nejsou nastaveny žádné zásady provádění. Odebere přiřazenou zásadu provádění z oboru, který není nastaven zásadami skupiny. Pokud je zásada provádění ve všech oborech Nedefinovaná, je účinná politika Restricted
  • Unrestricted Počínaje PowerShell 6.0 se jedná o výchozí zásadu provádění pro počítače, které nejsou Windows, a nelze ji změnit. Načte všechny konfigurační soubory a spustí všechny skripty. Pokud spustíte nepodepsaný skript, který byl stažen z internetu, budete před spuštěním vyzváni k povolení.

Jak zjistím aktuálně nastavenou politiku ve svém PowerShelu? Odpovědí je příkaz: 

Get-ExecutionPolicy -List

Jak nastavím Execution policy? Odpovědí je příkaz: 

Set-ExecutionPolicy

Execution policy jde nastavovat granuárně na různé úrovně. Pokud spustíme příkaz Set-ExecutionPolicy nastavujeme pravidla pouze pro daný proces PowerShellu. Pro nastavení pravidel například pro uživatele, počítač, nebo všechny uživatele složí přepínač: -Scope

Pro nastavení pravidel máme následující oblasti:

  • MachinePolicy Pomocí zásad skupiny aplikuje nastavení na všechny uživatele daného PC.
  • UserPolicy Nastavení platí lokálně pro daného uživatele.
  • Process Nastavení platí pouze pro dané okno PowerShellu.
  • CurrentUser Nastavení platí pouze pro daného uživatele (může s ním cestovat)
  • LocalMachine Mění výchozí nastavení pro všechny uživatele na daném PC.

Pokud nechcete, aby se v PowerShellu objevila výzva pro potvrzení nastavení použijte přepínač: -Force Pokud chci nastavit, že v aktuálně spuštěné instanci PowerShellu spustím podepsaný skript, využiji příklad níže.

Set-ExecutionPolicy AllSigned -Scope Process -Force

Obcházení Execution Policy

Obejít Execution Policy není nic těžkého a nejsou na to zapotřebí žádná oprávnění, člověk může být klidně uživatelem typu host. Níže si popíšeme 3 nejběžnější způsoby obcházení:

  1. Příkaz Set-ExecutionPolicy, kterým nastavím policy na požadovanou hodnotu (nelze provést uvnitř skriptu)
  2. V Průzkumníku kliknout na soubor skriptu pravým tlačítkem myši a zvolit „Run with PowerShell“
  3. Spustit si PowerShell ISE, skript v něm otevřít, celý označit pomocí CTRL+A a zvolit RUN SELECTION

Jak je vidět, tak Execution policy jsou pouze nástrojem, který má zabránit chybám z nepozornosti a nechtěnému spuštění kódu, nikoliv reálnou bezpečnostní technologií.

Constrained Language mode

Jak již bylo zmíněno PowerShell stojí na .NET Frameworku a z toho plyne, že umí volat .NET příkazy. Tato zkutečnost je podstatnou, protože dokáže zjednodušit některé úkony správy, ale zároveň je volání .NET příkazů alfou a omegou většiny škodlivých kódů v PowerShellu. Pro hackery je často samotný PowerShell skvělý pomocník, ale protože je určen pro jiné odvětví, je zároveň limitujícím faktorem útoku. Právě technologie Language mode, má za úkol omezit volání .NET příkazů, čímž eliminuje spouštění řady generických skriptů, které hackeři užívají.

Nastavení

Obrana proti školdivému kódu v PowerShellu je poměrně jednoduchá, stačí užívat Constrained Language mode, který značně omezí možnosti, které útočník má. Toto nastavení umožňuje zakázat volání .NET příkazů, a nastavit i další omezení omezení na vlastní PowerShell.

Zjištění jazykového režimu provedeme příkazem:

$ExecutionContext.SessionState.LanguageMode

Základní nastavení do omezeného režimu provedeme příkazem:

$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"

Pokud toto nastavení provádíme příkazy, pak není možné jej z omezeného režimu vrátit zpět na plný režim v rámci stejného okna PowerShellu. Stejně jako u Execution policy zde nejsou zapotřebí žádná správcovská práva. Krom omezeného jazykového režimu existují i další, pojďme se tedy podívat na úplný výčet.

  • Full Language – Režim jazyka FullLanguage umožňuje všechny jazykové prvky v relaci. FullLanguage je výchozí jazykový režim
  • Restricted Language – V jazykovém režimu RestrictedLanguage mohou uživatelé používat příkazy (rutiny, funkce, příkazy CIM a pracovní postupy), ale není dovoleno používat bloky skriptů. Jsou omezeny i proměnné a operátory. Bližší informace: Get-Help about_Language_Modes
  • No Language – V jazykovém režimu NoLanguage mohou uživatelé spouštět příkazy, ale nemohou používat žádné jazykové prvky.
  • Constrained Language – povoluje všechny prvky jazyka PowerShell, ale neumožňuje volání .NET a vybraných systémových rutin.

Na rozdíl od Execution policy lze z pozice správce vynutit nastavení na úrovni počítače, jež pevně stavuje jazykový režim a uživatelé jej nemohou změnit. Toto pevné nastavení se provádí pomocí systémové proměnné __PSLockDownPolicy, což lze udělat příkazem .NET:

[Environment]::SetEnvironmentVariable('__PSLockdownPolicy', '4', 'Machine')

Další alternativou je využití systémového GIU:

Poslední možností, pro správce systémů tou nejlepší, je využití Group Policy, resp. Local Policy.

Blokované soubory z internetu

Soubory konfigurace, skriptů a nápovědy jsou ve výchozím stavu po stažení z internetu blokovány a PowerShell bude explicitně vyžadovat potvrzení, že má takový soubor opravdu spustit i když politiky spuštění povolují. Každý soubor, který je stažen z internetu má o této skutečnosti informující příznak v NTFS (alternativní datový proud Zone.Identifier, který má hodnotu „3“), proto PowerShell zná jeho původ. Tuto informaci neobsahují jen soubory pro PowerShell, ale také ostatní stažené soubory, jako jsou Office, PDF, MSI, EXE apod. Výjimku tvoří soubory stažené v rámci modulu příkazem Install-Module z PowerShell galerie nebo jiného důvěryhodného repozitáře, zde si je PowerShell v rámci procesu instalace odblokuje.

Pro odblokování souborů slouží příkaz Unblock-File, který má povinný parametr -Path, nicméně dokáže pracovat i s pozičním zadáním hodnoty parametru, tudíž bez uvedení jména parametru, jen zadáním jeho hodnoty.

Následující příkaz odblokuje všechny soubory umístěné v adresáři C:\Users\Public\Dokuments:

Get-ChildItem C:\Users\Public\Dokuments\*.* | Unblock-File

Výše zmíněný příkaz odblokuje všechny soubory, tedy i soubory Office, pdf či instalátory. Jeho užití tedy může leckdy ušetřit i klikání na Povolit úpravy v sadě Office na souborech stažených z Moodlu nebo SISu. Více o tomto příkazu v jeho dokumentaci.

Posted in: Základy PowerShellu

Exporty

PowerShell, respektive jeho moduly, umí opravdu rozličné množství exportů a výpisů, ale zde mi jde o export výstupu libovolného příkazu, nikoliv například certifikátu. Nejjednodušší možností je přesměrování konzole do souboru, což se provede příkazem out-file. Například seznam běžících procesů do souboru procesy.txt na ploše uložím takto:

get-process | Out-File $env:USERPROFILE\Desktop\procesy.txt

Lepší je využít export o nějakého dále zpracovatelného formátu, jako je CSV nebo XML.

Export do CSV

K exportu výstupu do CSV slouží příkaz Export-CSV, modifikací příkladu výše pro výstupní formát CSV je následující příkaz:

get-process | Export-Csv $env:USERPROFILE\Desktop\procesy.csv -Encoding UTF8 -Delimiter ";" -NoTypeInformation

Pojďme si to rozebrat. CSV jako výchozí oddělovač využívá čárku, ale v českém jazykovém prostředí se běžně využívá středník, aby se správně uložila desetinná čísla. Parametr Delimiter umí nastavit, právě znak, který je oddělovačem, pokud parametr nevyužijeme, bude SCV využívat čárku jako oddělovač. Dalším parametrem je Encoding, který udává, jaké kódování se užije, bez uvedení přebírá výchozí kódování OS, doporučuji využívat UFT-8, protože je dobře přenosné mezi aplikacemi i systémy. Posledním je důležitý parametr NoTypeInformation, který do výstupu nezapíše na první řádek informace o typu objektu (TypeName). Pokud tento parametr zapomeneme, na první řádek se zapíše typ objektu, v našem příkladu by to bylo: System.Diagnostics.Process Tento řádek pak ztěžuje zpracování souboru dále např. v Excelu, kdy musí být při importu odebrán. Kde naopak je dobré jej mít, je následné zpracování pomocí PowerShellu.

Export do XML

Zde je situace dost podobná jako v případě CSV. Náš příklad s procesy by vypadal následovně:

Get-Process | Export-Clixml -Path $env:USERPROFILE\Desktop\procesy.xml -Encoding Unicode

Export do JSON

Dnes velmi populární datový formát JSON má rovněž v PowerShellu podporu. Bohužel zde není přímý příkaz exportu, ale postup je takový, že výstup se zkonvertuje pomocí příkazu ConvertTo-Json do objektu, který je vnitřně JSON a ten se uloží do souboru pomocí Out-File. Náš příklad s procesy by tedy vypadal následovně:

Get-Process | ConvertTo-Json | Out-File -FilePath $env:USERPROFILE\Desktop\procesy.json

Konverze do HTLM

Jedním z dobrý „exportů“ větších tabulek a dlouhých výpisů může být HTML, které je dobře čitelné po zobrazení v prohlížeči a rovněž prezentovatelné další lidem. I zde využíváme, podobně jako u JSON, konverzi a následné uložení do souboru. Pokud bychom využili samotné ConvertTo-HTML, dostaneme výstup, který je vidět na následujícím obrázku.

Náš příklad by pro uložení v podobě tabulky webové stránky vypadal následovně:

Get-Process | ConvertTo-Html | Out-File $env:USERPROFILE\Desktop\procesy.html

Možností pro ConvertTo je ještě o něco více a využívají se běžně uvnitř samotného kódu skriptů, ale již ne jako export v rozšířeném formátu. O těchto možnostech zase někdy jindy.

Back to Top