@@ -8,6 +8,12 @@ module Msf::Post::Windows::Runas
8
8
include Msf ::Post ::File
9
9
include Msf ::Exploit ::EXE
10
10
include Msf ::Exploit ::Powershell
11
+ include Msf ::Post ::Windows ::Error
12
+
13
+ ERROR = Msf ::Post ::Windows ::Error
14
+ MAX_PATH = 260
15
+ STARTF_USESHOWWINDOW = 0x00000001
16
+ SW_HIDE = 0
11
17
12
18
def shell_execute_exe ( filename = nil , path = nil )
13
19
exe_payload = generate_payload_exe
@@ -34,4 +40,217 @@ def shell_exec(command, args)
34
40
select ( nil , nil , nil , 1 ) until session_created?
35
41
end
36
42
end
43
+
44
+ #
45
+ # Create a STARTUP_INFO struct for use with CreateProcessA
46
+ #
47
+ # This struct will cause the process to be hidden
48
+ #
49
+ # @return [String] STARTUP_INFO struct
50
+ #
51
+ def startup_info
52
+ [ 0 , # cb
53
+ 0 , # lpReserved
54
+ 0 , # lpDesktop
55
+ 0 , # lpTitle
56
+ 0 , # dwX
57
+ 0 , # dwY
58
+ 0 , # dwXSize
59
+ 0 , # dwYSize
60
+ 0 , # dwXCountChars
61
+ 0 , # dwYCountChars
62
+ 0 , # dwFillAttribute
63
+ STARTF_USESHOWWINDOW , # dwFlags
64
+ SW_HIDE , # wShowWindow
65
+ 0 , # cbReserved2
66
+ 0 , # lpReserved2
67
+ 0 , # hStdInput
68
+ 0 , # hStdOutput
69
+ 0 # hStdError
70
+ ] . pack ( 'VVVVVVVVVVVVvvVVVV' )
71
+ end
72
+
73
+ #
74
+ # Call CreateProcessWithLogonW to start a process with the supplier
75
+ # user credentials
76
+ #
77
+ # @note The caller should clear up the handles returned in
78
+ # the PROCESS_INFORMATION @return hash.
79
+ #
80
+ # @param domain [String] The target user domain
81
+ # @param user [String] The target user
82
+ # @param password [String] The target user password
83
+ # @param application_name [String] The executable to be run, can be
84
+ # nil
85
+ # @param command_line [String] The command line or process arguments
86
+ #
87
+ # @return [Hash, nil] The values from the process_information struct
88
+ #
89
+ def create_process_with_logon ( domain , user , password , application_name , command_line )
90
+ return unless check_user_format ( user , domain )
91
+ return unless check_command_length ( application_name , command_line , 1024 )
92
+
93
+ vprint_status ( "Executing CreateProcessWithLogonW: #{ application_name } #{ command_line } ..." )
94
+ create_process = session . railgun . advapi32 . CreateProcessWithLogonW ( user ,
95
+ domain ,
96
+ password ,
97
+ 'LOGON_WITH_PROFILE' ,
98
+ application_name ,
99
+ command_line ,
100
+ 'CREATE_UNICODE_ENVIRONMENT' ,
101
+ nil ,
102
+ nil ,
103
+ startup_info ,
104
+ 16 )
105
+ if create_process [ 'return' ]
106
+ pi = parse_process_information ( create_process [ 'lpProcessInformation' ] )
107
+ print_good ( "Process started successfully, PID: #{ pi [ :process_id ] } " )
108
+ else
109
+ print_error ( "Unable to create process, Error Code: #{ create_process [ 'GetLastError' ] } - #{ create_process [ 'ErrorMessage' ] } " )
110
+ print_error ( "Try setting the DOMAIN or USER in the format: user@domain" ) if create_process [ 'GetLastError' ] == 1783 && domain . nil?
111
+ end
112
+
113
+ pi
114
+ end
115
+
116
+ #
117
+ # Call CreateProcessAsUser to start a process with the supplier
118
+ # user credentials
119
+ #
120
+ # Can be used by SYSTEM processes with the SE_INCREASE_QUOTA_NAME and
121
+ # SE_ASSIGNPRIMARYTOKEN_NAME privileges.
122
+ #
123
+ # This will normally error with 0xc000142 on later OS's (Vista+?) for
124
+ # gui apps but is ok for firing off cmd.exe...
125
+ #
126
+ # @param domain [String] The target user domain
127
+ # @param user [String] The target user
128
+ # @param password [String] The target user password
129
+ # @param application_name [String] Thn executableived :CloseHandle
130
+ # with unexpected arguments
131
+ # expected: ("testPhToken")
132
+ # got: (n be run, can be
133
+ # nil
134
+ # @param command_line [String] The command line or process arguments
135
+ #
136
+ # @return [Hash, nil] The values from the process_information struct
137
+ #
138
+ def create_process_as_user ( domain , user , password , application_name , command_line )
139
+ return unless check_user_format ( user , domain )
140
+ return unless check_command_length ( application_name , command_line , 32000 )
141
+
142
+ vprint_status ( "Executing LogonUserA..." )
143
+ logon_user = session . railgun . advapi32 . LogonUserA ( user ,
144
+ domain ,
145
+ password ,
146
+ 'LOGON32_LOGON_INTERACTIVE' ,
147
+ 'LOGON32_PROVIDER_DEFAULT' ,
148
+ 4 )
149
+
150
+ if logon_user [ 'return' ]
151
+ begin
152
+ ph_token = logon_user [ 'phToken' ]
153
+ vprint_status ( "Executing CreateProcessAsUserA..." )
154
+ create_process = session . railgun . advapi32 . CreateProcessAsUserA ( ph_token ,
155
+ application_name ,
156
+ command_line ,
157
+ nil ,
158
+ nil ,
159
+ false ,
160
+ 'CREATE_NEW_CONSOLE' ,
161
+ nil ,
162
+ nil ,
163
+ startup_info ,
164
+ 16 )
165
+
166
+ if create_process [ 'return' ]
167
+ begin
168
+ pi = parse_process_information ( create_process [ 'lpProcessInformation' ] )
169
+ ensure
170
+ session . railgun . kernel32 . CloseHandle ( pi [ :process_handle ] )
171
+ session . railgun . kernel32 . CloseHandle ( pi [ :thread_handle ] )
172
+ end
173
+ print_good ( "Process started successfully, PID: #{ pi [ :process_id ] } " )
174
+ else
175
+ print_error ( "Unable to create process, Error Code: #{ create_process [ 'GetLastError' ] } - #{ create_process [ 'ErrorMessage' ] } " )
176
+ end
177
+
178
+ return pi
179
+ ensure
180
+ session . railgun . kernel32 . CloseHandle ( ph_token )
181
+ end
182
+ else
183
+ print_error ( "Unable to login the user, Error Code: #{ logon_user [ 'GetLastError' ] } - #{ logon_user [ 'ErrorMessage' ] } " )
184
+ end
185
+
186
+ nil
187
+ end
188
+
189
+ #
190
+ # Parse the PROCESS_INFORMATION struct
191
+ #
192
+ # @param process_information [String] The PROCESS_INFORMATION value
193
+ # from the CreateProcess call
194
+ #
195
+ # @return [Hash] The values from the process_information struct
196
+ #
197
+ def parse_process_information ( process_information )
198
+ fail ArgumentError , 'process_information is nil' if process_information . nil?
199
+ fail ArgumentError , 'process_information is empty string' if process_information . empty?
200
+
201
+ pi = process_information . unpack ( 'VVVV' )
202
+ { :process_handle => pi [ 0 ] , :thread_handle => pi [ 1 ] , :process_id => pi [ 2 ] , :thread_id => pi [ 3 ] }
203
+ end
204
+
205
+ #
206
+ # Checks the username and domain is in the correct format
207
+ # for the CreateProcess_x WinAPI calls.
208
+ #
209
+ # @param username [String] The target user
210
+ # @param domain [String] The target user domain
211
+ #
212
+ # @raise [ArgumentError] If the username format is incorrect
213
+ #
214
+ # @return [True] True if username is in the correct format
215
+ #
216
+ def check_user_format ( username , domain )
217
+ fail ArgumentError , 'username is nil' if username . nil?
218
+
219
+ if domain && username . include? ( '@' )
220
+ raise ArgumentError , 'Username is in UPN format (user@domain) so the domain parameter must be nil'
221
+ end
222
+
223
+ true
224
+ end
225
+
226
+ #
227
+ # Checks the command_length parameter is the correct length
228
+ # for the CreateProcess_x WinAPI calls depending on the presence
229
+ # of application_name
230
+ #
231
+ # @param application_name [String] lpApplicationName
232
+ # @param command_line [String] lpCommandLine
233
+ # @param max_length [Integer] The max command length of the respective
234
+ # CreateProcess function
235
+ #
236
+ # @raise [ArgumentError] If the command_line is too large
237
+ #
238
+ # @return [True] True if the command_line is within the correct bounds
239
+ #
240
+ def check_command_length ( application_name , command_line , max_length )
241
+ fail ArgumentError , 'max_length is nil' if max_length . nil?
242
+
243
+ if application_name . nil? && command_line . nil?
244
+ raise ArgumentError , 'Both application_name and command_line are nil'
245
+ elsif command_line && command_line . length > max_length
246
+ raise ArgumentError , "Command line must be less than #{ max_length } characters (Currently #{ command_line . length } )"
247
+ elsif application_name . nil? && command_line
248
+ cl = command_line . split ( ' ' )
249
+ if cl [ 0 ] && cl [ 0 ] . length > MAX_PATH
250
+ raise ArgumentError , "When application_name is nil the command line module must be less than MAX_PATH #{ MAX_PATH } characters (Currently #{ cl [ 0 ] . length } )"
251
+ end
252
+ end
253
+
254
+ true
255
+ end
37
256
end
0 commit comments