{"id":15885750,"url":"https://github.com/withsecurelabs/remotepspy","last_synced_at":"2025-12-14T12:55:07.721Z","repository":{"id":57461135,"uuid":"164461464","full_name":"WithSecureLabs/RemotePSpy","owner":"WithSecureLabs","description":"RemotePSpy provides live monitoring of remote PowerShell sessions, which is particularly useful for older (pre-5.0) versions of PowerShell which do not have comprehensive logging facilities built in.","archived":false,"fork":false,"pushed_at":"2020-03-12T17:48:51.000Z","size":722,"stargazers_count":18,"open_issues_count":1,"forks_count":11,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-10-28T01:06:45.657Z","etag":null,"topics":["countercept"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WithSecureLabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-07T16:40:03.000Z","updated_at":"2024-08-12T19:44:39.000Z","dependencies_parsed_at":"2022-09-17T04:00:24.545Z","dependency_job_id":null,"html_url":"https://github.com/WithSecureLabs/RemotePSpy","commit_stats":null,"previous_names":["countercept/remotepspy"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WithSecureLabs%2FRemotePSpy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WithSecureLabs%2FRemotePSpy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WithSecureLabs%2FRemotePSpy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WithSecureLabs%2FRemotePSpy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WithSecureLabs","download_url":"https://codeload.github.com/WithSecureLabs/RemotePSpy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222431531,"owners_count":16983387,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["countercept"],"created_at":"2024-10-06T05:07:20.217Z","updated_at":"2025-12-14T12:55:07.667Z","avatar_url":"https://github.com/WithSecureLabs.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RemotePSpy\r\n\r\nRemotePSpy provides live monitoring of remote PowerShell sessions, which is particularly useful for older (pre-5.0)\r\nversions of PowerShell which do not have comprehensive logging facilities built in.\r\n\r\nIt uses Event Tracing for Windows (ETW) to obtain message data from the WinRM protocol that is used as a transport for\r\nremote PowerShell, and decodes the various protocol layers to provide a trace of the script commands executed and their\r\ninput/output.\r\n\r\nThis is currently an early research prototype and so does not support every aspect of the PowerShell Remote Protocol\r\n(PSRP). It can still provide useful insights as to what is happening in a remote PowerShell session.\r\n\r\n## Installation\r\n\r\nThe easiest way to install is using pip:\r\n\r\n```\r\npip install remotepspy\r\n```\r\n\r\n### Dependencies\r\n\r\nDependencies should be installed automatically when installing via pip, but in case you wish to install from source they\r\nare listed here:\r\n\r\n* Python 3.7\r\n* pywintrace (https://github.com/fireeye/pywintrace or https://pypi.org/project/pywintrace/)\r\n* psutils (https://pypi.org/project/pywintrace/)\r\n* libwim-15.dll from https://wimlib.net/ (bundled in the RemotePSpy package for convenience)\r\n\r\nThe libwim-15.dll is only required if using the (recommended, and default) Microsoft-Windows-WinRM ETW provider as a\r\ndata source. It is used to decompress certain stream objects in the WinRM/WSMan protocol which contain PSRP message\r\nfragments. Just put it in the same directory as the main Python script.\r\n\r\n## Usage\r\n\r\nSimply execute `RemotePSpy` to start monitoring and logging, and press Return when you are finished. The log will be\r\nwritten to the current working directory, named `RemotePSpy.log`.\r\n\r\nThe tool will also print an approximate replica of what the user of remote PowerShell would see on their screen to\r\nstdout alongside the more verbose information in the log file. More complex logging is available, see \"Logging\" below.\r\n\r\nIf you installed Python to be in your PATH, the RemotePSpy executable scripts will also be in your PATH. Otherwise you\r\nmay need to look for then in your Python site-packages directory.\r\n\r\n### PowerShell ETW Provider Version\r\n\r\nThe default `RemotePSpy` uses the (recommended) WinRM ETW provider as a data source. If you wish to use the PowerShell\r\nETW provider instead, you can execute `RemotePSpy_powershell_prov`.\r\n\r\nThis version may produce some unecessary warnings due to the added complexity of tracking certain state based on how the\r\ndata is provided in this particular ETW provider.\r\n\r\n## Internals\r\n\r\nThe code consists of a number of fairly modular classes which can accept input at different layers in the protocol\r\nstack. This allows them to be plugged together in slightly different ways depending on where the data is obtained from\r\nand in what form. Most classes accept a callback function which they use to pass on the result they produce to the next\r\nlayer in the stack.\r\n\r\nA description of the main classes used is given below, followed by some processing flows which show which class feeds\r\ninto which in different scenarios.\r\n\r\n* ETWWinRM – Obtains ETW events from the WinRM provider.\r\n\r\n* ETWPowerShell – Obtains ETW events from the PowerShell provider.\r\n\r\n* PowerShellETWParser – Identifies Shell context for PowerShell ETW events, passing on the fragment data to\r\nPSRPDefragmenter. Also provides thread synchronization.\r\n\r\n* SoapDefragmenter – Re-assembles full WSMan SOAP messages from WinRM ETW events, passing the complete SOAP on to\r\nWSManPS.\r\n\r\n* WSManPS – Filters out non-PowerShell related WinRM, tracks Shell context, parses out PSRP fragment data from the WSMan\r\nSOAP, and passes on the fragment data to PSRPDefragmenter.\r\n\r\n* PSRPDefragmenter – Re-assembles PSRP fragments into full PRSP messages. Fragments are assembled by ObjectID, and\r\nuniqueness of ObjectID is ensured by taking Shell context into account. Passes final PSRP messages on to PSRPParser.\r\n\r\n* PSRPParser – Decodes the raw binary PSRP message, extracting header details like RPID, Pipeline ID, and MessageType.\r\nPasses decoded messages on to SimpleCommandTracer.\r\n\r\n* SimpleCommandTracer – Interprets PSRP messages using MessageType, and extracts and prints/logs commands, arguments,\r\nand their output. Includes partial decoding of serialized PowerShell objects. Not a complete implementation of every\r\npossible feature, but attempts to cover most common cases to allow for execution trace.\r\n\r\nProcessing flow when using Microsoft-Windows-WinRM ETW provider:\r\n\r\n_ETWWinRM -\u003e SoapDefragmenter -\u003e WSManPS -\u003e PSRPDefragmenter -\u003e PSRPParser -\u003e SimpleCommandTracer_\r\n\r\nProcessing flow when using Microsoft-Windows-PowerShell ETW provider:\r\n\r\n_ETWPowerShell -\u003e PowerShellETWParser -\u003e PSRPDefragmenter -\u003e PSRPParser -\u003e SimpleCommandTracer_\r\n\r\n\r\n## Logging\r\n\r\nThere is comprehensive logging at each layer in the protocol stack. This allows debugging at various levels, and was\r\nespecially helpful during development. Hopefully it can also be useful to anyone wanting to investigate remote\r\nPowerShell in action, as it is possible to get a full trace of the protocol at all the key layers.\r\n\r\nCurrently, the source code at the end of the script where loggers are configured must be edited to change logging.\r\nFuture releases should hopefully provide a better method for such configuration.\r\n\r\nEach logger name is defined by a constant, LOGGER_NAME, in the class that uses it. Every logger is defined as a child of\r\n\"RemotePSpy\" (e.g. RemotePSpy.etw). Programmatically, loggers can be configured by using logging.getLogger() with the\r\nappropriate log name.\r\n\r\nFor quick reference, the following loggers and levels will provide you with protocol traces at different layers in the\r\nprotocol:\r\n\r\n| Key Log Data                     | Logger Name Constant            | Level |\r\n| -------------------------------- | ------------------------------- | ----- |\r\n| Command Trace                    | SimpleCommandTracer.LOGGER_NAME | INFO  |\r\n| Full ETW event trace             | ETWWinRM.LOGGER_NAME            | DEBUG |\r\n| Full WSMan SOAP message trace    | SoapDefragmenter.LOGGER_NAME    | INFO  |\r\n| Full trace of each PSRP fragment | PSRPDefragmenter.LOGGER_NAME    | DEBUG |\r\n| Full PSRP message trace          | PSRPParser.LOGGER_NAME          | DEBUG |\r\n\r\nNote that SimpleCommandTracer outputs a trace on stdout as well, and this attempts to somewhat replicate the display as\r\nthe remote PowerShell user would see it. This is different to the command trace log which logs each command, pipeline\r\nmethod call, and pipeline output in a more precise way, including additional context such as RPID and Pipeline ID.\r\n\r\nA fuller summary of what appears in each log at which level is given below:\r\n\r\n#### SimpleCommandTracer.LOGGER_NAME\r\n**ERROR:** \r\n* Parsing errors\r\n\r\n**WARNING:** \r\n* Unsupported type or pipeline method encountered\r\n\r\n**INFO:** \r\n* The actual command trace\r\n\r\n**DEBUG:**\r\n* Pipeline method called with no arguments\r\n\r\n#### PSRPParser.LOGGER_NAME\r\n**DEBUG:** \r\n* Full PSRP message trace\r\n\r\n#### PSRPDefragmenter.LOGGER_NAME\r\n**ERROR:** \r\n* Out-of-order fragment received\r\n\r\n**WARNING:** \r\n* Non-fatal Shell tracking anomalies\r\n\r\n**INFO:** \r\n* End fragment found\r\n* Tracking Shell ID found in fragment data that was not explicitly tracked before\r\n\r\n**DEBUG:** \r\n* Full trace of each PSRP fragment\r\n\r\n#### WSManPS.LOGGER_NAME\r\n**ERROR:** \r\n* Serious parsing errors\r\n\r\n**WARNING:** \r\n* Less serious parsing errors\r\n* Overwriting of existing shell/command tracking contexts\r\n\r\n**INFO:** \r\n* Shell and command tracking info\r\n\r\n**DEBUG:** \r\n* Full trace of WSMan messages that are ignored due to not being related to PowerShell\r\n\r\n#### SoapDefragmenter.LOGGER_NAME\r\n**INFO:** \r\n* Full WSMan SOAP message trace\r\n\r\n**DEBUG:** \r\n* SOAP chunk processed\r\n\r\n#### PowerShellETWParser.LOGGER_NAME\r\n**DEBUG:** \r\n* Shell tracking info\r\n\r\n**ERROR:** \r\n* Shell context identification errors\r\n* Catch-all exceptions from lower layers\r\n\r\n#### ETWWinRM.LOGGER_NAME\r\n**DEBUG:** \r\n* Full ETW event trace\r\n\r\n**INFO:** \r\n* ETW session start/stop\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwithsecurelabs%2Fremotepspy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwithsecurelabs%2Fremotepspy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwithsecurelabs%2Fremotepspy/lists"}