PowerShell Remoting

PowerShell Remoting is a comprehensive built-in remoting subsystem that is a part of Microsoft's native Windows management framework (WMF) and Windows remote management (WinRM). This feature allows you to handle most remoting tasks in any configuration you might encounter by creating a remote PowerShell session to Windows hosts and executing commands in the created session. The integration includes out-of-the-box commands which supports agentless forensics for remote hosts.

Utilities · Powershell Remoting

Details

IDPowerShell Remoting
ProviderMicrosoft
CategoryUtilities
From Version6.0.0
Docker Imagedemisto/powershell-ubuntu:7.5.0.10049856
Supported ModulesAgentix XSIAM

README

PowerShell Remoting is a comprehensive built-in remoting subsystem that is a part of Microsoft’s native Windows management framework (WMF) and Windows remote management (WinRM).
This feature allows you to handle most remoting tasks in any configuration you might encounter by creating a remote PowerShell session to Windows hosts and executing commands in the created session.
The integration includes out-of-the-box commands which supports agentless forensics for remote hosts.

For more information about setting up PowerShell Remoting, see the PowerShell Remoting - Configuration guide.

Configure PowerShell Remoting in Cortex

Parameter Description Required
Domain Fully Qualified Domain Name suffix to be added to the hostname. For example mydomain.local False
DNS The IP address of the DNS server the integration will use to resolve your hosts. False
Username Username in the target machine. This can be a local or domain user with administrative privileges True
Password   True
Test Host Hostname or IP address to use as a test for the integration connectivity. False
Authentication Method   True
Trust any certificate (not secure)   False
Use SSL (HTTPS)   False

Configuration Notes

Please note that in order for the integration to function properly, Basic Authentication is required to be enabled for the target host. This is due to the library relying on Basic Auth headers to pass an authentication token even when MFA is enabled.

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

ps-remote-command


Executes remote PowerShell commands on a single host.

Base Command

ps-remote-command

Input

Argument Name Description Required
host A single hostname or IP address on which to run the command. Required
command PowerShell commands (can be single or multiple in order of execution) to run on the target machine. Required

Context Output

Path Type Description
PsRemote.Host string The host on which the command was invoked.
PsRemote.FQDN string The Fully Qualified Domain Name of the host on which the command was invoked.
PsRemote.CommandResult list The result of the command run from the target machine.
PsRemote.CommandName string The command sent to the target machine, used as an ID of that query.
PsRemote.UTCTime string Time the command finished execution in UTC time.

Command Example

!ps-remote-command host=XSOAR-XSOAR command=whoami

Context Example

{
    "PsRemote": {
        "UTCTime": "2021-07-18T15:46:07.2006094+00:00", 
        "FQDN": "XSOAR-XSOAR.winrm.local", 
        "Host": "XSOAR-XSOAR", 
        "CommandName": "whoami", 
        "CommandResult": [
            "winrm\\administrator\n"
        ]
    }
}

Human Readable Output

Result for PowerShell Remote Command: whoami
winrm\administrator

ps-remote-download-file


Downloads a file from the remote endpoint.

Base Command

ps-remote-download-file

Input

Argument Name Description Required
host Hostname or the IP address on which to run the command. Required
path The path of the file to download. Required
zip_file Whether to compress the file. Possible values: “true” and “false”. Default is “false”. Possible values are: true, false. Default is false. Optional
check_hash Whether to compare the value of the original file with the downloaded file and return an error if any differences are found. Possible values: “true” and “false”. Default is “false”. Possible values are: true, false. Default is false. Optional
host_prefix Whether to use the host as a prefix for the name of the downloaded version of the file. Possible values: “true” and “false”. Default is “false”. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
PsRemoteDownloadedFile.Host string The host from which the file was downloaded.
PsRemoteDownloadedFile.FQDN string The Fully Qualified Domain Name of the host from which the file was downloaded.
PsRemoteDownloadedFile.FileName String File name.
PsRemoteDownloadedFile.FileSize Number File size.
PsRemoteDownloadedFile.FileMD5 String The MD5 hash of the file.
PsRemoteDownloadedFile.FileSHA1 String The SHA1 hash of the file.
PsRemoteDownloadedFile.FileSHA256 String The SHA256 hash of the file.
PsRemoteDownloadedFile.FileExtension String The extension of the file.
File.Size Number The size of the file in bytes.
File.SHA1 String The SHA1 hash of the file.
File.SHA256 String The SHA256 hash of the file.
File.SHA512 String The SHA512 hash of the file.
File.Name String The full file name.
File.SSDeep String The ssdeep hash of the file.
File.EntryID String The ID for locating the file in the War Room.
File.Info String The file information.
File.Type String The file type.
File.MD5 String The MD5 hash of the file.
File.Extension String The file extension, for example: “txt”.

Command Example

!ps-remote-download-file host="XSOAR-XSOAR" path="c:\\XSOAR-XSOAR.etl" zip_file="true" check_hash="true" host_prefix="true"

ps-remote-upload-file


Uploads a file to the remote endpoint.

Base Command

ps-remote-upload-file

Input

Argument Name Description Required
host A single hostname or IP address on which to run the command. Required
path The path of the file to upload. Required
entry_id The file entry ID to upload. Required
zip_file Whether to compress the file before upload. Possible values: “true” and “false”. Default is “false”. Possible values are: true, false. Default is false. Optional
check_hash Whether to compare the values of the original file and uploaded file and return an error if any differences are found. Possible values: “true” and “false”. Default is “false”. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
PsRemoteUploadedFile.FileName String File name.
PsRemoteUploadedFile.FilePath String File path in the remote server.
PsRemoteUploadedFile.FileSize Number File size.
PsRemoteUploadedFile.FileMD5 String The MD5 hash of the file.
PsRemoteUploadedFile.FileSHA1 String The SHA1 hash of the file.
PsRemoteUploadedFile.FileSHA256 String The SHA256 hash of the file.
PsRemoteUploadedFile.FileExtension String The extension of the file.
PsRemoteUploadedFile.Host string The host to which the file was uploaded.
PsRemote.FQDN string The Fully Qualified Domain Name of the host the file was uploaded to.

