-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
180 lines (167 loc) · 6.25 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/python3
# Developed by Rui Lopes (ruilopes.com). Released under the MIT license.
import argparse
import base64
import logging
import os
import sys
import textwrap
import typing
from pypsrp.client import WSMan, Client
from pypsrp.shell import WinRS, Process, SignalCode, CommandState
from pypsrp._utils import to_unicode
def split_lines(buffer, encoding):
lines = []
for line in to_unicode(buffer, encoding).splitlines(True):
if line[-1] != '\r' and line[-1] != '\n':
return line.encode(encoding), lines
lines.append(line.rstrip())
return b'', lines
def execute_process(conn: WSMan, env: typing.List[str], cmd: str, args: typing.List[str]):
encoding = '437'
environment = {}
for e in env:
parts = e.split('=', 2)
key = parts[0]
value = parts[1] if len(parts) > 1 else os.getenv(key, '')
environment[key] = value
if len(environment) == 0:
environment = None
with WinRS(conn, environment=environment, no_profile=False) as shell:
process = Process(shell, cmd, args, no_shell=True)
process.begin_invoke()
while not process.state == CommandState.DONE:
process.poll_invoke()
process.stdout, stdout = split_lines(process.stdout, encoding)
for line in stdout:
print(line)
process.end_invoke()
process.signal(SignalCode.CTRL_C)
exit_code = process.rc if process.rc is not None else -1
stdout = to_unicode(process.stdout, encoding)
stderr = Client.sanitise_clixml(to_unicode(process.stderr, encoding))
if stdout:
print(stdout)
if stderr:
sys.stdout.flush()
print(stderr, file=sys.stderr)
return exit_code
def execute_main(args):
script = sys.stdin.read() if args.script == '-' else args.script
with WSMan(
server=args.host,
port=args.port,
ssl=args.ssl,
auth=args.auth,
encryption=args.encryption,
username=args.username,
password=args.password) as conn:
return execute_process(conn, args.env, "PowerShell.exe", [
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
"-OutputFormat", "Text",
"-EncodedCommand",
base64.b64encode(script.encode('utf-16le')).decode('ascii')])
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=textwrap.dedent('''\
remote execute a powershell command on a windows machine using winrm/psrp.
example:
%(prog)s -v execute
'''))
parser.add_argument(
'--verbose',
'-v',
default=0,
action='count',
help='verbosity level. specify multiple to increase logging.')
subparsers = parser.add_subparsers(help='sub-command help')
execute_parser = subparsers.add_parser('execute', help='execute remote powershell script')
execute_parser.add_argument(
'--host',
default='windows.example.com',
help='host.')
execute_parser.add_argument(
'--port',
type=int,
default=5985,
help='port.')
execute_parser.add_argument(
'--ssl',
type=bool,
default=False,
help='ssl.')
execute_parser.add_argument(
'--auth',
default='credssp',
help='auth.')
execute_parser.add_argument(
'--encryption',
default='never',
help='encryption.')
execute_parser.add_argument(
'--username',
default='vagrant',
help='username.')
execute_parser.add_argument(
'--password',
default='vagrant',
help='password.')
execute_parser.add_argument(
'--env',
action='append',
default=[],
help='environment variables.')
execute_parser.add_argument(
'--script',
default=textwrap.dedent('''\
$FormatEnumerationLimit = -1
function Write-Title($title) {
Write-Output "`n#`n# $title`n#`n"
}
Write-Title 'User rights'
whoami /all
Write-Title 'UAC remote settings'
# dump the UAC remote settings.
# 0=This value builds a filtered token. It's the default value. The administrator credentials are removed.
# 1=This value builds an elevated token.
# see https://learn.microsoft.com/en-US/troubleshoot/windows-server/windows-security/user-account-control-and-remote-restriction
$localAccountTokenFilterPolicy = (
Get-ItemProperty `
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System `
-Name LocalAccountTokenFilterPolicy
).LocalAccountTokenFilterPolicy
Write-Output "LocalAccountTokenFilterPolicy=$localAccountTokenFilterPolicy"
Write-Title 'Environment variables'
dir env: `
| Sort-Object -Property Name `
| Format-Table -AutoSize `
| Out-String -Stream -Width ([int]::MaxValue) `
| ForEach-Object {$_.TrimEnd()}
# show that we can stream lines in realtime.
Write-Title 'Slowly write lines'
for ($i = 3; $i -gt 0; --$i) {
Write-Output "T-$i"
Start-Sleep -Seconds 1
}
# # throw an error to see how they appear.
# Write-Title 'Throw error'
# throw "ops"
'''),
help='powershell script to execute or - to read it from stdin.')
execute_parser.set_defaults(sub_command=execute_main)
args = parser.parse_args()
LOGGING_FORMAT = '%(asctime)-15s %(levelname)s %(name)s: %(message)s'
if args.verbose >= 3:
logging.basicConfig(level=logging.DEBUG, format=LOGGING_FORMAT)
from http.client import HTTPConnection
HTTPConnection.debuglevel = 1
elif args.verbose >= 2:
logging.basicConfig(level=logging.DEBUG, format=LOGGING_FORMAT)
elif args.verbose >= 1:
logging.basicConfig(level=logging.INFO, format=LOGGING_FORMAT)
return args.sub_command(args)
sys.exit(main())