Profile – część szósta.

W ostatniej części cyklu chcę skupić się na bezpieczeństwie profili. To, co jest wielką zaletą profili, może się jednocześnie stać potencjalnym źródłem niebezpieczeństwa. Profil jest domyślnie uruchamiany razem z hostem, do którego jest przypisany. Oprócz tego większość hostów uruchamia profil przypisany do danego użytkownika (lub do wszystkich użytkowników) niezależnie od hosta, którego aktualnie używamy. Bez tego nie miałyby one wielkiego sensu. I o ile profile dotyczące wszystkich użytkowników wymagają pełnych uprawnień na danym komputerze, o tyle profil bieżącego użytkownika nie jest w żaden sposób zabezpieczony – z punktu widzenia systemu niczym nie różni się od wszystkich innych plików przechowywanych w folderze z dokumentami. W dodatku jego nazwa jest stała (nie ma sposobu, by nadpisać domyślną ścieżkę). To wszystko czyni profil użytkownika idealnym punktem, w który można by uderzyć atakiem ‘wstrzykującym’ kod. Prosty przykład:

<# To będzie wyglądało jak komentarz blokowy dla niewprawnego oka
    #>Import-Module ActiveDirectory                        
    Get-ADUser -Filter * | Remove-ADUser -Force <#            
    A to drugi komentarz, który wyglada jak dokończenie pierwszego.            
#>

Raczej nie chciałbym, by ktoś taki „komentarz” dopisał na końcu profilu, uruchamianego na koncie z uprawnieniami domain admin. Na szczęście nie wymaga wiele pracy zabezpieczenie się na taką ewentualność. Lekarstwem na to potencjalne zagrożenie jest podpis elektroniczny.

Złoty środek.

Podpis elektroniczny wymaga nieco pracy. Trzeba go stworzyć lub zakupić. Każda modyfikacja skrypty podpisanego elektronicznie wymaga ponownego podpisania skryptu. Czy chcielibyśmy wobec tego pracować cały czas z Execution Policy AllSigned, nawet jeśli świadomie uruchamiamy skrypty, zabezpieczone bardziej niż zawartość naszego folderu z dokumentami i przez to nie narażonymi na ‘wstrzykiwanie’ kodu? Z drugiej strony, jeśli chcemy mieć pewność, że nikt nie będzie mógł w prosty sposób zmodyfikować naszych profili, to nie specjalnie mamy wybór. Długo nie mogłem znaleźć wyjścia gdzie i wilk byłby syty, i owca cała… Z pomocą, jak zawsze, przyszła wspólnota. Glenn Sizemore jakiś czas temu opisał metodę, która pozwala skorzystać zarówno z dobrodziejstwa jakie daje wymuszenie podpisu w profilach a jednocześnie swobodną pracę „interaktywną”, nie zmuszającą do podpisywania każdego skryptu, z którym przyjdzie nam pracować (czy raczej – nad którym przyjdzie nam pracować). Schematycznie można to przedstawić w ten sposób:

image

Zmiana ExecutionPolicy jest możliwa z tej prostej przyczyny, że komenda

Set-ExecutionPolicy, o czym wiele osób zapomina, ma parametr Scope. Po pierwsze zawężenie Scope do CurrentUser pozwala zmodyfikować ustawienia bieżącego użytkownika, nawet jeśli nie ma on uprawnień administratora. Po drugie, ustawienie Scope  na pojedyńczy Process pozwala ograniczyć zmianę tylko do bieżącego procesu, co otwiera furtkę dla tego właśnie rozwiązania. Pełna komenda ma postać:

$Parametry = @{            
    Scope = 'Process'            
    ExecutionPolicy = 'RemoteSigned'            
    Force = $True            
}            
Set-ExecutionPolicy @Parametry

Implementacja

