From bb650456f267ac374d112bc2ef22199f55fedfaa Mon Sep 17 00:00:00 2001 From: CoprDistGit Date: Mon, 10 Apr 2023 17:33:22 +0000 Subject: automatic import of python-pypsexec --- .gitignore | 1 + python-pypsexec.spec | 1234 ++++++++++++++++++++++++++++++++++++++++++++++++++ sources | 1 + 3 files changed, 1236 insertions(+) create mode 100644 python-pypsexec.spec create mode 100644 sources diff --git a/.gitignore b/.gitignore index e69de29..30e6e70 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +/pypsexec-0.3.0.tar.gz diff --git a/python-pypsexec.spec b/python-pypsexec.spec new file mode 100644 index 0000000..da09792 --- /dev/null +++ b/python-pypsexec.spec @@ -0,0 +1,1234 @@ +%global _empty_manifest_terminate_build 0 +Name: python-pypsexec +Version: 0.3.0 +Release: 1 +Summary: Run commands on a remote Windows host using SMB/RPC +License: MIT +URL: https://github.com/jborean93/pypsexec +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/d4/cd/da60adc8d022ec3c38248f36d444568143f18de3f588c1b155a82ccd62c5/pypsexec-0.3.0.tar.gz +BuildArch: noarch + +Requires: python3-smbprotocol + +%description +# Python PsExec Library + +[![Test workflow](https://github.com/jborean93/pypsexec/actions/workflows/ci.yml/badge.svg)](https://github.com/jborean93/pypsexec/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/jborean93/pypsexec/branch/master/graph/badge.svg?token=Hi2Nk4RfMF)](https://codecov.io/gh/jborean93/pypsexec) +[![PyPI version](https://badge.fury.io/py/pypsexec.svg)](https://badge.fury.io/py/pypsexec) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jborean93/pypsexec/blob/master/LICENSE) + +This library can run commands on a remote Windows host through Python. This +means that it can be run on any host with Python and does not require any +binaries to be present or a specific OS. It uses SMB/RPC to executable commands +in a similar fashion to the popular PsExec tool. More details on this tool +can be read on +[this blog post](https://www.bloggingforlogging.com/2018/03/12/introducing-psexec-for-python/). + +The executable wrapper that is sent to the service is based on the +[PAExec](https://github.com/poweradminllc/PAExec) library. PAExec is an free, +redistributable and open source equivalent to Microsoft's +[PsExec](https://docs.microsoft.com/en-us/sysinternals/downloads/psexec) +application. This program is stored as a binary in this package and is used +to run the remote service and start the process execution. + +I would like to thank the developers of Power Admin for creating this library +as it has made this library a lot less complex than what it would have been. + + +## Features + +With pypsexec you can run commands of a remote Windows host like you would with +PsExec. Current you can use pypsexec to do the following; + +* Run as a specific local or domain user or the user +* Run as the local SYSTEM account +* Run as an interactive process +* Specify the session the interactive process should run on +* Specify the run level of the user token, `highest` or `limited` +* Set the priority of the process +* Set a timeout for the remote process +* Send input through the stdin pipe to the running process +* Set the processors the process can run on + + +## Further Info + +While this info is not necessary for you to use this library it can help people +understand what is happening under the hood. This library runs the following +steps when running a command; + +* Create an SMB connection to the host +* Copies across the PAExec binary to the `ADMIN$` share of the remote host +* Binds the Windows Service Manager to the opened `IPC$` tree using RPC +* Creates and starts a Windows service as the `SYSTEM` account to run the binary copied +* Connect to the PAExec named pipe the service creates +* Sends the process details to the PAExec service through the pipe +* Send a request to the PAExec service to start the process based on the settings sent +* Connect to the newly spawned process's stdout, stderr, stdin pipe (if not interactive or async) +* Read the stdout/stderr pipe until the process is complete +* Get the return code of the new process +* Stop and remove the PAExec service +* Remove the PAExec binary from the `ADMIN$` share +* Disconnects from the SMB connection + +In the case of a failed process, the PAExec service and binary may not be +removed from the host and may need to be done manually. This is only the case +for a critical error or the cleanup functions not being called. + +By default the data being sent to and from the server is encrypted to stop +people listening in on the network from snooping your data. Unfortunately this +uses SMB encryption which was added in the SMB 3.x dialects so hosts running +Windows 7, Server 2008, or Server 2008 R2 will not work with encryption. + +This means that any data sent over the wire on these older versions of Windows +is viewable by anyone reading those packets. Any input or output of the process +comes through these packets so any secrets sent over the network won't be +encrypted. PAExec tries to reduce this risk by doing a simple XOR scramble of +the settings set in `run_executable` so it isn't plaintext but it can be +decoded by someone who knows the protocol. + + +## Requirements + +* Python 3.6+ +* [smbprotocol](https://github.com/jborean93/smbprotocol) + +To install pypsexec, simply run + +```bash +pip install pypsexec +``` + +This will download the required packages that are required and get your +Python environment ready to do. + +Out of the box, pypsexec supports authenticating to a Windows host with NTLM +authentication but users in a domain environment can take advantage of Kerberos +authentication as well for added security. The Kerberos libraries are an +optional install which can be installed with; + +```bash +# for Debian/Ubuntu/etc: +sudo apt-get install gcc python-dev libkrb5-dev +pip install smbprotocol[kerberos] + +# for RHEL/CentOS/etc: +sudo yum install gcc python-devel krb5-devel krb5-workstation python-devel +pip install smbprotocol[kerberos] +``` + + +## Remote Host Requirements + +The goal of this package to be able to run executables on a vanilla remote +Windows host with as little setup as possible. Unfortunately there is still +some setup required to get working depending on the OS version and type +that is being used. What pypsexec requires on the host is; + +* SMB to be up and running on the Windows port and readable from the Python host +* The `ADMIN$` share to be enabled with read/write access of the user configured +* The above usually means the configured user is an administrator of the Windows host +* At least SMB 2 on the host (Server 2008 and newer) +* The connection user has a full logon token that is not filtered by UAC +* If connecting to localhost and `pywin32` is installed, the script must be run as a user with Administrator privileges + +### Firewall Setup + +By default, Windows blocks the SMB port 445 and it needs to be opened up before +pypsexec can connect to the host. To do this run either one of the following +commands; + +```powershell +# PowerShell (Windows 8 and Server 2012 or Newer) +Set-NetFirewallRule -Name FPS-SMB-In-TCP -Enabled True + +# CMD (All OS's) +netsh advfirewall firewall set rule name="File and Printer Sharing (SMB-In)" dir=in new enable=Yes +``` + +This will open up inbound traffic to port `445` which is used by SMB. + + +### User Account Control + +In some circumstances, UAC will filter any remote logon token and limit the +rights that are available to it. This causes issues with pypsexec and it will +fail with an `ACCESS_IS_DENIED` error message when trying to interact with the +remote SCMR API. This restriction is enforced in various different scenarios +and to get it working with pypsexec you can either; + +* In a domain environment, use any domain account that is a member of the local `Administrators` group +* Use any local account that is a member of the local `Administrators` group if [LocalAccountTokenFilterPolicy](https://support.microsoft.com/en-us/help/951016/description-of-user-account-control-and-remote-restrictions-in-windows) is set to `1` + * This means any remote logon token will not be filtered and will have the full rights of that user + * By default this is not defined and needs to be created + * This only affects remote tokens, any local tokens/processes will still be limited as per usual +* Use the builtin local Administrator account (SID `S-1-5-21-*-500`) that is created when Windows was installed + * The builtin Administrator account for English installs is typically called `Administrator` but it can be renamed + * This account is typically disabled by default on the desktop variants of Windows, e.g. Windows 7, 8.1, 10 + * When [AdminApprovalMode](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd835564(v=ws.10)#BKMK_BuiltInAdmin) is `Enabled` this will not work. `AdminApprovalMode` is not `Enabled` by default +* Use any local account that is a member of the local `Administrators` group if [EnableLUA](https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-lua-settings-enablelua) is `Disabled` + * Unlike the `LocalAccountTokenFilterPolicy` option, this affects local tokens and processes spawned locally + * This effectively disables UAC for any Administrator accounts and should be avoided + +To set `LocalAccountTokenFilterPolicy` to allow a full token on a remote logon, +run the following PowerShell commands; + +```powershell +$reg_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" +$reg_prop_name = "LocalAccountTokenFilterPolicy" + +$reg_key = Get-Item -Path $reg_path +$reg_prop = $reg_key.GetValue($reg_prop_name) +if ($null -ne $reg_prop) { + Remove-ItemProperty -Path $reg_path -Name $reg_prop_name +} + +New-ItemProperty -Path $reg_path -Name $reg_prop_name -Value 1 -PropertyType DWord +``` + +To get the name of the builtin Administrator (SID `S-1-5-21-*-500`), you can +run the following PowerShell commands; + +```powershell +Add-Type -AssemblyName System.DirectoryServices.AccountManagement +$principal_context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine) +$user_principal = New-Object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal($principal_context) +$searcher = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher($user_principal) +$users = $searcher.FindAll() | Where-Object { $_.Sid -like "*-500" } +$users[0].Name +``` + +The last resort would be to disable UAC for any local Administrator account. +Once again this should be avoided as there are other options available and this +will reduce the security of your Windows host, but to do so you can run the +following PowerShell commands; + +```powershell +$reg_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" +$reg_prop_name = "EnableLUA" + +$reg_key = Get-Item -Path $reg_path +$reg_prop = $reg_key.GetValue($reg_prop_name) +if ($null -ne $reg_prop) { + Remove-ItemProperty -Path $reg_path -Name $reg_prop_name +} + +New-ItemProperty -Path $reg_path -Name $reg_prop_name -Value 0 -PropertyType DWord +``` + +After changing the `EnableLUA` setting, the Windows host needs to be rebooted +before the policies are enacted. + + +## Examples + +Here is an example of how to run a command with this library + +```python +from pypsexec.client import Client + +# creates an encrypted connection to the host with the username and password +c = Client("hostname", username="username", password="password") + +# set encrypt=False for Windows 7, Server 2008 +c = Client("hostname", username="username", password="password", encrypt=False) + +# if Kerberos is available, this will use the default credentials in the +# credential cache +c = Client("hostname") + +# you can also tell it to use a specific Kerberos principal in the cache +# without a password +c = Client("hostname", username="username@DOMAIN.LOCAL") + +c.connect() +try: + c.create_service() + + # After creating the service, you can run multiple exe's without + # reconnecting + + # run a simple cmd.exe program with arguments + stdout, stderr, rc = c.run_executable("cmd.exe", + arguments="/c echo Hello World") + + # run whoami.exe as the SYSTEM account + stdout, stderr, rc = c.run_executable("whoami.exe", use_system_account=True) + + # run command asynchronously (in background), the rc is the PID of the spawned service + stdout, stderr, rc = c.run_executable("longrunning.exe", + arguments="/s other args", + asynchronous=True) + + # run whoami.exe as a specific user + stdout, stderr, rc = c.run_executable("whoami", + arguments="/all", + username="local-user", + password="password", + run_elevated=True) +finally: + c.remove_service() + c.disconnect() +``` + +In the case of a fatal failure, this project may leave behind some the PAExec +payload in `C:\Windows` or the service still installed. As these are uniquely +named they can build up over time. They can be manually removed but you can +also use pypsexec to cleanup them all up at once. To do this run + +```python +from pypsexec.client import Client + +c = Client("server", username="username", password="password") +c.connect() +c.cleanup() # this is where the magic happens +c.disconnect() +``` + +The script will delete any files that match `C:\Windows\PAExec-*` and any +services that match `PAExec-*`. For an individual run, the `remove_service()` +function should still be used. + +### Client Options + +When creating the main pypsexec `Client` object there are some configuration +options that can be set to control the process. These args are; + +* `server`: This needs to be set and is the host or IP address of the server to connect to +* `username`: The username to connect with. Can be `None` if `python-gssapi` is installed and a ticket has been granted in the local credential cache +* `password`: The password for `username`. Can be `None` if `python-gssapi` is installed and a ticket has been granted for the user specified +* `port`: Override the default port of `445` when connecting to the server +* `encrypt`: Whether to encrypt the messages or not, default is `True`. Server 2008, 2008 R2 and Windows 7 hosts do not support SMB Encryption and need this to be set to `False` + + +### Run Executable Options + +When calling `run_executable`, there are multiple kwargs that can define +how the remote process will work. These args are; + +* `executable`: (string) The path to the executable to be run +* `arguments`: (string) Arguments for the executable +* `processors`: (list) A list of processor numbers that the process can run on +* `asynchronous`: (bool) Doesn't wait until the process is complete before returning. The `rc` returned by the function is the `PID` of the async process, default is `False` +* `load_profile`: (bool) Load the user's profile, default is `True` +* `interactive_session`: (int) The session ID to display the interactive process when `interactive=True`, default is `0` +* `interactive`: (bool) Runs the process as an interactive process. The stdout and stderr buffers will be `None` if `True`, default `False` +* `run_elevated`: (bool) When `username` is defined, will elevated permissions, default `False` +* `run_limited`: (bool) When `username` is defined, will run the process under limited permissions, default `False` +* `username`: (string) Used to run the process under a different user than the one that authenticated the SMB session +* `password`: (string) The password for `username` +* `use_system_account`: (bool) Run the process as `NT AUTHORITY\SYSTEM` +* `working_dir`: (string) The working directory of the process, default `C:\Windows\System32` +* `show_ui_on_win_logon`: (bool) Displays the UI on the Winlogon secure desktop when `use_system_account=True`, default `False` +* `priority`: (pypsexec.ProcessPriority) The priority level of the process, default `NORMAL_PRIORITY_CLASS` +* `remote_log_path`: (string) A path on the remote host to log the PAExec service details +* `timeout_seconds`: (int) The maximum time the process can run for, default is `0` (no timeout) +* `stdout`: (pipe.OutputPipe) A class that implements pipe.OutputPipe that controls how the stdout output is processed and returned, will default to returning the byte string of the stdout. Is ignored when `interactive=True` and `asynchronous=True` +* `stderr`: (pipe.OutputPipe) A class that implements pipe.OutputPipe that controls how the stderr output is processed and returned, will default to returning the byte string of the stderr. Is ignored when `interactive=True` and `asynchronous=True` +* `stdin`: (bytes/generator) A byte string or generator that yields a byte string to send over the stdin pipe, does not work with `interactive=True` and `asynchronous=True` +* `wow64`: (bool) Set to `True` to run the executable in 32-bit mode on 64-bit systems. This does nothing on 32-bit systems, default `False` + + +## Logging + +This library uses the builtin Python logging library and can be used to find +out what is happening in the pypsexec process. Log messages are logged to the +`pypsexec` named logger as well as `pypsexec.*` where `*` is each python script +in the `pypsexec` directory. + +A way to enable the logging in your scripts through code is to add the +following to the top of the script being used; + +```python +import logging + +logger = logging.getLogger("pypsexec") +logger.setLevel(logging.DEBUG) # set to logging.INFO if you don't want DEBUG logs +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - ' + '%(message)s') +ch.setFormatter(formatter) +logger.addHandler(ch) +``` + +These logs are generally useful when debugging issues as they give you a more +step by step snapshot of what it is doing and what may be going wrong. The +debug level will also print out a human readable string of each SMB packet that +is sent out from the client but this level can get really verbose. + + +## Testing + +To this module, you need to install some pre-requisites first. This can be done +by running; + +```bash +pip install -r requirements-test.txt + +# you can also run tox by installing tox +pip install tox +``` + +From there to run the basic tests run; + +```bash +py.test -v --cov pypsexec --cov-report term-missing + +# or with tox +tox +``` + +There are extra tests that only run when certain environment variables are set. +To run these tests set the following variables; + +* `PYPSEXEC_SERVER`: The hostname or IP to a Windows host +* `PYPSEXEC_USERNAME`: The username to use authenticate with +* `PYPSEXEC_PASSWORD`: The password for `PYPSEXEC_USERNAME` + +From there, you can just run `tox` or `py.test` with these environment +variables to run the integration tests. + + +## Future + +Some things I would be interested in looking at adding in the future would be + +* Add a Python script that can be called to run adhoc commands like `PsExec.exe` + + + + +%package -n python3-pypsexec +Summary: Run commands on a remote Windows host using SMB/RPC +Provides: python-pypsexec +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-pypsexec +# Python PsExec Library + +[![Test workflow](https://github.com/jborean93/pypsexec/actions/workflows/ci.yml/badge.svg)](https://github.com/jborean93/pypsexec/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/jborean93/pypsexec/branch/master/graph/badge.svg?token=Hi2Nk4RfMF)](https://codecov.io/gh/jborean93/pypsexec) +[![PyPI version](https://badge.fury.io/py/pypsexec.svg)](https://badge.fury.io/py/pypsexec) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jborean93/pypsexec/blob/master/LICENSE) + +This library can run commands on a remote Windows host through Python. This +means that it can be run on any host with Python and does not require any +binaries to be present or a specific OS. It uses SMB/RPC to executable commands +in a similar fashion to the popular PsExec tool. More details on this tool +can be read on +[this blog post](https://www.bloggingforlogging.com/2018/03/12/introducing-psexec-for-python/). + +The executable wrapper that is sent to the service is based on the +[PAExec](https://github.com/poweradminllc/PAExec) library. PAExec is an free, +redistributable and open source equivalent to Microsoft's +[PsExec](https://docs.microsoft.com/en-us/sysinternals/downloads/psexec) +application. This program is stored as a binary in this package and is used +to run the remote service and start the process execution. + +I would like to thank the developers of Power Admin for creating this library +as it has made this library a lot less complex than what it would have been. + + +## Features + +With pypsexec you can run commands of a remote Windows host like you would with +PsExec. Current you can use pypsexec to do the following; + +* Run as a specific local or domain user or the user +* Run as the local SYSTEM account +* Run as an interactive process +* Specify the session the interactive process should run on +* Specify the run level of the user token, `highest` or `limited` +* Set the priority of the process +* Set a timeout for the remote process +* Send input through the stdin pipe to the running process +* Set the processors the process can run on + + +## Further Info + +While this info is not necessary for you to use this library it can help people +understand what is happening under the hood. This library runs the following +steps when running a command; + +* Create an SMB connection to the host +* Copies across the PAExec binary to the `ADMIN$` share of the remote host +* Binds the Windows Service Manager to the opened `IPC$` tree using RPC +* Creates and starts a Windows service as the `SYSTEM` account to run the binary copied +* Connect to the PAExec named pipe the service creates +* Sends the process details to the PAExec service through the pipe +* Send a request to the PAExec service to start the process based on the settings sent +* Connect to the newly spawned process's stdout, stderr, stdin pipe (if not interactive or async) +* Read the stdout/stderr pipe until the process is complete +* Get the return code of the new process +* Stop and remove the PAExec service +* Remove the PAExec binary from the `ADMIN$` share +* Disconnects from the SMB connection + +In the case of a failed process, the PAExec service and binary may not be +removed from the host and may need to be done manually. This is only the case +for a critical error or the cleanup functions not being called. + +By default the data being sent to and from the server is encrypted to stop +people listening in on the network from snooping your data. Unfortunately this +uses SMB encryption which was added in the SMB 3.x dialects so hosts running +Windows 7, Server 2008, or Server 2008 R2 will not work with encryption. + +This means that any data sent over the wire on these older versions of Windows +is viewable by anyone reading those packets. Any input or output of the process +comes through these packets so any secrets sent over the network won't be +encrypted. PAExec tries to reduce this risk by doing a simple XOR scramble of +the settings set in `run_executable` so it isn't plaintext but it can be +decoded by someone who knows the protocol. + + +## Requirements + +* Python 3.6+ +* [smbprotocol](https://github.com/jborean93/smbprotocol) + +To install pypsexec, simply run + +```bash +pip install pypsexec +``` + +This will download the required packages that are required and get your +Python environment ready to do. + +Out of the box, pypsexec supports authenticating to a Windows host with NTLM +authentication but users in a domain environment can take advantage of Kerberos +authentication as well for added security. The Kerberos libraries are an +optional install which can be installed with; + +```bash +# for Debian/Ubuntu/etc: +sudo apt-get install gcc python-dev libkrb5-dev +pip install smbprotocol[kerberos] + +# for RHEL/CentOS/etc: +sudo yum install gcc python-devel krb5-devel krb5-workstation python-devel +pip install smbprotocol[kerberos] +``` + + +## Remote Host Requirements + +The goal of this package to be able to run executables on a vanilla remote +Windows host with as little setup as possible. Unfortunately there is still +some setup required to get working depending on the OS version and type +that is being used. What pypsexec requires on the host is; + +* SMB to be up and running on the Windows port and readable from the Python host +* The `ADMIN$` share to be enabled with read/write access of the user configured +* The above usually means the configured user is an administrator of the Windows host +* At least SMB 2 on the host (Server 2008 and newer) +* The connection user has a full logon token that is not filtered by UAC +* If connecting to localhost and `pywin32` is installed, the script must be run as a user with Administrator privileges + +### Firewall Setup + +By default, Windows blocks the SMB port 445 and it needs to be opened up before +pypsexec can connect to the host. To do this run either one of the following +commands; + +```powershell +# PowerShell (Windows 8 and Server 2012 or Newer) +Set-NetFirewallRule -Name FPS-SMB-In-TCP -Enabled True + +# CMD (All OS's) +netsh advfirewall firewall set rule name="File and Printer Sharing (SMB-In)" dir=in new enable=Yes +``` + +This will open up inbound traffic to port `445` which is used by SMB. + + +### User Account Control + +In some circumstances, UAC will filter any remote logon token and limit the +rights that are available to it. This causes issues with pypsexec and it will +fail with an `ACCESS_IS_DENIED` error message when trying to interact with the +remote SCMR API. This restriction is enforced in various different scenarios +and to get it working with pypsexec you can either; + +* In a domain environment, use any domain account that is a member of the local `Administrators` group +* Use any local account that is a member of the local `Administrators` group if [LocalAccountTokenFilterPolicy](https://support.microsoft.com/en-us/help/951016/description-of-user-account-control-and-remote-restrictions-in-windows) is set to `1` + * This means any remote logon token will not be filtered and will have the full rights of that user + * By default this is not defined and needs to be created + * This only affects remote tokens, any local tokens/processes will still be limited as per usual +* Use the builtin local Administrator account (SID `S-1-5-21-*-500`) that is created when Windows was installed + * The builtin Administrator account for English installs is typically called `Administrator` but it can be renamed + * This account is typically disabled by default on the desktop variants of Windows, e.g. Windows 7, 8.1, 10 + * When [AdminApprovalMode](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd835564(v=ws.10)#BKMK_BuiltInAdmin) is `Enabled` this will not work. `AdminApprovalMode` is not `Enabled` by default +* Use any local account that is a member of the local `Administrators` group if [EnableLUA](https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-lua-settings-enablelua) is `Disabled` + * Unlike the `LocalAccountTokenFilterPolicy` option, this affects local tokens and processes spawned locally + * This effectively disables UAC for any Administrator accounts and should be avoided + +To set `LocalAccountTokenFilterPolicy` to allow a full token on a remote logon, +run the following PowerShell commands; + +```powershell +$reg_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" +$reg_prop_name = "LocalAccountTokenFilterPolicy" + +$reg_key = Get-Item -Path $reg_path +$reg_prop = $reg_key.GetValue($reg_prop_name) +if ($null -ne $reg_prop) { + Remove-ItemProperty -Path $reg_path -Name $reg_prop_name +} + +New-ItemProperty -Path $reg_path -Name $reg_prop_name -Value 1 -PropertyType DWord +``` + +To get the name of the builtin Administrator (SID `S-1-5-21-*-500`), you can +run the following PowerShell commands; + +```powershell +Add-Type -AssemblyName System.DirectoryServices.AccountManagement +$principal_context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine) +$user_principal = New-Object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal($principal_context) +$searcher = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher($user_principal) +$users = $searcher.FindAll() | Where-Object { $_.Sid -like "*-500" } +$users[0].Name +``` + +The last resort would be to disable UAC for any local Administrator account. +Once again this should be avoided as there are other options available and this +will reduce the security of your Windows host, but to do so you can run the +following PowerShell commands; + +```powershell +$reg_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" +$reg_prop_name = "EnableLUA" + +$reg_key = Get-Item -Path $reg_path +$reg_prop = $reg_key.GetValue($reg_prop_name) +if ($null -ne $reg_prop) { + Remove-ItemProperty -Path $reg_path -Name $reg_prop_name +} + +New-ItemProperty -Path $reg_path -Name $reg_prop_name -Value 0 -PropertyType DWord +``` + +After changing the `EnableLUA` setting, the Windows host needs to be rebooted +before the policies are enacted. + + +## Examples + +Here is an example of how to run a command with this library + +```python +from pypsexec.client import Client + +# creates an encrypted connection to the host with the username and password +c = Client("hostname", username="username", password="password") + +# set encrypt=False for Windows 7, Server 2008 +c = Client("hostname", username="username", password="password", encrypt=False) + +# if Kerberos is available, this will use the default credentials in the +# credential cache +c = Client("hostname") + +# you can also tell it to use a specific Kerberos principal in the cache +# without a password +c = Client("hostname", username="username@DOMAIN.LOCAL") + +c.connect() +try: + c.create_service() + + # After creating the service, you can run multiple exe's without + # reconnecting + + # run a simple cmd.exe program with arguments + stdout, stderr, rc = c.run_executable("cmd.exe", + arguments="/c echo Hello World") + + # run whoami.exe as the SYSTEM account + stdout, stderr, rc = c.run_executable("whoami.exe", use_system_account=True) + + # run command asynchronously (in background), the rc is the PID of the spawned service + stdout, stderr, rc = c.run_executable("longrunning.exe", + arguments="/s other args", + asynchronous=True) + + # run whoami.exe as a specific user + stdout, stderr, rc = c.run_executable("whoami", + arguments="/all", + username="local-user", + password="password", + run_elevated=True) +finally: + c.remove_service() + c.disconnect() +``` + +In the case of a fatal failure, this project may leave behind some the PAExec +payload in `C:\Windows` or the service still installed. As these are uniquely +named they can build up over time. They can be manually removed but you can +also use pypsexec to cleanup them all up at once. To do this run + +```python +from pypsexec.client import Client + +c = Client("server", username="username", password="password") +c.connect() +c.cleanup() # this is where the magic happens +c.disconnect() +``` + +The script will delete any files that match `C:\Windows\PAExec-*` and any +services that match `PAExec-*`. For an individual run, the `remove_service()` +function should still be used. + +### Client Options + +When creating the main pypsexec `Client` object there are some configuration +options that can be set to control the process. These args are; + +* `server`: This needs to be set and is the host or IP address of the server to connect to +* `username`: The username to connect with. Can be `None` if `python-gssapi` is installed and a ticket has been granted in the local credential cache +* `password`: The password for `username`. Can be `None` if `python-gssapi` is installed and a ticket has been granted for the user specified +* `port`: Override the default port of `445` when connecting to the server +* `encrypt`: Whether to encrypt the messages or not, default is `True`. Server 2008, 2008 R2 and Windows 7 hosts do not support SMB Encryption and need this to be set to `False` + + +### Run Executable Options + +When calling `run_executable`, there are multiple kwargs that can define +how the remote process will work. These args are; + +* `executable`: (string) The path to the executable to be run +* `arguments`: (string) Arguments for the executable +* `processors`: (list) A list of processor numbers that the process can run on +* `asynchronous`: (bool) Doesn't wait until the process is complete before returning. The `rc` returned by the function is the `PID` of the async process, default is `False` +* `load_profile`: (bool) Load the user's profile, default is `True` +* `interactive_session`: (int) The session ID to display the interactive process when `interactive=True`, default is `0` +* `interactive`: (bool) Runs the process as an interactive process. The stdout and stderr buffers will be `None` if `True`, default `False` +* `run_elevated`: (bool) When `username` is defined, will elevated permissions, default `False` +* `run_limited`: (bool) When `username` is defined, will run the process under limited permissions, default `False` +* `username`: (string) Used to run the process under a different user than the one that authenticated the SMB session +* `password`: (string) The password for `username` +* `use_system_account`: (bool) Run the process as `NT AUTHORITY\SYSTEM` +* `working_dir`: (string) The working directory of the process, default `C:\Windows\System32` +* `show_ui_on_win_logon`: (bool) Displays the UI on the Winlogon secure desktop when `use_system_account=True`, default `False` +* `priority`: (pypsexec.ProcessPriority) The priority level of the process, default `NORMAL_PRIORITY_CLASS` +* `remote_log_path`: (string) A path on the remote host to log the PAExec service details +* `timeout_seconds`: (int) The maximum time the process can run for, default is `0` (no timeout) +* `stdout`: (pipe.OutputPipe) A class that implements pipe.OutputPipe that controls how the stdout output is processed and returned, will default to returning the byte string of the stdout. Is ignored when `interactive=True` and `asynchronous=True` +* `stderr`: (pipe.OutputPipe) A class that implements pipe.OutputPipe that controls how the stderr output is processed and returned, will default to returning the byte string of the stderr. Is ignored when `interactive=True` and `asynchronous=True` +* `stdin`: (bytes/generator) A byte string or generator that yields a byte string to send over the stdin pipe, does not work with `interactive=True` and `asynchronous=True` +* `wow64`: (bool) Set to `True` to run the executable in 32-bit mode on 64-bit systems. This does nothing on 32-bit systems, default `False` + + +## Logging + +This library uses the builtin Python logging library and can be used to find +out what is happening in the pypsexec process. Log messages are logged to the +`pypsexec` named logger as well as `pypsexec.*` where `*` is each python script +in the `pypsexec` directory. + +A way to enable the logging in your scripts through code is to add the +following to the top of the script being used; + +```python +import logging + +logger = logging.getLogger("pypsexec") +logger.setLevel(logging.DEBUG) # set to logging.INFO if you don't want DEBUG logs +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - ' + '%(message)s') +ch.setFormatter(formatter) +logger.addHandler(ch) +``` + +These logs are generally useful when debugging issues as they give you a more +step by step snapshot of what it is doing and what may be going wrong. The +debug level will also print out a human readable string of each SMB packet that +is sent out from the client but this level can get really verbose. + + +## Testing + +To this module, you need to install some pre-requisites first. This can be done +by running; + +```bash +pip install -r requirements-test.txt + +# you can also run tox by installing tox +pip install tox +``` + +From there to run the basic tests run; + +```bash +py.test -v --cov pypsexec --cov-report term-missing + +# or with tox +tox +``` + +There are extra tests that only run when certain environment variables are set. +To run these tests set the following variables; + +* `PYPSEXEC_SERVER`: The hostname or IP to a Windows host +* `PYPSEXEC_USERNAME`: The username to use authenticate with +* `PYPSEXEC_PASSWORD`: The password for `PYPSEXEC_USERNAME` + +From there, you can just run `tox` or `py.test` with these environment +variables to run the integration tests. + + +## Future + +Some things I would be interested in looking at adding in the future would be + +* Add a Python script that can be called to run adhoc commands like `PsExec.exe` + + + + +%package help +Summary: Development documents and examples for pypsexec +Provides: python3-pypsexec-doc +%description help +# Python PsExec Library + +[![Test workflow](https://github.com/jborean93/pypsexec/actions/workflows/ci.yml/badge.svg)](https://github.com/jborean93/pypsexec/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/jborean93/pypsexec/branch/master/graph/badge.svg?token=Hi2Nk4RfMF)](https://codecov.io/gh/jborean93/pypsexec) +[![PyPI version](https://badge.fury.io/py/pypsexec.svg)](https://badge.fury.io/py/pypsexec) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jborean93/pypsexec/blob/master/LICENSE) + +This library can run commands on a remote Windows host through Python. This +means that it can be run on any host with Python and does not require any +binaries to be present or a specific OS. It uses SMB/RPC to executable commands +in a similar fashion to the popular PsExec tool. More details on this tool +can be read on +[this blog post](https://www.bloggingforlogging.com/2018/03/12/introducing-psexec-for-python/). + +The executable wrapper that is sent to the service is based on the +[PAExec](https://github.com/poweradminllc/PAExec) library. PAExec is an free, +redistributable and open source equivalent to Microsoft's +[PsExec](https://docs.microsoft.com/en-us/sysinternals/downloads/psexec) +application. This program is stored as a binary in this package and is used +to run the remote service and start the process execution. + +I would like to thank the developers of Power Admin for creating this library +as it has made this library a lot less complex than what it would have been. + + +## Features + +With pypsexec you can run commands of a remote Windows host like you would with +PsExec. Current you can use pypsexec to do the following; + +* Run as a specific local or domain user or the user +* Run as the local SYSTEM account +* Run as an interactive process +* Specify the session the interactive process should run on +* Specify the run level of the user token, `highest` or `limited` +* Set the priority of the process +* Set a timeout for the remote process +* Send input through the stdin pipe to the running process +* Set the processors the process can run on + + +## Further Info + +While this info is not necessary for you to use this library it can help people +understand what is happening under the hood. This library runs the following +steps when running a command; + +* Create an SMB connection to the host +* Copies across the PAExec binary to the `ADMIN$` share of the remote host +* Binds the Windows Service Manager to the opened `IPC$` tree using RPC +* Creates and starts a Windows service as the `SYSTEM` account to run the binary copied +* Connect to the PAExec named pipe the service creates +* Sends the process details to the PAExec service through the pipe +* Send a request to the PAExec service to start the process based on the settings sent +* Connect to the newly spawned process's stdout, stderr, stdin pipe (if not interactive or async) +* Read the stdout/stderr pipe until the process is complete +* Get the return code of the new process +* Stop and remove the PAExec service +* Remove the PAExec binary from the `ADMIN$` share +* Disconnects from the SMB connection + +In the case of a failed process, the PAExec service and binary may not be +removed from the host and may need to be done manually. This is only the case +for a critical error or the cleanup functions not being called. + +By default the data being sent to and from the server is encrypted to stop +people listening in on the network from snooping your data. Unfortunately this +uses SMB encryption which was added in the SMB 3.x dialects so hosts running +Windows 7, Server 2008, or Server 2008 R2 will not work with encryption. + +This means that any data sent over the wire on these older versions of Windows +is viewable by anyone reading those packets. Any input or output of the process +comes through these packets so any secrets sent over the network won't be +encrypted. PAExec tries to reduce this risk by doing a simple XOR scramble of +the settings set in `run_executable` so it isn't plaintext but it can be +decoded by someone who knows the protocol. + + +## Requirements + +* Python 3.6+ +* [smbprotocol](https://github.com/jborean93/smbprotocol) + +To install pypsexec, simply run + +```bash +pip install pypsexec +``` + +This will download the required packages that are required and get your +Python environment ready to do. + +Out of the box, pypsexec supports authenticating to a Windows host with NTLM +authentication but users in a domain environment can take advantage of Kerberos +authentication as well for added security. The Kerberos libraries are an +optional install which can be installed with; + +```bash +# for Debian/Ubuntu/etc: +sudo apt-get install gcc python-dev libkrb5-dev +pip install smbprotocol[kerberos] + +# for RHEL/CentOS/etc: +sudo yum install gcc python-devel krb5-devel krb5-workstation python-devel +pip install smbprotocol[kerberos] +``` + + +## Remote Host Requirements + +The goal of this package to be able to run executables on a vanilla remote +Windows host with as little setup as possible. Unfortunately there is still +some setup required to get working depending on the OS version and type +that is being used. What pypsexec requires on the host is; + +* SMB to be up and running on the Windows port and readable from the Python host +* The `ADMIN$` share to be enabled with read/write access of the user configured +* The above usually means the configured user is an administrator of the Windows host +* At least SMB 2 on the host (Server 2008 and newer) +* The connection user has a full logon token that is not filtered by UAC +* If connecting to localhost and `pywin32` is installed, the script must be run as a user with Administrator privileges + +### Firewall Setup + +By default, Windows blocks the SMB port 445 and it needs to be opened up before +pypsexec can connect to the host. To do this run either one of the following +commands; + +```powershell +# PowerShell (Windows 8 and Server 2012 or Newer) +Set-NetFirewallRule -Name FPS-SMB-In-TCP -Enabled True + +# CMD (All OS's) +netsh advfirewall firewall set rule name="File and Printer Sharing (SMB-In)" dir=in new enable=Yes +``` + +This will open up inbound traffic to port `445` which is used by SMB. + + +### User Account Control + +In some circumstances, UAC will filter any remote logon token and limit the +rights that are available to it. This causes issues with pypsexec and it will +fail with an `ACCESS_IS_DENIED` error message when trying to interact with the +remote SCMR API. This restriction is enforced in various different scenarios +and to get it working with pypsexec you can either; + +* In a domain environment, use any domain account that is a member of the local `Administrators` group +* Use any local account that is a member of the local `Administrators` group if [LocalAccountTokenFilterPolicy](https://support.microsoft.com/en-us/help/951016/description-of-user-account-control-and-remote-restrictions-in-windows) is set to `1` + * This means any remote logon token will not be filtered and will have the full rights of that user + * By default this is not defined and needs to be created + * This only affects remote tokens, any local tokens/processes will still be limited as per usual +* Use the builtin local Administrator account (SID `S-1-5-21-*-500`) that is created when Windows was installed + * The builtin Administrator account for English installs is typically called `Administrator` but it can be renamed + * This account is typically disabled by default on the desktop variants of Windows, e.g. Windows 7, 8.1, 10 + * When [AdminApprovalMode](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd835564(v=ws.10)#BKMK_BuiltInAdmin) is `Enabled` this will not work. `AdminApprovalMode` is not `Enabled` by default +* Use any local account that is a member of the local `Administrators` group if [EnableLUA](https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-lua-settings-enablelua) is `Disabled` + * Unlike the `LocalAccountTokenFilterPolicy` option, this affects local tokens and processes spawned locally + * This effectively disables UAC for any Administrator accounts and should be avoided + +To set `LocalAccountTokenFilterPolicy` to allow a full token on a remote logon, +run the following PowerShell commands; + +```powershell +$reg_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" +$reg_prop_name = "LocalAccountTokenFilterPolicy" + +$reg_key = Get-Item -Path $reg_path +$reg_prop = $reg_key.GetValue($reg_prop_name) +if ($null -ne $reg_prop) { + Remove-ItemProperty -Path $reg_path -Name $reg_prop_name +} + +New-ItemProperty -Path $reg_path -Name $reg_prop_name -Value 1 -PropertyType DWord +``` + +To get the name of the builtin Administrator (SID `S-1-5-21-*-500`), you can +run the following PowerShell commands; + +```powershell +Add-Type -AssemblyName System.DirectoryServices.AccountManagement +$principal_context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine) +$user_principal = New-Object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal($principal_context) +$searcher = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher($user_principal) +$users = $searcher.FindAll() | Where-Object { $_.Sid -like "*-500" } +$users[0].Name +``` + +The last resort would be to disable UAC for any local Administrator account. +Once again this should be avoided as there are other options available and this +will reduce the security of your Windows host, but to do so you can run the +following PowerShell commands; + +```powershell +$reg_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" +$reg_prop_name = "EnableLUA" + +$reg_key = Get-Item -Path $reg_path +$reg_prop = $reg_key.GetValue($reg_prop_name) +if ($null -ne $reg_prop) { + Remove-ItemProperty -Path $reg_path -Name $reg_prop_name +} + +New-ItemProperty -Path $reg_path -Name $reg_prop_name -Value 0 -PropertyType DWord +``` + +After changing the `EnableLUA` setting, the Windows host needs to be rebooted +before the policies are enacted. + + +## Examples + +Here is an example of how to run a command with this library + +```python +from pypsexec.client import Client + +# creates an encrypted connection to the host with the username and password +c = Client("hostname", username="username", password="password") + +# set encrypt=False for Windows 7, Server 2008 +c = Client("hostname", username="username", password="password", encrypt=False) + +# if Kerberos is available, this will use the default credentials in the +# credential cache +c = Client("hostname") + +# you can also tell it to use a specific Kerberos principal in the cache +# without a password +c = Client("hostname", username="username@DOMAIN.LOCAL") + +c.connect() +try: + c.create_service() + + # After creating the service, you can run multiple exe's without + # reconnecting + + # run a simple cmd.exe program with arguments + stdout, stderr, rc = c.run_executable("cmd.exe", + arguments="/c echo Hello World") + + # run whoami.exe as the SYSTEM account + stdout, stderr, rc = c.run_executable("whoami.exe", use_system_account=True) + + # run command asynchronously (in background), the rc is the PID of the spawned service + stdout, stderr, rc = c.run_executable("longrunning.exe", + arguments="/s other args", + asynchronous=True) + + # run whoami.exe as a specific user + stdout, stderr, rc = c.run_executable("whoami", + arguments="/all", + username="local-user", + password="password", + run_elevated=True) +finally: + c.remove_service() + c.disconnect() +``` + +In the case of a fatal failure, this project may leave behind some the PAExec +payload in `C:\Windows` or the service still installed. As these are uniquely +named they can build up over time. They can be manually removed but you can +also use pypsexec to cleanup them all up at once. To do this run + +```python +from pypsexec.client import Client + +c = Client("server", username="username", password="password") +c.connect() +c.cleanup() # this is where the magic happens +c.disconnect() +``` + +The script will delete any files that match `C:\Windows\PAExec-*` and any +services that match `PAExec-*`. For an individual run, the `remove_service()` +function should still be used. + +### Client Options + +When creating the main pypsexec `Client` object there are some configuration +options that can be set to control the process. These args are; + +* `server`: This needs to be set and is the host or IP address of the server to connect to +* `username`: The username to connect with. Can be `None` if `python-gssapi` is installed and a ticket has been granted in the local credential cache +* `password`: The password for `username`. Can be `None` if `python-gssapi` is installed and a ticket has been granted for the user specified +* `port`: Override the default port of `445` when connecting to the server +* `encrypt`: Whether to encrypt the messages or not, default is `True`. Server 2008, 2008 R2 and Windows 7 hosts do not support SMB Encryption and need this to be set to `False` + + +### Run Executable Options + +When calling `run_executable`, there are multiple kwargs that can define +how the remote process will work. These args are; + +* `executable`: (string) The path to the executable to be run +* `arguments`: (string) Arguments for the executable +* `processors`: (list) A list of processor numbers that the process can run on +* `asynchronous`: (bool) Doesn't wait until the process is complete before returning. The `rc` returned by the function is the `PID` of the async process, default is `False` +* `load_profile`: (bool) Load the user's profile, default is `True` +* `interactive_session`: (int) The session ID to display the interactive process when `interactive=True`, default is `0` +* `interactive`: (bool) Runs the process as an interactive process. The stdout and stderr buffers will be `None` if `True`, default `False` +* `run_elevated`: (bool) When `username` is defined, will elevated permissions, default `False` +* `run_limited`: (bool) When `username` is defined, will run the process under limited permissions, default `False` +* `username`: (string) Used to run the process under a different user than the one that authenticated the SMB session +* `password`: (string) The password for `username` +* `use_system_account`: (bool) Run the process as `NT AUTHORITY\SYSTEM` +* `working_dir`: (string) The working directory of the process, default `C:\Windows\System32` +* `show_ui_on_win_logon`: (bool) Displays the UI on the Winlogon secure desktop when `use_system_account=True`, default `False` +* `priority`: (pypsexec.ProcessPriority) The priority level of the process, default `NORMAL_PRIORITY_CLASS` +* `remote_log_path`: (string) A path on the remote host to log the PAExec service details +* `timeout_seconds`: (int) The maximum time the process can run for, default is `0` (no timeout) +* `stdout`: (pipe.OutputPipe) A class that implements pipe.OutputPipe that controls how the stdout output is processed and returned, will default to returning the byte string of the stdout. Is ignored when `interactive=True` and `asynchronous=True` +* `stderr`: (pipe.OutputPipe) A class that implements pipe.OutputPipe that controls how the stderr output is processed and returned, will default to returning the byte string of the stderr. Is ignored when `interactive=True` and `asynchronous=True` +* `stdin`: (bytes/generator) A byte string or generator that yields a byte string to send over the stdin pipe, does not work with `interactive=True` and `asynchronous=True` +* `wow64`: (bool) Set to `True` to run the executable in 32-bit mode on 64-bit systems. This does nothing on 32-bit systems, default `False` + + +## Logging + +This library uses the builtin Python logging library and can be used to find +out what is happening in the pypsexec process. Log messages are logged to the +`pypsexec` named logger as well as `pypsexec.*` where `*` is each python script +in the `pypsexec` directory. + +A way to enable the logging in your scripts through code is to add the +following to the top of the script being used; + +```python +import logging + +logger = logging.getLogger("pypsexec") +logger.setLevel(logging.DEBUG) # set to logging.INFO if you don't want DEBUG logs +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - ' + '%(message)s') +ch.setFormatter(formatter) +logger.addHandler(ch) +``` + +These logs are generally useful when debugging issues as they give you a more +step by step snapshot of what it is doing and what may be going wrong. The +debug level will also print out a human readable string of each SMB packet that +is sent out from the client but this level can get really verbose. + + +## Testing + +To this module, you need to install some pre-requisites first. This can be done +by running; + +```bash +pip install -r requirements-test.txt + +# you can also run tox by installing tox +pip install tox +``` + +From there to run the basic tests run; + +```bash +py.test -v --cov pypsexec --cov-report term-missing + +# or with tox +tox +``` + +There are extra tests that only run when certain environment variables are set. +To run these tests set the following variables; + +* `PYPSEXEC_SERVER`: The hostname or IP to a Windows host +* `PYPSEXEC_USERNAME`: The username to use authenticate with +* `PYPSEXEC_PASSWORD`: The password for `PYPSEXEC_USERNAME` + +From there, you can just run `tox` or `py.test` with these environment +variables to run the integration tests. + + +## Future + +Some things I would be interested in looking at adding in the future would be + +* Add a Python script that can be called to run adhoc commands like `PsExec.exe` + + + + +%prep +%autosetup -n pypsexec-0.3.0 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-pypsexec -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Mon Apr 10 2023 Python_Bot - 0.3.0-1 +- Package Spec generated diff --git a/sources b/sources new file mode 100644 index 0000000..35d3f2d --- /dev/null +++ b/sources @@ -0,0 +1 @@ +8df0532154fe8503c6f7b6aed9cb96d9 pypsexec-0.3.0.tar.gz -- cgit v1.2.3