Command Example

!ps-remote-upload-file entry_id=105@1d0796aa-dde9-4f18-8f04-bbe92434ba81 host="XSOAR-XSOAR" path="c:\\tmpetl.etl"

Human Readable Output

File 1d0796aa-dde9-4f18-8f04-bbe92434ba81_105@1d0796aa-dde9-4f18-8f04-bbe92434ba81 was uploaded successfully as: c:\tmpetl.etl

ps-remote-etl-create-start


This command starts the recording of an ETL file on a Windows endpoint. An ETL file is just like a PCAP file which is created by the Windows Netsh command.

Base Command

ps-remote-etl-create-start

Input

Argument Name Description Required
host A single hostname or IP address on which to run the command. Required
etl_path The path on the hostname on which to create the ETL file. For example c:\temp\myhost.etl. Default is “C:\Users\<username>\AppData\Local\Temp\NetTraces\NetTrace.etl”. Required
etl_filter The filter to apply when creating the ETL file. For example IPv4.Address=1.1.1.1 to capture traffic just from the 1.1.1.1 IP address. If no filter is specified all traffic will be recorded. For more examples, see: https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/jj129382(v=ws.11)#using-filters-to-limit-etl-trace-file-details. Optional
etl_max_size The maximum file size for the ETL. Once the file has reached this size, the capture will stop. For example 10MB. The default size is 10MB. Default is 10. Optional
overwrite Whether to overwrite the file in the path. Possible values: “yes” and “no”. Default is “no”. Possible values are: yes, no. Default is no. Optional

Context Output

Path Type Description
PsRemote.CommandResult string The start ETL command results.
PsRemote.EtlFileName string The name of the ETL file.
PsRemote.EtlFilePath unknown The path and name of the ETL file.
PsRemote.CommandName string The name of the command that ran on the host.
PsRemote.FQDN string The Full Qualified Domain Name of the host.
PsRemote.Host string The name of the host.

Command Example

!ps-remote-etl-create-stop host="XSOAR-XSOAR"

Human Readable Output

Trace configuration: ——————————————————————- Status: Running Trace File: C:\XSOAR-XSOAR__a.etl Append: Off Circular: On Max Size: 10 MB Report: Off

Context Example

{
    "PsRemote": {
        "CommandResult": [
            "Trace configuration:\n", 
            "-------------------------------------------------------------------\n", 
            "Status:             Running\n", 
            "Trace File:         C:\\XSOAR-XSOAR__a.etl\n", 
            "Append:             Off\n", 
            "Circular:           On\n", 
            "Max Size:           10 MB\n", 
            "Report:             Off\n"
        ], 
        "FQDN": "XSOAR-XSOAR.winrm.local", 
        "EtlFilePath": "c:\\XSOAR-XSOAR__a.etl", 
        "UTCTime": "2021-07-18T15:58:08.2528721+00:00", 
        "Host": "XSOAR-XSOAR", 
        "CommandName": "netsh trace start capture=yes traceFile=c:\\XSOAR-XSOAR__a.etl maxsize=10 overwrite=no ", 
        "EtlFileName": "XSOAR-XSOAR__a.etl"
    }
}

ps-remote-etl-create-stop


Ends the recording of an ETL file on a Windows endpoint.

Base Command

ps-remote-etl-create-stop

Input

Argument Name Description Required
host A single Hostname or IP address on which to run the command. Required

Context Output

Path Type Description
PsRemote.CommandResult string The stop ETL command results.
PsRemote.CommandName string The name of the command that ran on the host.
PsRemote.EtlFileName string The name of the ETL file.
PsRemote.EtlFilePath unknown The path and name of the ETL file.
PsRemote.Host string The host the command was invoked on.
PsRemote.FQDN string The Fully Qualified Domain Name of the host on which the command was invoked.

Command Example

!ps-remote-etl-create-stop host="XSOAR-XSOAR"

Human Readable Output

Correlating traces … done Merging traces … done Generating data collection … done The trace file and additional troubleshooting information have been compiled as “c:\XSOAR-XSOAR__a.cab”. File location = c:\XSOAR-XSOAR__a.etl Tracing session was successfully stopped.

Context Example

{
    "PsRemote": {
        "CommandResult": "Correlating traces ... done Merging traces ... done Generating data collection ... done The trace file and additional troubleshooting information have been compiled as \"c:\\XSOAR-XSOAR__a.cab\". File location = c:\\XSOAR-XSOAR__a.etl Tracing session was successfully stopped. ", 
        "FQDN": "XSOAR-XSOAR.winrm.local", 
        "EtlFilePath": "c:\\XSOAR-XSOAR__a.etl", 
        "Host": "XSOAR-XSOAR", 
        "CommandName": "netsh trace stop", 
        "EtlFileName": "XSOAR-XSOAR__a.etl"
    }
}

ps-remote-export-registry


Exports the specified registry of hive to a file.

Base Command

ps-remote-export-registry

Input

Argument Name Description Required
host A single hostname or IP address on which to run the command. Required
reg_key_hive The registry key or hive to export. For example, reg_key_hive=HKEY_LOCAL_MACHINE. If the user specifies “all”, the entire registry will be exported. Default is “all”. Default is all. Optional
file_path The path and name on the Windows host where the registry file will be created. For example, file_path=c:\hklm.reg. Required

Context Output

Path Type Description
PsRemote.CommandResult string The command results in the remote host - regedit for all or reg export.
PsRemote.RegistryFilePath string The path and name of the registry file.
PsRemote.RegistryFileName string The name of the registry file.
PsRemote.Command.Name string The name of the command that ran on the host.
PsRemote.Fqdn string The Full Qualified Domain Name of the host.
PsRemote.Host string The host on which the command was invoked.
PsRemote.FQDN string The Fully Qualified Domain Name of the host on which the command was invoked.

