|
| 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