CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/base/sessions/powershell.rb
Views: 11784
1
# -*- coding: binary -*-
2
3
class Msf::Sessions::PowerShell < Msf::Sessions::CommandShell
4
module Mixin
5
#
6
# Takes over the shell_command of the parent
7
#
8
def shell_command(cmd, timeout = 1800)
9
# insert random marker
10
strm = Rex::Text.rand_text_alpha(15)
11
endm = Rex::Text.rand_text_alpha(15)
12
13
# Send the shell channel's stdin.
14
shell_write(";'#{strm}'\n" + cmd + "\n'#{endm}';\n")
15
16
etime = ::Time.now.to_f + timeout
17
18
buff = ''
19
# Keep reading data until the marker has been received or the 30 minute timeout has occurred
20
while (::Time.now.to_f < etime)
21
res = shell_read(-1, timeout)
22
break unless res
23
24
timeout = etime - ::Time.now.to_f
25
26
buff << res
27
next unless buff.include?(endm)
28
29
# if you see the end marker, read the buffer from the start marker to the end and then display back to screen
30
buff = buff.split(/#{strm}\r\n/)[-1]
31
buff = buff.split(endm)[0]
32
buff.gsub!(/(?<=\r\n)PS [^>]*>/, '')
33
return buff
34
end
35
buff
36
end
37
end
38
39
include Mixin
40
41
# Convert the executable and argument array to a command that can be run in this command shell
42
# @param cmd_and_args [Array<String>] The process path and the arguments to the process
43
def to_cmd(cmd_and_args)
44
self.class.to_cmd(cmd_and_args)
45
end
46
47
# Convert the executable and argument array to a command that can be run in this command shell
48
# @param cmd_and_args [Array<String>] The process path and the arguments to the process
49
def self.to_cmd(cmd_and_args)
50
# The principle here is that we want to launch a process such that it receives *exactly* what is in `args`.
51
# This means we need to:
52
# - Escape all special characters
53
# - Not escape environment variables
54
# - Side-step any PowerShell magic
55
# If someone specifically wants to use the PowerShell magic, they can use other APIs
56
57
needs_wrapping_chars = ['$', '`', '(', ')', '@', '>', '<', '{','}', '&', ',', ' ', ';']
58
59
result = ""
60
cmd_and_args.each_with_index do |arg, index|
61
needs_single_quoting = false
62
if arg.include?("'")
63
arg = arg.gsub("'", "''")
64
needs_single_quoting = true
65
end
66
67
if arg.include?('"')
68
# PowerShell acts weird around quotes and backslashes
69
# First we need to escape backslashes immediately prior to a double-quote, because
70
# they're treated differently than backslashes anywhere else
71
arg = arg.gsub(/(\\+)"/, '\\1\\1"')
72
73
# Then we can safely prepend a backslash to escape our double-quote
74
arg = arg.gsub('"', '\\"')
75
needs_single_quoting = true
76
end
77
78
needs_wrapping_chars.each do |char|
79
if arg.include?(char)
80
needs_single_quoting = true
81
end
82
end
83
84
# PowerShell magic - https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_special_characters?view=powershell-7.4#stop-parsing-token---
85
if arg == '--%'
86
needs_single_quoting = true
87
end
88
89
will_be_double_quoted_by_powershell = [' ', '\t', '\v'].any? do |bad_char|
90
arg.include?(bad_char)
91
end
92
93
if will_be_double_quoted_by_powershell
94
# This is horrible, and I'm so so sorry.
95
# If an argument ends with a series of backslashes, and it will be quoted by PowerShell when *it* launches the process (e.g. because the arg contains a space),
96
# PowerShell will not correctly handle backslashes immediately preceeding the quote that it *itself* adds. So we need to be responsible for this.
97
arg = arg.gsub(/(\\*)$/, '\\1\\1')
98
end
99
100
if needs_single_quoting
101
arg = "'#{arg}'"
102
end
103
104
if arg == ''
105
# Pass in empty strings
106
arg = '\'""\''
107
end
108
109
if index == 0
110
if needs_single_quoting
111
# If the executable name (i.e. index 0) has beeen wrapped, then we'll have converted it to a string.
112
# We then need to use the call operator ('&') to call it.
113
# https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators?view=powershell-7.3#call-operator-
114
result = "& #{arg}"
115
else
116
result = arg
117
end
118
else
119
result = "#{result} #{arg}"
120
end
121
end
122
123
result
124
end
125
126
#
127
# Execute any specified auto-run scripts for this session
128
#
129
def process_autoruns(datastore)
130
# Read the username and hostname from the initial banner
131
initial_output = shell_read(-1, 2)
132
if initial_output =~ /running as user ([^\s]+) on ([^\s]+)/
133
username = Regexp.last_match(1)
134
hostname = Regexp.last_match(2)
135
self.info = "#{username} @ #{hostname}"
136
elsif initial_output
137
self.info = initial_output.gsub(/[\r\n]/, ' ')
138
end
139
140
# Call our parent class's autoruns processing method
141
super
142
end
143
144
#
145
# Returns the type of session.
146
#
147
def self.type
148
'powershell'
149
end
150
151
def self.can_cleanup_files
152
true
153
end
154
155
#
156
# Returns the session platform.
157
#
158
def platform
159
'windows'
160
end
161
162
#
163
# Returns the session description.
164
#
165
def desc
166
'Powershell session'
167
end
168
169
end
170
171