Command Example

!ps-remote-export-registry host="XSOAR-XSOAR" file_path="c:\\XSOAR-XSOAR__a.reg"

Human Readable Output

Ran Export Registry.
Registry file expected path: c:\XSOAR-XSOAR__a.reg

Context Example

{
    "PsRemote": {
        "RegistryFileName": "XSOAR-XSOAR__a.reg", 
        "RegistryFilePath": "c:\\XSOAR-XSOAR__a.reg", 
        "FQDN": "XSOAR-XSOAR.winrm.local", 
        "CommandName": "regedit /e c:\\XSOAR-XSOAR__a.reg", 
        "Host": "XSOAR-XSOAR", 
        "CommandResult": null
    }
}

ps-remote-export-mft


Extracts the master file table from the volume.

Base Command

ps-remote-export-mft

Input

Argument Name Description Required
host A single hostname or IP address on which to run the command. Required
volume The volume from which to retrieve its master file table. Optional
output_path The path in which the MFT file is to be created. Optional

Context Output

Path Type Description
PsRemote.ExportMFT unknown The extract master file table command result.
PsRemote.Host string The host on which the command was invoked.
PsRemote.FQDN string The Fully Qualified Domain Name of the host on which the command was invoked.

Command Example

!ps-remote-export-mft host="XSOAR-XSOAR" volume=C

Human Readable Output

MFT Export results

ComputerName MFT File MFT Size MFT Volume NetworkPath PSComputerName PSShowComputerName RunspaceId
XSOAR-XSOAR C:\Users\ADMINI~1\AppData\Local\Temp\zzthh5sh.hl2 222 MB C \\XSOAR-XSOAR\C$\Users\ADMINI~1\AppData\Local\Temp\zzthh5sh.hl2 XSOAR-XSOAR.winrm.local true {“value”:”58aa1f39-f86d-4f18-978a-c9257295df49”,”Guid”:”58aa1f39-f86d-4f18-978a-c9257295df49”}

Context Example

{
    "PsRemote": {
        "ExportMFT": {
            "ComputerName": "XSOAR-XSOAR", 
            "MFT File": "C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\zzthh5sh.hl2", 
            "MFT Size": "222 MB", 
            "PSComputerName": "XSOAR-XSOAR.winrm.local", 
            "NetworkPath": "\\\\XSOAR-XSOAR\\C$\\Users\\ADMINI~1\\AppData\\Local\\Temp\\zzthh5sh.hl2", 
            "MFT Volume": "C", 
            "RunspaceId": "58aa1f39-f86d-4f18-978a-c9257295df49", 
            "PSShowComputerName": true
        }, 
        "Host": "XSOAR-XSOAR", 
        "FQDN": "XSOAR-XSOAR.winrm.local"
    }
}

Configuration parameters

  • domain — Domain
  • dns — DNS
  • credentials — Username (required)
  • host — Test Host
  • auth_method — Authentication Method (required)
  • insecure — Trust any certificate (not secure)
  • ssl — Use SSL (HTTPS)

Commands (7)

  • ps-remote-command

    Executes remote PowerShell commands on a single host.

  • ps-remote-download-file

    Downloads a file from the remote endpoint.

  • ps-remote-etl-create-start

    This command starts the recording of an ETL file on a Windows endpoint. An ETL file is just like a PCAP file which is created by the Windows Netsh command.

  • ps-remote-etl-create-stop

    Ends the recording of an ETL file on a Windows endpoint.

  • ps-remote-export-mft

    Extracts the master file table from the volume.

  • ps-remote-export-registry

    Exports the specified registry of hive to a file.

  • ps-remote-upload-file

    Uploads a file to the remote endpoint.

. $PSScriptRoot\CommonServerPowerShell.ps1

# remove progress messages such as [oo  ] - used to hide archiving operations
$global:ProgressPreference = 'SilentlyContinue'

$script:INTEGRATION_NAME = "PowerShell Remoting"
$script:COMMAND_PREFIX = "ps-remote"
$script:INTEGRATION_ENTRY_CONTEXT = "PsRemote"
$script:INSECURE_WARNING = "Unix does not currently support CA or CN checks."
$script:ValidIpAddressRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"

#### HELPER FUNCTIONS ####

function CreateNewSession {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Scope='Function')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUsernameAndPasswordParams', '', Scope='Function')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Scope = 'Function')]
    param([string]$fqdn, [string]$username, [string]$password, [bool]$insecure, [bool]$ssl, [string]$auth_method)

    $credential = ConvertTo-SecureString "$password" -AsPlainText -Force
    $ps_credential = New-Object System.Management.Automation.PSCredential($username, $credential)

    $session_option_params = @{
        "SkipCACheck" = $insecure
        "SkipCNCheck" = $insecure
    }
    $session_options = New-PSSessionOption @session_option_params

    $sessions_params = @{
        "ComputerName" = $fqdn
        "Credential" = $ps_credential
        "SessionOption" = $session_options
        "Authentication" = $auth_method
        "ErrorAction" = "Stop"
        "WarningAction" = "SilentlyContinue"
        "UseSSL" = $ssl
    }
    try
    {
        $session = New-PSSession @sessions_params
    }
    catch
    {
        if ($_.Exception.Message -Match $script:INSECURE_WARNING)
        {
            throw "HTTPS certificate check isn't currently supported. Please enable 'Trust any certificate' or disable 'Use SSL'"
        }
        throw $_.Exception.Message
    }
    return $session
    <#
        .DESCRIPTION
        Creates new pssession using Negotiate authentication.

        .PARAMETER fqdn
        Fully qualified domain name (FQDN) to connect.

        .PARAMETER username
        Username is the name of a system user.

        .PARAMETER password
        Password of the system user.

        .PARAMETER insecure
        Wheter to trust any TLS/SSL Certificate) or not.

        .EXAMPLE
        CreateNewSession("myhost.example.com", "user", "pass")

        .OUTPUTS
        PSSession - PSSession object.

        .LINK
        https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/new-pssession?view=powershell-7
    #>
}