Zdobycie podpisu elektronicznego to temat na osobny artykuł, pozwolę więc sobie pominąć dziś ten etap. Zakładam, że podpis takowy już posiadamy. Druga rzecz, jaką powinniśmy zrobić, to zmodyfikować ExecutionPolicy (ustawić je na AllSigned). Kolejna – to oczywiście podpisanie wszystkich posiadanych profili. Ostatnia: zmodyfikowanie profili dla każdego z używanych hostów w taki sposób, by komenda zmieniająca ExecutionPolicy się tam znalazła. Naturalnie, można umieścić ją na samym końcu, ale często może się okazać, że spowoduje to znaczne problemy przy ładowaniu modułów. Można więc albo również je podpisać (czasami wymaga to sporo pracy), lub też przesunąć komendy ładujące moduły nieco dalej. Wydaje mi się, że nie ma to wielkiego wpływu: i tak dopisanie modułu będzie wymagało ponownego podpisania całego profilu, nie powinno więc stanowić zagrożenia. Osobiście, dla ułatwienia sobie pracy, dodałem jeszcze do profilu prostą funkcję, którą aktualizuję podpis elektroniczny wybranego skryptu, przy pomocy wykorzystywanego przeze mnie certyfikatu:

function Update-scriptSignature {            
            
<#
    .Synopsis
        Function to update script SIG if not yet valid/ signed.
    .Description
        Function to simplify work with Set-AuthenticodeSignature. 
        Using Get-AuthenticodeSignature to verify script first.
        By default it will use first CodeSigning cert found on PC.
        Primary purpose is to update certificate for profile.ps1.
    .Example
        $profile | Update-ScriptSignature
        Updates signature for profile for current host.
    .Example
        Update-ScriptSignature
        Without any parameter it will update certificate for 
        $profile.CurrentUserAllHosts
    .Inputs
        System.String, System.IO.FileInfo
    .Outputs
        System.Management.Automation.Signature, 
        
#>            
            
[CmdletBinding()]            
param (            
    # Path to file that will be signed            
    [Parameter(ValueFromPipeline=$true,            
        ValueFromPipelineByPropertyName = $true)]            
    [Alias('Path','FullName')]            
    [string]$FilePath = $profile.CurrentUserAllHosts,            
    # Certificate object that will be used to sign a script            
    $Certificate = $(Get-ChildItem cert:\CurrentUser\My -CodeSigning |            
        Select-Object -First 1)            
)            
            
begin {            
    $Arguments = @{            
        Certificate = $Certificate            
        ErrorAction = 'Stop'            
    }            
}            
            
process {            
    Write-Verbose "Updating certificate for file: $FilePath"            
    try {            
        $Signature = Get-AuthenticodeSignature -FilePath $FilePath            
        if (!$Signature -or $Signature.Status -ne 'Valid') {            
            $Arguments.FilePath = $FilePath            
            Set-AuthenticodeSignature @Arguments            
        } else {            
            Write-Host $FilePath "has already a valid cerificate"            
        }            
    } catch {            
        Write-Verbose "Errors while processing file: $FilePath"            
        $_ | Format-List * -Force            
    }            
}            
            
}            

Używam jej w zasadzie głównie wtedy, gdy potrzebuję zmodyfikować swój profil. Jeśli zapomnę z niej skorzystać, PowerShell zachowa się tak, jak zachowa się gdy coś zechce zmodyfikować mój profil i dopisać kilka “niewinnych” linii:

AllSignedverRemoteSigned

Jak widać po prawej, gdy wymusiłem nieco mniej restrykcyjne ExecutionPolicy (powershell.exe –ExecutionPolicy RemoteSigned) mój “groźny” kod został wykonany. W normalnych warunkach – nic takiego nie miało miejsca. Myślę, że to dobry sposób, by ustrzec się przed najprostszymi próbami wykorzystania profili przeciw nam. Sam korzystam z tego rozwiązania i zapewniam, nie jest bardzo uciążliwe, a sen mam dzięki temu nieco spokojniejszy… 😉 I tym miłym akcentem kończę cykl o profilach. Następny post będzie poświęcony enumeratorom w PowerShellu.

~ - autor: Bartek Bielawski w dniu 14 lipca, 2011.

Jedna odpowiedź to “Profile – część szósta.”

  1. […] Inną sprawą jest to, że powinien skorzystać z porad Bartka na temat zabezpieczania pliku profilu. […]

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s

 
%d blogerów lubi to: