evil-winrm 3.9 → 4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +0 -0
- data/evil-winrm.rb +121 -50
- metadata +39 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 30c79c310ded67cef1d5bf2b58e0a40fab19f4677a35c3d6c60f5cd8c50d838e
|
|
4
|
+
data.tar.gz: 434a47d6a124f8769fe549ec89e40fe7b2c582fd48c023c72e7944697cc9efbb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: baa637901d809d708be6580a8cc12ad6296929d246aebc4d31e225c34aac31de1ac02b174141f5bda902c1c675c2000bd29c866a903f4c2cc0738183ca50be82
|
|
7
|
+
data.tar.gz: 5c318e9333a6d934a84dece396a1c189819545bf6ef7c61d801a65a062128561e7d4b8b2b0bfe323155ca5547d466d28051f4b8fcc28c038397b46e58c59e96b
|
data/LICENSE
CHANGED
|
File without changes
|
data/evil-winrm.rb
CHANGED
|
@@ -18,11 +18,12 @@ require 'time'
|
|
|
18
18
|
require 'fileutils'
|
|
19
19
|
require 'logger'
|
|
20
20
|
require 'shellwords'
|
|
21
|
+
require 'thread'
|
|
21
22
|
|
|
22
23
|
# Constants
|
|
23
24
|
|
|
24
25
|
# Version
|
|
25
|
-
VERSION = '
|
|
26
|
+
VERSION = '4.1'
|
|
26
27
|
|
|
27
28
|
# Msg types
|
|
28
29
|
TYPE_INFO = 0
|
|
@@ -34,7 +35,7 @@ TYPE_SUCCESS = 4
|
|
|
34
35
|
# Global vars
|
|
35
36
|
|
|
36
37
|
# Available commands
|
|
37
|
-
$LIST = %w[Bypass-4MSI services upload download clear cls menu exit]
|
|
38
|
+
$LIST = %w[Bypass-4MSI services upload download clear cls menu exit quit]
|
|
38
39
|
$COMMANDS = $LIST.dup
|
|
39
40
|
$CMDS = $COMMANDS.clone
|
|
40
41
|
$LISTASSEM = [''].sort
|
|
@@ -94,6 +95,9 @@ $WORDS_RANDOM_CASE = [
|
|
|
94
95
|
$colors_enabled = true
|
|
95
96
|
$check_rpath_completion = true
|
|
96
97
|
|
|
98
|
+
# Flush stdout after each write
|
|
99
|
+
$stdout.sync = true
|
|
100
|
+
|
|
97
101
|
# Path for ps1 scripts and exec files
|
|
98
102
|
$scripts_path = ''
|
|
99
103
|
$executables_path = ''
|
|
@@ -155,7 +159,10 @@ module WinRM
|
|
|
155
159
|
command = "Get-ChildItem #{remote_path} | Select-Object Name"
|
|
156
160
|
|
|
157
161
|
@connection.shell(:powershell) { |e| e.run(command) }.stdout.strip.split(/\n/).drop(2).each do |file|
|
|
158
|
-
|
|
162
|
+
sanitized_path = File.basename(file.strip)
|
|
163
|
+
next if sanitized_path.empty? || sanitized_path == '.' || sanitized_path == '..'
|
|
164
|
+
|
|
165
|
+
download(File.join(remote_file_path.to_s, file.strip), File.join(local_path, sanitized_path), chunk_size, false)
|
|
159
166
|
end
|
|
160
167
|
end
|
|
161
168
|
|
|
@@ -1073,6 +1080,80 @@ class EvilWinRM
|
|
|
1073
1080
|
ccache_path
|
|
1074
1081
|
end
|
|
1075
1082
|
|
|
1083
|
+
# True when the exception indicates the WinRM session is dead or was never established
|
|
1084
|
+
def connection_error?(error)
|
|
1085
|
+
error_msg = error.message.to_s.downcase
|
|
1086
|
+
error_class = error.class.to_s
|
|
1087
|
+
|
|
1088
|
+
return true if error_msg.match?(/timeout|connection|closed|broken|reset|refused|unreachable|unauthorized|digest|ssl|ntlm|spnego|kerberos|gssapi|wsman|winrm|http|401|403|500|503|expired|empty response/)
|
|
1089
|
+
return true if error_class.match?(/Timeout|Connection|Socket|HTTP|WinRM|GSSAPI|GssApi|OpenSSL|Errno/)
|
|
1090
|
+
|
|
1091
|
+
false
|
|
1092
|
+
end
|
|
1093
|
+
|
|
1094
|
+
# WinRM::Connection#shell is lazy: the TCP/WinRM handshake happens on the first command.
|
|
1095
|
+
# Never show the interactive prompt unless that command actually succeeds.
|
|
1096
|
+
def verify_remote_connection(shell)
|
|
1097
|
+
result = shell.run('(get-location).path')
|
|
1098
|
+
pwd = result&.output.to_s.strip
|
|
1099
|
+
raise 'Empty response from remote endpoint; session is not ready' if pwd.empty?
|
|
1100
|
+
|
|
1101
|
+
pwd
|
|
1102
|
+
rescue => e
|
|
1103
|
+
print_message('Cannot establish connection to remote endpoint. Check credentials and network.', TYPE_ERROR, true, $logger)
|
|
1104
|
+
print_message("#{e.class}: #{e.message}", TYPE_ERROR, true, $logger)
|
|
1105
|
+
custom_exit(1, false)
|
|
1106
|
+
end
|
|
1107
|
+
|
|
1108
|
+
def handle_lost_connection(error)
|
|
1109
|
+
puts
|
|
1110
|
+
print_message("Connection timeout or error occurred: #{error.class} - #{error.message}", TYPE_ERROR, true, $logger)
|
|
1111
|
+
print_message('Cleaning up and exiting...', TYPE_WARNING, true, $logger)
|
|
1112
|
+
custom_exit(1, false)
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1115
|
+
# Keep the remote shell active while the local prompt is idle.
|
|
1116
|
+
# Without this, Windows marks the shell Disconnected after ~1 minute of ShellInactivity
|
|
1117
|
+
# and the next command fails even though the prompt still looks connected.
|
|
1118
|
+
def decorate_shell_with_keepalive(shell, interval = 30)
|
|
1119
|
+
mutex = Mutex.new
|
|
1120
|
+
state = { last_activity: Time.now, stop: false }
|
|
1121
|
+
original_run = shell.method(:run)
|
|
1122
|
+
|
|
1123
|
+
shell.define_singleton_method(:run) do |*args, &block|
|
|
1124
|
+
mutex.synchronize do
|
|
1125
|
+
result = original_run.call(*args, &block)
|
|
1126
|
+
state[:last_activity] = Time.now
|
|
1127
|
+
result
|
|
1128
|
+
end
|
|
1129
|
+
end
|
|
1130
|
+
|
|
1131
|
+
thread = Thread.new do
|
|
1132
|
+
Thread.current.abort_on_exception = false
|
|
1133
|
+
until state[:stop]
|
|
1134
|
+
sleep(interval)
|
|
1135
|
+
break if state[:stop]
|
|
1136
|
+
next if (Time.now - state[:last_activity]) < interval
|
|
1137
|
+
next unless shell.shell_id
|
|
1138
|
+
|
|
1139
|
+
begin
|
|
1140
|
+
shell.run('[void]0')
|
|
1141
|
+
rescue StandardError
|
|
1142
|
+
# The next user command will surface a dead session
|
|
1143
|
+
end
|
|
1144
|
+
end
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
lambda do
|
|
1148
|
+
state[:stop] = true
|
|
1149
|
+
begin
|
|
1150
|
+
thread.wakeup
|
|
1151
|
+
rescue ThreadError
|
|
1152
|
+
end
|
|
1153
|
+
thread.join(1)
|
|
1154
|
+
end
|
|
1155
|
+
end
|
|
1156
|
+
|
|
1076
1157
|
# Main function
|
|
1077
1158
|
def main
|
|
1078
1159
|
arguments
|
|
@@ -1128,7 +1209,14 @@ class EvilWinRM
|
|
|
1128
1209
|
time = Time.now.to_i
|
|
1129
1210
|
print_message('Establishing connection to remote endpoint', TYPE_INFO)
|
|
1130
1211
|
$conn.shell(:powershell) do |shell|
|
|
1212
|
+
stop_keepalive = nil
|
|
1131
1213
|
begin
|
|
1214
|
+
last_pwd = verify_remote_connection(shell)
|
|
1215
|
+
pwd = last_pwd
|
|
1216
|
+
skip_pwd_refresh = true
|
|
1217
|
+
print_message('Connection successful', TYPE_INFO)
|
|
1218
|
+
stop_keepalive ||= decorate_shell_with_keepalive(shell)
|
|
1219
|
+
|
|
1132
1220
|
completion = proc do |str|
|
|
1133
1221
|
case
|
|
1134
1222
|
when Readline.line_buffer =~ /help.*/i
|
|
@@ -1198,32 +1286,22 @@ class EvilWinRM
|
|
|
1198
1286
|
# Load history for this host/user
|
|
1199
1287
|
load_history
|
|
1200
1288
|
|
|
1201
|
-
until command
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
ENV['KRB5CCNAME'] = $original_krb5ccname
|
|
1217
|
-
elsif defined?($original_krb5ccname) && $original_krb5ccname.nil?
|
|
1218
|
-
ENV.delete('KRB5CCNAME') if ENV.key?('KRB5CCNAME')
|
|
1219
|
-
end
|
|
1220
|
-
rescue => cleanup_error
|
|
1221
|
-
# Ignore cleanup errors
|
|
1289
|
+
until %w[exit quit].include?(command) do
|
|
1290
|
+
if skip_pwd_refresh
|
|
1291
|
+
skip_pwd_refresh = false
|
|
1292
|
+
else
|
|
1293
|
+
begin
|
|
1294
|
+
new_pwd = shell.run('(get-location).path').output.to_s.strip
|
|
1295
|
+
unless new_pwd.empty?
|
|
1296
|
+
pwd = new_pwd
|
|
1297
|
+
last_pwd = pwd
|
|
1298
|
+
end
|
|
1299
|
+
rescue => e
|
|
1300
|
+
if connection_error?(e)
|
|
1301
|
+
handle_lost_connection(e)
|
|
1302
|
+
else
|
|
1303
|
+
pwd = last_pwd
|
|
1222
1304
|
end
|
|
1223
|
-
custom_exit(1, false)
|
|
1224
|
-
else
|
|
1225
|
-
# For other errors, try to continue with a default pwd
|
|
1226
|
-
pwd = "C:\\"
|
|
1227
1305
|
end
|
|
1228
1306
|
end
|
|
1229
1307
|
|
|
@@ -1233,8 +1311,14 @@ class EvilWinRM
|
|
|
1233
1311
|
command = Readline.readline("*Evil-WinRM* PS #{pwd}> ", true)
|
|
1234
1312
|
end
|
|
1235
1313
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1314
|
+
if command.nil?
|
|
1315
|
+
custom_exit(0)
|
|
1316
|
+
end
|
|
1317
|
+
|
|
1318
|
+
break if %w[exit quit].include?(command.strip.downcase)
|
|
1319
|
+
|
|
1320
|
+
# Handle Ctrl+L if it returns as a special character
|
|
1321
|
+
if command == "\f"
|
|
1238
1322
|
clear_screen
|
|
1239
1323
|
command = ''
|
|
1240
1324
|
next
|
|
@@ -1511,27 +1595,9 @@ class EvilWinRM
|
|
|
1511
1595
|
end
|
|
1512
1596
|
$logger.info(output_logger)
|
|
1513
1597
|
rescue => e
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
if error_msg.include?('timeout') || error_msg.include?('connection') ||
|
|
1517
|
-
error_msg.include?('closed') || error_msg.include?('broken') ||
|
|
1518
|
-
e.class.to_s.include?('Timeout') || e.class.to_s.include?('Connection')
|
|
1519
|
-
puts
|
|
1520
|
-
print_message("Connection timeout or error occurred: #{e.class} - #{e.message}", TYPE_ERROR, true, $logger)
|
|
1521
|
-
print_message("Cleaning up and exiting...", TYPE_WARNING, true, $logger)
|
|
1522
|
-
# Clean up KRB5CCNAME before exiting
|
|
1523
|
-
begin
|
|
1524
|
-
if defined?($original_krb5ccname) && !$original_krb5ccname.nil?
|
|
1525
|
-
ENV['KRB5CCNAME'] = $original_krb5ccname
|
|
1526
|
-
elsif defined?($original_krb5ccname) && $original_krb5ccname.nil?
|
|
1527
|
-
ENV.delete('KRB5CCNAME') if ENV.key?('KRB5CCNAME')
|
|
1528
|
-
end
|
|
1529
|
-
rescue => cleanup_error
|
|
1530
|
-
# Ignore cleanup errors
|
|
1531
|
-
end
|
|
1532
|
-
custom_exit(1, false)
|
|
1598
|
+
if connection_error?(e)
|
|
1599
|
+
handle_lost_connection(e)
|
|
1533
1600
|
else
|
|
1534
|
-
# Re-raise other errors
|
|
1535
1601
|
raise
|
|
1536
1602
|
end
|
|
1537
1603
|
end
|
|
@@ -1548,6 +1614,11 @@ class EvilWinRM
|
|
|
1548
1614
|
else
|
|
1549
1615
|
retry
|
|
1550
1616
|
end
|
|
1617
|
+
ensure
|
|
1618
|
+
if stop_keepalive
|
|
1619
|
+
stop_keepalive.call
|
|
1620
|
+
stop_keepalive = nil
|
|
1621
|
+
end
|
|
1551
1622
|
end
|
|
1552
1623
|
|
|
1553
1624
|
custom_exit(0)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: evil-winrm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '
|
|
4
|
+
version: '4.1'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- CyberVaca
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date:
|
|
14
|
+
date: 2026-09-03 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: benchmark
|
|
@@ -75,6 +75,34 @@ dependencies:
|
|
|
75
75
|
- - ">="
|
|
76
76
|
- !ruby/object:Gem::Version
|
|
77
77
|
version: 1.4.3
|
|
78
|
+
- !ruby/object:Gem::Dependency
|
|
79
|
+
name: readline
|
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - "~>"
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: 0.0.4
|
|
85
|
+
type: :runtime
|
|
86
|
+
prerelease: false
|
|
87
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
88
|
+
requirements:
|
|
89
|
+
- - "~>"
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: 0.0.4
|
|
92
|
+
- !ruby/object:Gem::Dependency
|
|
93
|
+
name: readline-ext
|
|
94
|
+
requirement: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - "~>"
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: 0.2.0
|
|
99
|
+
type: :runtime
|
|
100
|
+
prerelease: false
|
|
101
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
102
|
+
requirements:
|
|
103
|
+
- - "~>"
|
|
104
|
+
- !ruby/object:Gem::Version
|
|
105
|
+
version: 0.2.0
|
|
78
106
|
- !ruby/object:Gem::Dependency
|
|
79
107
|
name: stringio
|
|
80
108
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -147,16 +175,22 @@ dependencies:
|
|
|
147
175
|
name: bundler
|
|
148
176
|
requirement: !ruby/object:Gem::Requirement
|
|
149
177
|
requirements:
|
|
150
|
-
- - "
|
|
178
|
+
- - ">="
|
|
151
179
|
- !ruby/object:Gem::Version
|
|
152
180
|
version: '2.0'
|
|
181
|
+
- - "<"
|
|
182
|
+
- !ruby/object:Gem::Version
|
|
183
|
+
version: '5.0'
|
|
153
184
|
type: :development
|
|
154
185
|
prerelease: false
|
|
155
186
|
version_requirements: !ruby/object:Gem::Requirement
|
|
156
187
|
requirements:
|
|
157
|
-
- - "
|
|
188
|
+
- - ">="
|
|
158
189
|
- !ruby/object:Gem::Version
|
|
159
190
|
version: '2.0'
|
|
191
|
+
- - "<"
|
|
192
|
+
- !ruby/object:Gem::Version
|
|
193
|
+
version: '5.0'
|
|
160
194
|
description: The ultimate WinRM shell for hacking/pentesting
|
|
161
195
|
email:
|
|
162
196
|
- oscar.alfonso.diaz@gmail.com
|
|
@@ -193,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
193
227
|
- !ruby/object:Gem::Version
|
|
194
228
|
version: '0'
|
|
195
229
|
requirements: []
|
|
196
|
-
rubygems_version: 3.
|
|
230
|
+
rubygems_version: 3.5.22
|
|
197
231
|
signing_key:
|
|
198
232
|
specification_version: 4
|
|
199
233
|
summary: Evil-WinRM
|