class RemotingClient
{
    [string]$host
    [string]$fqdn
    [string]$username
    [string]$password
    [string]$domain
    [string]$dns
    [string]$auth_method
    [psobject]$session
    [bool]$insecure
    [bool]$ssl
    [bool]$proxy

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Scope = 'Function')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUsernameAndPasswordParams', '', Scope='Function')]
    RemotingClient([string]$hosts, [string]$username, [string]$password, [string]$domain, [string]$dns,
                   [string]$auth_method, [bool]$insecure, [bool]$ssl, [bool]$proxy)
    {
        # set the container's resolv.conf to use to provided dns
        # http://manpages.ubuntu.com/manpages/xenial/man5/resolv.conf.5.html
        if ($dns)
        {
            "nameserver $dns" | Set-Content -Path \etc\resolv.conf
        }

        $this.domain = If ($domain) {".$domain"} Else {""}
        if (! ($hosts -match $script:ValidIpAddressRegex)) {
            # if not IP add the domain to the host
            $this.host = $hosts
            $this.fqdn = $hosts + $domain
        } else {
            $this.host = $hosts
            $this.fqdn = $hosts
        }

        $this.username = $username
        $this.password = $password
        $this.insecure = $insecure
        $this.ssl = $ssl
        $this.auth_method = $auth_method
        $this.proxy = $proxy
        <#
            .DESCRIPTION
            RemotingClient connect to a remote hostname and run processes on it remotely.

            .PARAMETER domain
            The host's domain.

            .PARAMETER dns
            Domain Name System (DNS) is the hierarchical and decentralized naming system for computers connected to the network.

            .PARAMETER username
            Username is the name of a system user.

            .PARAMETER password
            Password of the system user.

            .PARAMETER insecure
            Wheter to trust any TLS/SSL Certificate) or not.

            .PARAMETER ssl
            Wheter use SSL protocol to establish a connection.

            .PARAMETER auth_method
            Authentication method to use when creating a new session.

            .PARAMETER proxy
            Wheter to user system proxy configuration or not.

            .EXAMPLE
            $client = [RemotingClient]::new("MyComputer", "username", "password")
        #>
    }

    CreateSession()
    {
        $this.session = CreateNewSession -fqdn $this.fqdn -username $this.username -password $this.password -insecure $this.insecure -ssl $this.ssl $this.auth_method
        <#
            .DESCRIPTION
            This method is for internal use. It creates session to the remote hosts.

            .EXAMPLE
            $client.CreateSession()

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/new-pssession?view=powershell-7
            https://docs.microsoft.com/en-us/powershell/partnercenter/multi-factor-auth?view=partnercenterps-3.0#exchange-online-powershell
        #>
    }

    CloseSession()
    {
        if ($this.session)
        {
            Remove-PSSession $this.session
        }
        <#
            .DESCRIPTION
            This method is for internal use. It removes an active session.

            .EXAMPLE
            $client.CloseSession()

            .LINK
            https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/remove-pssession?view=powershell-7
            https://docs.microsoft.com/en-us/powershell/partnercenter/multi-factor-auth?view=partnercenterps-3.0#exchange-online-powershell
        #>
    }


    [System.Array]InvokeCommandInSession([string]$remote_command)
    {
        if (!$this.session)
        {
            $this.CreateSession()
        }
        $temp = $script:Demisto.UniqueFile()
        $file_name = $script:Demisto.Investigation().id + "_" + $temp + ".ps1"
        $remote_command | Out-File -FilePath $file_name
        $result = Invoke-Command -Session $this.session -FilePath $file_name
        return $result
    <#
        .DESCRIPTION
        This method invokes a command in the session via a temporary file.

        .EXAMPLE
        $client.InvokeCommandInSession("$PSVersionTable")

        .LINK
        https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-7.1
    #>
    }

    CopyItemFromSession([string]$path, [string]$file_name)
    {
        if (!$this.session)
        {
            $this.CreateSession()
        }
        Copy-Item -FromSession $this.session $path -Destination $file_name
    <#
        .DESCRIPTION
        This method copies an item from the session in specified path to the destination file_name.

        .EXAMPLE
        $client.CopyItemFromSession("remote\file.txt", "local\file.txt")

        .LINK
        https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/copy-item?view=powershell-7.1
    #>
    }

    CopyItemToSession([string]$path, [string]$file_name)
    {
        if (!$this.session)
        {
            $this.CreateSession()
        }
        Copy-Item -ToSession $this.session $path -Destination $file_name
    <#
        .DESCRIPTION
        This method copies an item from the local path to the session destination file_name.

        .EXAMPLE
        $client.CopyItemFromSession("remote\file.txt", "local\file.txt")

        .LINK
        https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/copy-item?view=powershell-7.1
    #>
    }

    [PSCustomObject]ExportMFT([string]$volume, [string]$output_file_path) {
        if (!$this.session)
        {
            $this.CreateSession()
        }
        $remote_script_block = {
            Param($volume, $output_file_path)

            if ($volume -ne 0)
            {
                $Win32_Volume = Get-CimInstance -Class Win32_Volume -Filter "DriveLetter LIKE '$( $volume ):'"
                if ($Win32_Volume.FileSystem -ne "NTFS")
                {
                    Write-Error "$volume is not an NTFS filesystem."
                    break
                }
            }
            else
            {
                $Win32_Volume = Get-CimInstance -Class Win32_Volume -Filter "DriveLetter LIKE '$( $env:SystemDrive )'"
                if ($Win32_Volume.FileSystem -ne "NTFS")
                {
                    Write-Error "$env:SystemDrive is not an NTFS filesystem."
                    break
                }
            }
            if (-not$output_file_path)
            {
                $output_file_path = $env:TEMP + "\$([IO.Path]::GetRandomFileName() )"
            }

            #region WinAPI

            $GENERIC_READWRITE = 0x80000000
            $FILE_SHARE_READWRITE = 0x02 -bor 0x01
            $OPEN_EXISTING = 0x03

            $DynAssembly = New-Object System.Reflection.AssemblyName('MFT')
            $AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run)
            $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemory', $false)

            $TypeBuilder = $ModuleBuilder.DefineType('kernel32', 'Public, Class')
            $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
            $SetLastError = [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
            $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor,
            @('kernel32.dll'),
            [Reflection.FieldInfo[]]@($SetLastError),
            @($True))

            #CreateFile
            $PInvokeMethodBuilder = $TypeBuilder.DefinePInvokeMethod('CreateFile', 'kernel32.dll',
                    ([Reflection.MethodAttributes]::Public -bor [Reflection.MethodAttributes]::Static),
                    [Reflection.CallingConventions]::Standard,
                    [IntPtr],
                    [Type[]]@([String],[Int32],[UInt32],[IntPtr],[UInt32],[UInt32],[IntPtr]),
                    [Runtime.InteropServices.CallingConvention]::Winapi,
                    [Runtime.InteropServices.CharSet]::Ansi)
            $PInvokeMethodBuilder.SetCustomAttribute($SetLastErrorCustomAttribute)

            #CloseHandle
            $PInvokeMethodBuilder = $TypeBuilder.DefinePInvokeMethod('CloseHandle', 'kernel32.dll',
                    ([Reflection.MethodAttributes]::Public -bor [Reflection.MethodAttributes]::Static),
                    [Reflection.CallingConventions]::Standard,
                    [Bool],
                    [Type[]]@([IntPtr]),
                    [Runtime.InteropServices.CallingConvention]::Winapi,
                    [Runtime.InteropServices.CharSet]::Auto)
            $PInvokeMethodBuilder.SetCustomAttribute($SetLastErrorCustomAttribute)

            $Kernel32 = $TypeBuilder.CreateType()

            #endregion WinAPI

            # Get handle to volume
            if ($volume -ne 0)
            {
                $VolumeHandle = $Kernel32::CreateFile(('\\.\' + $volume + ':'), $GENERIC_READWRITE, $FILE_SHARE_READWRITE, [IntPtr]::Zero, $OPEN_EXISTING, 0, [IntPtr]::Zero)
            }
            else
            {
                $VolumeHandle = $Kernel32::CreateFile(('\\.\' + $env:SystemDrive), $GENERIC_READWRITE, $FILE_SHARE_READWRITE, [IntPtr]::Zero, $OPEN_EXISTING, 0, [IntPtr]::Zero)
                $volume = ($env:SystemDrive).TrimEnd(':')
            }

            if ($VolumeHandle -eq -1)
            {
                Write-Error "Unable to obtain read handle for volume."
                break
            }

            # Create a FileStream to read from the volume handle
            $FileStream = New-Object IO.FileStream($VolumeHandle, [IO.FileAccess]::Read)

            # Read VBR from volume
            $VolumeBootRecord = New-Object Byte[](512)
            if ($FileStream.Read($VolumeBootRecord, 0, $VolumeBootRecord.Length) -ne 512)
            {
                Write-Error "Error reading volume boot record."
            }

            # Parse MFT offset from VBR and set stream to its location
            $MftOffset = [Bitconverter]::ToInt32($VolumeBootRecord[0x30..0x37], 0) * 0x1000
            $FileStream.Position = $MftOffset

            # Read MFT's file record header
            $MftFileRecordHeader = New-Object byte[](48)
            if ($FileStream.Read($MftFileRecordHeader, 0, $MftFileRecordHeader.Length) -ne $MftFileRecordHeader.Length)
            {
                Write-Error "Error reading MFT file record header."
            }

            # Parse values from MFT's file record header
            $OffsetToAttributes = [Bitconverter]::ToInt16($MftFileRecordHeader[0x14..0x15], 0)
            $AttributesRealSize = [Bitconverter]::ToInt32($MftFileRecordHeader[0x18..0x21], 0)

            # Read MFT's full file record
            $MftFileRecord = New-Object byte[]($AttributesRealSize)
            $FileStream.Position = $MftOffset
            if ($FileStream.Read($MftFileRecord, 0, $MftFileRecord.Length) -ne $AttributesRealSize)
            {
                Write-Error "Error reading MFT file record."
            }

            # Parse MFT's attributes from file record
            $Attributes = New-object byte[]($AttributesRealSize - $OffsetToAttributes)
            [Array]::Copy($MftFileRecord, $OffsetToAttributes, $Attributes, 0, $Attributes.Length)

            # Find Data attribute
            $CurrentOffset = 0
            do
            {
                $AttributeType = [Bitconverter]::ToInt32($Attributes[$CurrentOffset..$( $CurrentOffset + 3 )], 0)
                $AttributeSize = [Bitconverter]::ToInt32($Attributes[$( $CurrentOffset + 4 )..$( $CurrentOffset + 7 )], 0)
                $CurrentOffset += $AttributeSize
            } until ($AttributeType -eq 128)

            # Parse data attribute from all attributes
            $DataAttribute = $Attributes[$( $CurrentOffset - $AttributeSize )..$( $CurrentOffset - 1 )]

            # Parse MFT size from data attribute
            $MftSize = [Bitconverter]::ToUInt64($DataAttribute[0x30..0x37], 0)

            # Parse data runs from data attribute
            $OffsetToDataRuns = [Bitconverter]::ToInt16($DataAttribute[0x20..0x21], 0)
            $DataRuns = $DataAttribute[$OffsetToDataRuns..$( $DataAttribute.Length - 1 )]

            # Convert data run info to string[] for calculations
            $DataRunStrings = ([Bitconverter]::ToString($DataRuns)).Split('-')

            # Setup to read MFT
            $FileStreamOffset = 0
            $DataRunStringsOffset = 0
            $TotalBytesWritten = 0
            $MftData = New-Object byte[](0x1000)
            $OutputFileStream = [IO.File]::OpenWrite($output_file_path)

            do
            {
                $StartBytes = [int]($DataRunStrings[$DataRunStringsOffset][0]).ToString()
                $LengthBytes = [int]($DataRunStrings[$DataRunStringsOffset][1]).ToString()

                $DataRunStart = "0x"
                for ($i = $StartBytes; $i -gt 0; $i--) {
                    $DataRunStart += $DataRunStrings[($DataRunStringsOffset + $LengthBytes + $i)]
                }

                $DataRunLength = "0x"
                for ($i = $LengthBytes; $i -gt 0; $i--) {
                    $DataRunLength += $DataRunStrings[($DataRunStringsOffset + $i)]
                }

                $FileStreamOffset += ([int]$DataRunStart * 0x1000)
                $FileStream.Position = $FileStreamOffset

                for ($i = 0; $i -lt [int]$DataRunLength; $i++) {
                    if ($FileStream.Read($MftData, 0, $MftData.Length) -ne $MftData.Length)
                    {
                        Write-Warning "Possible error reading MFT data on $env:COMPUTERNAME."
                    }
                    $OutputFileStream.Write($MftData, 0, $MftData.Length)
                    $TotalBytesWritten += $MftData.Length
                }
                $DataRunStringsOffset += $StartBytes + $LengthBytes + 1
            } until ($TotalBytesWritten -eq $MftSize)

            $FileStream.Dispose()
            $OutputFileStream.Dispose()

            $Properties = @{
                NetworkPath = "\\$( $env:COMPUTERNAME )\C$\$($output_file_path.TrimStart('C:\') )"
                ComputerName = $env:COMPUTERNAME
                'MFT Size' = "$( $MftSize / 1024 / 1024 ) MB"
                'MFT Volume' = $volume
                'MFT File' = $output_file_path
            }
            New-Object -TypeName PSObject -Property $Properties
        }
        return Invoke-Command -Session $this.session -ScriptBlock $remote_script_block -ArgumentList @($volume, $output_file_path)
    }
}

function TestModuleCommand {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Scope='Function')]
    param([RemotingClient]$client)

    if ($client.ssl) {
        $res = Test-Connection -TcpPort 5986 -TargetName $client.fqdn -ErrorAction Stop
        if (-not $res) {
            throw 'Could not create connection for host ' + $client.fqdn + ' via port 5986.'
        }
    }
    else {
        $res = Test-Connection -TcpPort 5985 -TargetName $client.fqdn -ErrorAction Stop
        if (-not $res) {
            throw 'Could not create connection for host ' + $client.fqdn + ' via port 5985.'
        }
    }
    $tmp = $client.InvokeCommandInSession('$PSVersionTable')
    $client.CloseSession()

    $human_readable = "ok"

    return $human_readable, $null, $null
}

