Skip to content

Commit 975ddc9

Browse files
committed
Add some spec mockery
1 parent 81fa509 commit 975ddc9

File tree

2 files changed

+222
-5
lines changed

2 files changed

+222
-5
lines changed

lib/msf/core/post/windows/runas.rb

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def shell_exec(command, args)
4242
end
4343

4444
#
45-
# Create a STARTUP_INFO struct for use with CreateProcessa
45+
# Create a STARTUP_INFO struct for use with CreateProcessA
4646
#
4747
# This struct will cause the process to be hidden
4848
#
@@ -192,6 +192,9 @@ def create_process_as_user(domain, user, password, application_name, command_lin
192192
# @return [Hash] The values from the process_information struct
193193
#
194194
def parse_process_information(process_information)
195+
fail ArgumentError, 'process_information is nil' if process_information.nil?
196+
fail ArgumentError, 'process_information is empty string' if process_information.empty?
197+
195198
pi = process_information.unpack('LLLL')
196199
{ :process_handle => pi[0], :thread_handle => pi[1], :process_id => pi[2], :thread_id => pi[3] }
197200
end
@@ -208,6 +211,8 @@ def parse_process_information(process_information)
208211
# @return [True] True if username is in the correct format
209212
#
210213
def check_user_format(username, domain)
214+
fail ArgumentError, 'username is nil' if username.nil?
215+
211216
if domain && username.include?('@')
212217
raise ArgumentError, 'Username is in UPN format (user@domain) so the domain parameter must be nil'
213218
end
@@ -230,12 +235,17 @@ def check_user_format(username, domain)
230235
# @return [True] True if the command_line is within the correct bounds
231236
#
232237
def check_command_length(application_name, command_line, max_length)
238+
fail ArgumentError, 'max_length is nil' if max_length.nil?
239+
233240
if application_name.nil? && command_line.nil?
234241
raise ArgumentError, 'Both application_name and command_line are nil'
235-
elsif application_name.nil? && command_line && command_line.length > MAX_PATH
236-
raise ArgumentError, "When application_name is nil the command line must be less than MAX_PATH #{MAX_PATH} characters (Currently #{command_line.length})"
237-
elsif application_name && command_line && command_line.length > max_length
238-
raise ArgumentError, "When application_name is set, command line must be less than #{max_length} characters (Currently #{command_line.length})"
242+
elsif command_line && command_line.length > max_length
243+
raise ArgumentError, "Command line must be less than #{max_length} characters (Currently #{command_line.length})"
244+
elsif application_name.nil? && command_line
245+
cl = command_line.split(' ')
246+
if cl[0] && cl[0].length > MAX_PATH
247+
raise ArgumentError, "When application_name is nil the command line module must be less than MAX_PATH #{MAX_PATH} characters (Currently #{cl[0].length})"
248+
end
239249
end
240250

241251
true
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# -*- coding: binary -*-
2+
require 'spec_helper'
3+
4+
require 'msf/core/post/windows/runas'
5+
6+
describe Msf::Post::Windows::Runas do
7+
let(:process_info) do
8+
"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00"
9+
end
10+
11+
let(:phToken) do
12+
"testPhToken"
13+
end
14+
15+
let(:advapi32) do
16+
advapi32 = double('advapi32')
17+
advapi32.stub(:CreateProcessWithLogonW).and_return(
18+
'return' => true,
19+
'lpProcessInformation' => process_info
20+
)
21+
advapi32.stub(:CreateProcessAsUserA).and_return ({
22+
'return' => true,
23+
'lpProcessInformation' => process_info
24+
})
25+
advapi32.stub(:LogonUserA).and_return ({
26+
'return' => true,
27+
'phToken' => phToken
28+
})
29+
advapi32
30+
end
31+
32+
let(:kernel32) do
33+
double('kernel32', CloseHandle: nil)
34+
end
35+
36+
subject do
37+
mod = Module.new
38+
mod.extend described_class
39+
stubs = [ :vprint_status, :print_status, :vprint_good, :print_good, :print_error ]
40+
stubs.each { |meth| mod.stub(meth) }
41+
mod.stub_chain("session.railgun.kernel32").and_return(kernel32)
42+
mod.stub_chain("session.railgun.advapi32").and_return(advapi32)
43+
mod
44+
end
45+
46+
context "#create_process_with_logon" do
47+
it "should return a process_info hash" do
48+
expect(advapi32).to receive(:CreateProcessWithLogonW)
49+
expect(kernel32).not_to receive(:CloseHandle)
50+
pi = subject.create_process_with_logon(nil, 'bob', 'pass', nil, 'cmd.exe')
51+
pi.should be_kind_of(Hash)
52+
pi.should eq(process_handle: 1, thread_handle: 2, process_id: 3, thread_id: 4)
53+
end
54+
55+
it "should return a nil on failure" do
56+
expect(advapi32).to receive(:CreateProcessWithLogonW)
57+
expect(kernel32).not_to receive(:CloseHandle)
58+
advapi32.stub(:CreateProcessWithLogonW).and_return('return' => false, 'GetLastError' => 1783, 'ErrorMessage' => 'parp')
59+
subject.create_process_with_logon(nil, 'bob', 'pass', nil, 'cmd.exe').should be nil
60+
end
61+
end
62+
63+
context "#create_process_as_user" do
64+
it "should return a process_info hash" do
65+
expect(advapi32).to receive(:LogonUserA)
66+
expect(advapi32).to receive(:CreateProcessAsUserA)
67+
expect(kernel32).to receive(:CloseHandle).with(phToken)
68+
expect(kernel32).to receive(:CloseHandle).with(1)
69+
expect(kernel32).to receive(:CloseHandle).with(2)
70+
pi = subject.create_process_as_user(nil, 'bob', 'pass', nil, 'cmd.exe')
71+
pi.should be_kind_of(Hash)
72+
pi.should eq(process_handle: 1, thread_handle: 2, process_id: 3, thread_id: 4)
73+
end
74+
75+
it "should return a nil on failure of create process" do
76+
expect(advapi32).to receive(:LogonUserA)
77+
expect(advapi32).to receive(:CreateProcessAsUserA)
78+
expect(kernel32).to receive(:CloseHandle).with(phToken)
79+
expect(kernel32).not_to receive(:CloseHandle).with(1)
80+
expect(kernel32).not_to receive(:CloseHandle).with(2)
81+
advapi32.stub(:CreateProcessAsUserA).and_return('return' => false, 'GetLastError' => 1783, 'ErrorMessage' => 'parp')
82+
subject.create_process_as_user(nil, 'bob', 'pass', nil, 'cmd.exe').should be nil
83+
end
84+
85+
it "should return a nil on failure of logon user" do
86+
expect(advapi32).to receive(:LogonUserA)
87+
expect(advapi32).not_to receive(:CreateProcessAsUserA)
88+
expect(kernel32).not_to receive(:CloseHandle).with(phToken)
89+
expect(kernel32).not_to receive(:CloseHandle).with(1)
90+
expect(kernel32).not_to receive(:CloseHandle).with(2)
91+
advapi32.stub(:LogonUserA).and_return('return' => false, 'GetLastError' => 1783, 'ErrorMessage' => 'parp')
92+
subject.create_process_as_user(nil, 'bob', 'pass', nil, 'cmd.exe').should be nil
93+
end
94+
end
95+
96+
context "#startup_info" do
97+
it "should be 68 bytes" do
98+
subject.startup_info.size.should eq(68)
99+
end
100+
101+
it "should return SW_HIDE=0 and STARTF_USESHOWWINDOW=1" do
102+
si = subject.startup_info.unpack('VVVVVVVVVVVVvvVVVV')
103+
si[11].should eq(1)
104+
si[12].should eq(0)
105+
end
106+
end
107+
108+
context "#parse_process_information" do
109+
it "should return a hash when given valid data" do
110+
pi = subject.parse_process_information(process_info)
111+
pi.should be_kind_of(Hash)
112+
pi.should eq(process_handle: 1, thread_handle: 2, process_id: 3, thread_id: 4)
113+
end
114+
115+
it "should return an exception when given an empty string" do
116+
expect { subject.parse_process_information("") }.to raise_error
117+
end
118+
119+
it "should return an exception when given an nil value" do
120+
expect { subject.parse_process_information(nil) }.to raise_error
121+
end
122+
end
123+
124+
context "#check_user_format" do
125+
let(:upn_username) do
126+
127+
end
128+
let(:domain_username) do
129+
"flob\\bob"
130+
end
131+
let(:domain) do
132+
"flob"
133+
end
134+
135+
it "should return an exception when username is nil" do
136+
expect { subject.check_user_format(nil, domain) }.to raise_error
137+
end
138+
139+
it "should return an exception when UPN format and domain supplied" do
140+
expect { subject.check_user_format(upn_username, domain) }.to raise_error
141+
end
142+
143+
it "should return true when UPN format and domain is nil" do
144+
subject.check_user_format(upn_username, nil).should be true
145+
end
146+
147+
it "should return true when domain format and domain is nil" do
148+
subject.check_user_format(domain_username, nil).should be true
149+
end
150+
151+
it "should return true when domain format and domain supplied" do
152+
subject.check_user_format(domain_username, domain).should be true
153+
end
154+
end
155+
156+
context "#check_command_length" do
157+
let(:max_length) do
158+
1024
159+
end
160+
let(:max_path) do
161+
256
162+
end
163+
let(:large_command_module) do
164+
("A" * max_path + 1) + " arg1 arg2"
165+
end
166+
let(:normal_command_module) do
167+
("A" * max_path) + " arg1 arg2"
168+
end
169+
let(:large_command_line) do
170+
"A" * max_length + 1
171+
end
172+
let(:normal_command_line) do
173+
"A" * max_length
174+
end
175+
let(:application_name) do
176+
"c:\\windows\\system32\\calc.exe"
177+
end
178+
179+
it "should raise an exception when max_length is nil" do
180+
expect { subject.check_command_length(nil, nil, nil) }.to raise_error
181+
end
182+
183+
it "should raise an exception when application_name and command_line are nil" do
184+
expect { subject.check_command_length(nil, nil, max_length) }.to raise_error
185+
end
186+
187+
it "should return true when application_name is set and command_line is nil" do
188+
subject.check_command_length(application_name, nil, max_length).should be true
189+
end
190+
191+
it "should return true when application_name is set and command_line is max_length" do
192+
subject.check_command_length(application_name, normal_command_line, max_length).should be true
193+
end
194+
195+
it "should raise an exception when command_line is larger than max_length" do
196+
expect { subject.check_command_length(nil, large_command_line, max_length) }.to raise_error
197+
end
198+
199+
it "should raise an exception when application_name is nil command_line module is larger than MAX_PATH" do
200+
expect { subject.check_command_length(nil, large_command_module, max_length) }.to raise_error
201+
end
202+
203+
it "should return true when application_name is nil and command_module is less than MAX_PATH" do
204+
subject.check_command_length(nil, normal_command_module, max_length).should be true
205+
end
206+
end
207+
end

0 commit comments

Comments
 (0)