function CreatResultsMap {
    param([System.Array]$raw_result, [string]$command, [string]$fqdn, [string]$hosts)
    $now = Get-Date -Format "o"
    $results_map = @{
        Host = $hosts
        FQDN = $fqdn
        CommandName = $command
        UTCTime = $now
        CommandResult = [System.Collections.ArrayList]::new()
    }
    For ($i=0; $i -lt $raw_result.Length; $i++) {
        # base name
        if($raw_result[$i].GetType().Fullname -eq 'System.Management.Automation.PSObject') {
                $item = ($raw_result[$i] | select-object -ExcludeProperty PSComputerName,PSShowComputerName,RunspaceId)
        }
        elseif ($raw_result[$i].GetType().Fullname -eq 'System.String') {
            if ([string]::IsNullOrEmpty($raw_result[$i])) {
                continue
            }
            $item = ($raw_result[$i] | out-string)
        }
        $results_map['CommandResult'] += $item
    }
    return $results_map
    <#
        .DESCRIPTION
        Create result map from single computer result
    #>
}

function InvokeCommandCommand {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Scope='Function')]
    param([RemotingClient]$client, [string]$command)

    $title = "Result for PowerShell Remote Command: $command `n"
    $raw_result = $client.InvokeCommandInSession($command)
    $client.CloseSession()
    $results_map = CreatResultsMap -raw_result $raw_result -command $command -fqdn $client.fqdn -hosts $client.host
    # extract command results per computer
    if ($results_map) {
        $entry_context = @{
            $script:INTEGRATION_ENTRY_CONTEXT = $results_map
        }
        $human_readable = $title + $raw_result

        return $human_readable, $entry_context, $raw_result
    }
    else {
        return $title + 'No results', $null, $null
    }
    <#
        .DESCRIPTION
        Runs invoke-command on existing session.
    #>
}

function DownloadFileCommand([RemotingClient]$client, [string]$path, [string]$zip_file, [string]$check_hash, [bool]$host_as_prefix)
{
    $temp = $script:Demisto.UniqueFile()
    $file_name = $script:Demisto.Investigation().id + "_$temp"

    # assert file exists in the system
    $command = '[System.IO.File]::Exists("' + $path + '")'
    $raw_result = $client.InvokeCommandInSession($command)
    if (-Not $raw_result -or ($raw_result -eq $False))
    {
        $client.CloseSession()
        throw "$path was not found on the remote host."
    }

    if ($host_as_prefix -eq 'true')
    {
        $file_name_parent = Split-Path $path
        $pre_rename_leaf = Split-Path $path -leaf
        $file_name_leaf = $client.host + '_' + $pre_rename_leaf
        $command = "Rename-Item -Path $path -NewName $file_name_leaf"
        $client.InvokeCommandInSession($command)
        $path = "$file_name_parent/$file_name_leaf"
    }

    if ($zip_file -eq 'true')
    {
        # zip file at the host
        $pre_zip_path = $path
        $path = "$path.zip"
        $command = "Compress-Archive -Path $pre_zip_path -Update -DestinationPath $path"
        $client.InvokeCommandInSession($command)
    }

    if ($check_hash -eq 'true')
    {
        # save orig hash
        $command = "(Get-FileHash $path -Algorithm MD5).Hash"
        $src_hash = $client.InvokeCommandInSession($command)
    }

    $client.CopyItemFromSession($path, $file_name)
    if ($zip_file -eq 'true')
    {
        # clean zip from host
        $command = "Remove-Item $path"
        $client.InvokeCommandInSession($command)
    }

    if ($host_as_prefix -eq 'true')
    {
        if ($zip_file -eq 'true')
        {
            $command = "Rename-Item -Path $pre_zip_path -NewName $pre_rename_leaf"
        }
        else {
            $command = "Rename-Item -Path $path -NewName $pre_rename_leaf"
        }
        $client.InvokeCommandInSession($command)
    }

    $client.CloseSession()
    if ($check_hash -eq 'true')
    {
        # compare src-dst hashes
        $dst_hash = (Get-FileHash $file_name -Algorithm MD5).Hash
        if ($src_hash -ne $dst_hash)
        {
            throw "Failed check_hash: The downloaded file has a different hash than the file in the host. RemoteHostHash=$src_hash DownloadedHash=$dst_hash"
        }
    }

    # add file details to context
    $file_name_leaf = Split-Path $path -leaf
    $file_extension = [System.IO.Path]::GetExtension($file_name_leaf)
    $file_extension = If ($file_extension) {$file_extension.SubString(1, $file_extension.length - 1)} else {""}

    $entry_context = @{
        PsRemoteDownloadedFile = @{
            FQDN = $client.fqdn
            Host = $client.host
            FileName = $file_name_leaf
            FileSize = Get-Item $file_name | ForEach-Object { [math]::ceiling($_.length / 1kb) }
            FileSHA1 = (Get-FileHash $file_name -Algorithm SHA1).Hash
            FileSHA256 = (Get-FileHash $file_name -Algorithm SHA256).Hash
            FileMD5 = (Get-FileHash $file_name -Algorithm MD5).Hash
            FileExtension = $file_extension
        }
    }

    $demisto_results = @{
        Type = 3
        ContentsFormat = "text"
        Contents = ""
        File = $file_name_leaf
        FileID = $temp
        EntryContext = $entry_context
    }
    $script:Demisto.Results($demisto_results)
}

function StartETLCommand {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Scope='Function')]
    param([RemotingClient]$client, [string]$etl_path, [string]$etl_filter, [string]$etl_max_size, [string]$overwrite)
    $command = "netsh trace start capture=yes traceFile=$etl_path maxsize=$etl_max_size overwrite=$overwrite $etl_filter"
    $raw_result = $client.InvokeCommandInSession($command)
    $client.CloseSession()
    $results_map = CreatResultsMap -raw_result $raw_result -command $command -fqdn $client.fqdn -hosts $client.host
    if ($results_map) {
        $results_map['EtlFilePath'] = $etl_path
        $results_map['EtlFileName'] = Split-Path $etl_path -leaf
        $entry_context = @{
            $script:INTEGRATION_ENTRY_CONTEXT = $results_map
        }
        $human_readable = $title + $raw_result

        return $human_readable, $entry_context, $raw_result
    }
    else {
        return $title + 'No results', $null, $null
    }
}

function StopETLCommand([RemotingClient]$client)
{
    $command = 'netsh trace stop'
    $raw_results = $client.InvokeCommandInSession($command)
    $client.CloseSession()
    $etl_path = $raw_results | Select-String -Pattern "File location = "
    if ($etl_path)
    {
        # clean "File location = "
        $etl_path = $etl_path.ToString()
        $etl_path = $etl_path.Substring(16, $etl_path.Length - 16)
    }
    else
    {
        $etl_path = ""
    }
    if ($raw_results)
    {
        $raw_results = [string]$raw_results
    }
    $entry_context = @{
        $script:INTEGRATION_ENTRY_CONTEXT = @{
            FQDN = $client.fqdn
            Host = $client.host
            CommandName = $command
            CommandResult = $raw_results
            EtlFilePath = $etl_path
            EtlFileName = if ($etl_path) {Split-Path $etl_path -leaf} else {""}
        }
    }

    $human_readable = $raw_results

    return $human_readable, $entry_context, $raw_result
}

function ExportRegistryCommand([RemotingClient]$client, [string]$reg_key_hive, [string]$output_file_path)
{
    $command = if ($reg_key_hive -eq 'all') {"regedit /e $output_file_path"} else {"reg export $reg_key_hive $output_file_path"}
    $raw_results = $client.InvokeCommandInSession($command)
    Start-Sleep -Seconds 30
    $client.CloseSession()
    $title = "Ran Export Registry. `n"

    $entry_context = @{
        $script:INTEGRATION_ENTRY_CONTEXT = @{
            FQDN = $client.fqdn
            Host = $client.host
            CommandResult = $raw_results
            CommandName = $command
            RegistryFilePath = $output_file_path
            RegistryFileName = Split-Path $output_file_path -leaf
        }
    }

    $human_readable = $title + "Registry file expected path: $output_file_path"

    return $human_readable, $entry_context, $raw_results
}

function UploadFileCommand([RemotingClient]$client, [string]$entry_id, [string]$dst_path, [string]$zip_file, [string]$check_hash)
{
    $src_path = $script:Demisto.GetFilePath($entry_id).path
    $file_exists = Test-Path $src_path -PathType Leaf
    if (-Not $file_exists)
    {
        throw "Could not find $entry_id file, please make sure you entered it correctly."
    }
    if ($zip_file -eq 'true')
    {
        # compress file before upload
        $old_path = $src_path
        $src_path = "$src_path.zip"
        $dst_path = "$dst_path.zip"
        Compress-Archive -Path $old_path -Update -DestinationPath $src_path
    }
    if ($check_hash -eq 'true')
    {
        # save hash before upload
        $src_hash = (Get-FileHash $src_path -Algorithm MD5).Hash
    }
    $client.CopyItemToSession($src_path, $dst_path)
    if ($check_hash -eq 'true')
    {
        $command = "(Get-FileHash $dst_path -Algorithm MD5).Hash"
        $dst_hash = $client.InvokeCommandInSession($command)
        if ($src_hash -ne $dst_hash)
        {
            throw "Failed check_hash: The uploaded file has a different hash than the local file. LocalFileHash=$src_hash UploadedFileHash=$dst_hash"
        }
    }
    $client.CloseSession()

    $file_name_leaf = Split-Path $src_path -leaf
    $file_ext = [System.IO.Path]::GetExtension($file_name_leaf)
    $file_ext = If ($file_ext) {$file_ext.SubString(1, $file_ext.length - 1)} else {""}

    $entry_context = @{
        PsRemoteUploadedFile = @{
            FQDN = $client.fqdn
            Host = $client.host
            FilePath = $dst_path
            FileName = $file_name_leaf
            FileSize = Get-Item $src_path | ForEach-Object { [math]::ceiling($_.length / 1kb) }
            FileSHA1 = (Get-FileHash $src_path -Algorithm SHA1).Hash
            FileSHA256 = (Get-FileHash $src_path -Algorithm SHA256).Hash
            FileMD5 = (Get-FileHash $src_path -Algorithm MD5).Hash
            FileExtension = $file_ext
        }
    }
    $human_readable = "File $file_name_leaf was uploaded successfully as: $dst_path"

    return $human_readable, $entry_context, $null
}

function ExportMFTCommand([RemotingClient]$client, [string]$volume, [string]$output_file_path)
{
    $raw_response = $client.ExportMFT($volume, $output_file_path)
    $client.CloseSession()

    $entry_context = @{
        $script:INTEGRATION_ENTRY_CONTEXT = @{
            FQDN = $client.fqdn
            Host = $client.host
            ExportMFT = $raw_response
        }
    }
    $human_readable = TableToMarkdown $raw_response 'MFT Export results:'

    return $human_readable, $entry_context, $raw_result
}

function Main
{
    $command = $Demisto.GetCommand()
    $command_args = $Demisto.Args()
    $params = $Demisto.Params()
    <#
        Proxy currently isn't supported by PWSH New-Pssession, However further effort might yield an implementation,
        leaving this parameter for feature development if required.
    #>
    $no_proxy = $false
    $insecure = (ConvertTo-Boolean $params.insecure)
    $ssl = (ConvertTo-Boolean $params.ssl)

    try
    {
        $hosts = if ($command -eq 'test-module') {ArgToList $params.host } else {ArgToList $command_args.host}
        if ($hosts.Length -gt 1) {
            throw "too many hosts were provided. please provide just a single host"
        }
        $domain = if ($params.domain) {"." + $params.domain} else {""}
        $client = [RemotingClient]::new($hosts, $params.credentials.identifier, $params.credentials.password,
                                        $domain, $params.dns, $params.auth_method, $insecure, $ssl, $no_proxy)
        # Executing command
        $Demisto.Debug("Command being called is $command")
        switch ($command)
        {
            "test-module" {
                ($human_readable, $entry_context, $raw_response) = TestModuleCommand $client
            }
            "$script:COMMAND_PREFIX-command" {
                ($human_readable, $entry_context, $raw_response) = InvokeCommandCommand $client $command_args.command
            }
            "$script:COMMAND_PREFIX-download-file" {
                DownloadFileCommand -client $client -path $command_args.path -zip_file $command_args.zip_file -check_hash $command_args.check_hash -host_as_prefix (ConvertTo-Boolean $command_args.host_prefix)
                return
            }
            "$script:COMMAND_PREFIX-etl-create-start" {
                ($human_readable, $entry_context, $raw_response) = StartETLCommand -client $client -etl_path $command_args.etl_path -etl_filter $command_args.etl_filter -etl_max_size $command_args.etl_max_size -overwrite $command_args.overwrite
            }
            "$script:COMMAND_PREFIX-etl-create-stop" {
                ($human_readable, $entry_context, $raw_response) = StopETLCommand $client
            }
            "$script:COMMAND_PREFIX-export-registry" {
                ($human_readable, $entry_context, $raw_response) = ExportRegistryCommand -client $client -reg_key_hive $command_args.reg_key_hive -output_file_path $command_args.file_path
            }
            "$script:COMMAND_PREFIX-upload-file" {
                # to be tested
                ($human_readable, $entry_context, $raw_response) = UploadFileCommand -client $client -entry_id $command_args.entry_id -dst_path $command_args.path -zip_file $command_args.zip_file -check_hash $command_args.check_hash
            }
            "$script:COMMAND_PREFIX-export-mft" {
                ($human_readable, $entry_context, $raw_response) = ExportMFTCommand -client $client -volume $command_args.volume -output_file_path $command_args.output_path
            }
            Default {
                $Demisto.Error("Unsupported command was entered: $command.")
            }
        }
        # Return results to Demisto Server
        ReturnOutputs $human_readable $entry_context $raw_response | Out-Null
    }
    catch
    {
        $Demisto.debug("Integration: $script:INTEGRATION_NAME Command: $command Arguments: $( $command_args | ConvertTo-Json ) Error: $( $_.Exception.Message )")
        if ($command -ne "test-module")
        {
            ReturnError "Error:Integration: $script:INTEGRATION_NAME Command: $command Arguments: $( $command_args | ConvertTo-Json ) Error: $( $_.Exception )" | Out-Null
        }
        else
        {
            $err_msg = $_.Exception.Message
            if ($err_msg -match 'Authorization failed') {
                $err_msg = 'The integration was able to resolve and perform a connectivity test to the specified host. There seems to be an issue with the credentials or host settings.
                For further troubleshooting please refer to the Powershell Remoting article in https://xsoar.pan.dev/docs/reference/index.'
        }
            ReturnError $err_msg
        }
    }
}


# Execute Main when not in Tests
if ($MyInvocation.ScriptName -notlike "*.tests.ps1" -AND -NOT$Test)
{
    Main
}