diff --git a/.gitignore b/.gitignore
index 79668441e..8f2732f8c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,4 @@ lib/resources/images/scenario
rgloader
.ruby-version
vendor/**
+plans/
diff --git a/modules/generators/content/maltrail_data/maltrail_data.pp b/modules/generators/content/maltrail_data/maltrail_data.pp
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/generators/content/maltrail_data/manifests/.no_puppet b/modules/generators/content/maltrail_data/manifests/.no_puppet
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/generators/content/maltrail_data/secgen_local/local.rb b/modules/generators/content/maltrail_data/secgen_local/local.rb
new file mode 100644
index 000000000..5fe7037fa
--- /dev/null
+++ b/modules/generators/content/maltrail_data/secgen_local/local.rb
@@ -0,0 +1,282 @@
+#!/usr/bin/ruby
+require_relative '../../../../../lib/objects/local_string_generator.rb'
+require 'json'
+require 'securerandom'
+
+class MalTrailDataGenerator < StringGenerator
+ attr_accessor :data_volume
+ attr_accessor :embedded_flag
+
+ # TLDs for malicious-looking domains
+ TLDS = ['.com', '.net', '.xyz', '.ru', '.cn', '.info', '.pw', '.top', '.ml', '.tk']
+
+ # Prefixes that look suspicious
+ PREFIXES = ['update', 'cdn', 'api', 'mail', 'secure', 'auth', 'login', 'download',
+ 'verify', 'account', 'admin', 'panel', 'config', 'backup', 'db']
+
+ # Suffixes that suggest malicious intent
+ SUFFIXES = ['malware', 'bot', 'trojan', 'crypto', 'apt', 'c2', 'shell', 'payload',
+ ' exploit', 'hack', 'ransom', 'miner', 'phish', 'spam', 'ddos']
+
+ # Threat types with references
+ THREAT_TYPES = [
+ { info: 'known_attacker', ref: 'abuseipdb' },
+ { info: 'malware_c2', ref: '(static)' },
+ { info: 'trojan', ref: 'malc0de' },
+ { info: 'cryptominer', ref: 'minerchk' },
+ { info: 'phishing', ref: 'openphish' },
+ { info: 'apt', ref: '(static)' },
+ { info: 'spam', ref: 'spamhaus' },
+ { info: 'botnet', ref: 'zeustracker' },
+ { info: 'ransomware', ref: '(static)' },
+ { info: 'c2', ref: 'emergingthreats' }
+ ]
+
+ # Common ports for different traffic types
+ PORTS = {
+ http: [80, 8080, 8000, 8888],
+ https: [443, 8443],
+ dns: [53],
+ ssh: [22],
+ ftp: [21],
+ smtp: [25, 587],
+ custom: [4444, 5555, 6666, 7777, 9999]
+ }
+
+ def initialize
+ super
+ self.module_name = 'MalTrail Randomized Data Generator'
+ self.data_volume = 'medium'
+ self.embedded_flag = ''
+ end
+
+ def get_options_array
+ super + [
+ ['--data_volume', GetoptLong::REQUIRED_ARGUMENT],
+ ['--embedded_flag', GetoptLong::REQUIRED_ARGUMENT]
+ ]
+ end
+
+ def process_options(opt, arg)
+ super
+ case opt
+ when '--data_volume'
+ self.data_volume = arg
+ when '--embedded_flag'
+ self.embedded_flag = arg
+ end
+ end
+
+ def generate
+ # Validate and normalize data_volume
+ volume = normalize_volume(self.data_volume)
+
+ # Determine event count based on volume
+ event_count = get_event_count(volume)
+
+ # Generate events
+ events = []
+ (1..event_count).each do
+ events << generate_random_event
+ end
+
+ # Generate the flag event
+ flag_event = generate_flag_event if self.embedded_flag && !self.embedded_flag.empty?
+
+ # Add flag event to events (inserted at a random position)
+ if flag_event
+ insert_position = rand(events.length)
+ events.insert(insert_position, flag_event)
+ end
+
+ # Generate custom trails
+ custom_trails = generate_custom_trails(events, flag_event)
+
+ # Build output structure
+ output = {
+ events: events,
+ custom_trails: custom_trails
+ }
+
+ output[:flag_event] = flag_event if flag_event
+
+ # Output as JSON
+ self.outputs << JSON.generate(output)
+ end
+
+ private
+
+ def normalize_volume(volume)
+ case volume.to_s.downcase
+ when 'low'
+ 'low'
+ when 'high'
+ 'high'
+ else
+ 'medium'
+ end
+ end
+
+ def get_event_count(volume)
+ case volume
+ when 'low'
+ rand(10..20)
+ when 'high'
+ rand(500..1000)
+ else # medium
+ rand(50..100)
+ end
+ end
+
+ def generate_random_event
+ threat = THREAT_TYPES.sample
+ src_ip = random_private_ip
+ dst_ip = random_public_ip
+ proto = ['TCP', 'UDP'].sample
+ port_type = proto == 'TCP' ? [:http, :https, :ssh, :custom].sample : [:dns, :custom].sample
+ dst_port = PORTS[port_type].sample
+ src_port = rand(1024..65535)
+
+ # Determine trail type and trail value
+ trail_type = rand(0..2) == 0 ? 'URL' : (rand(0..1) == 0 ? 'IP' : 'DNS')
+ trail = case trail_type
+ when 'IP'
+ dst_ip
+ when 'DNS'
+ random_malicious_domain
+ when 'URL'
+ random_malicious_url(dst_ip)
+ end
+
+ {
+ timestamp: generate_timestamp,
+ sensor: 'maltrail',
+ src_ip: src_ip,
+ src_port: src_port,
+ dst_ip: dst_ip,
+ dst_port: dst_port,
+ proto: proto,
+ trail_type: trail_type,
+ trail: trail,
+ trail_info: threat[:info],
+ reference: threat[:ref]
+ }
+ end
+
+ def generate_flag_event
+ return nil if self.embedded_flag.empty?
+
+ # Create a suspicious-looking domain that contains the flag
+ flag_domain = "update.flag.local"
+
+ {
+ timestamp: generate_timestamp,
+ sensor: 'maltrail',
+ src_ip: random_private_ip,
+ src_port: rand(1024..65535),
+ dst_ip: '8.8.8.8', # Google DNS - makes it look like a DNS query
+ dst_port: 53,
+ proto: 'UDP',
+ trail_type: 'DNS',
+ trail: flag_domain,
+ trail_info: self.embedded_flag,
+ reference: 'flag'
+ }
+ end
+
+ def generate_custom_trails(events, flag_event)
+ trails = []
+
+ # Add header comment
+ trails << '# Custom trails for training scenario'
+ trails << '# Malicious IPs'
+
+ # Extract unique IPs from events
+ ip_trails = events.select { |e| e[:trail_type] == 'IP' }
+ .map { |e| "#{e[:trail]} #{e[:trail_info]} #{e[:reference]}" }
+ .uniq
+ .take(5) # Limit to 5 IP entries
+ trails.concat(ip_trails)
+
+ trails << '# Malicious domains'
+
+ # Extract unique domains from events
+ dns_trails = events.select { |e| e[:trail_type] == 'DNS' }
+ .map { |e| "#{e[:trail]} #{e[:trail_info]} #{e[:reference]}" }
+ .uniq
+ .take(5) # Limit to 5 domain entries
+ trails.concat(dns_trails)
+
+ # Add flag trail if present
+ if flag_event
+ trails << '# CTF flag trail'
+ trails << "#{flag_event[:trail]} #{flag_event[:trail_info]} #{flag_event[:reference]}"
+ end
+
+ trails << '# Malicious URLs'
+
+ # Extract unique URLs from events
+ url_trails = events.select { |e| e[:trail_type] == 'URL' }
+ .map { |e| "#{e[:trail]} #{e[:trail_info]} #{e[:reference]}" }
+ .uniq
+ .take(3) # Limit to 3 URL entries
+ trails.concat(url_trails)
+
+ trails
+ end
+
+ def random_private_ip
+ case rand(3)
+ when 0
+ "192.168.#{rand(1..254)}.#{rand(1..254)}"
+ when 1
+ "10.#{rand(0..255)}.#{rand(0..255)}.#{rand(1..254)}"
+ else
+ "172.#{rand(16..31)}.#{rand(0..255)}.#{rand(1..254)}"
+ end
+ end
+
+ def random_public_ip
+ loop do
+ first = rand(1..223)
+ # Skip private ranges
+ next if first == 10
+ next if first == 127 # Loopback
+ next if first == 172 && rand(0..255) >= 16 && rand(0..255) <= 31
+ next if first == 192 && rand(0..255) == 168
+
+ return "#{first}.#{rand(0..255)}.#{rand(0..255)}.#{rand(1..254)}"
+ end
+ end
+
+ def random_malicious_domain
+ prefix = PREFIXES.sample
+ suffix = SUFFIXES.sample
+ tld = TLDS.sample
+ number = rand(100..999)
+
+ "#{prefix}-#{suffix}#{number}#{tld}"
+ end
+
+ def random_malicious_url(ip = nil)
+ domain = random_malicious_domain
+ paths = ['update.exe', 'download/payload.zip', 'install.msi', 'setup.bin',
+ 'config.jar', 'update.sh', 'backup.tar.gz']
+
+ if rand(2) == 0 && ip
+ "http://#{ip}/#{paths.sample}"
+ else
+ "http://#{domain}/#{paths.sample}"
+ end
+ end
+
+ def generate_timestamp
+ # Generate a timestamp within the last 24 hours
+ now = Time.now
+ random_seconds = rand(0..86400) # Within last 24 hours
+ time = now - random_seconds
+ time.strftime('%Y-%m-%d %H:%M:%S.%6N')
+ end
+end
+
+MalTrailDataGenerator.new.run
\ No newline at end of file
diff --git a/modules/generators/content/maltrail_data/secgen_metadata.xml b/modules/generators/content/maltrail_data/secgen_metadata.xml
new file mode 100644
index 000000000..96496041e
--- /dev/null
+++ b/modules/generators/content/maltrail_data/secgen_metadata.xml
@@ -0,0 +1,27 @@
+
+
+ MalTrail Randomized Data Generator
+ Rosie Fletcher
+ MIT
+ Generates randomized MalTrail threat events and custom trails for security training scenarios. Creates synthetic IPs, domains, URLs, and malware indicators. Output includes a flag event that embeds a CTF flag within the threat data.
+
+ maltrail_data
+ local_calculation
+ linux
+
+ data_volume
+ embedded_flag
+
+
+ medium
+
+
+
+
+
+
+ generated_strings
+
+
\ No newline at end of file
diff --git a/modules/vulnerabilities/unix/http/maltrail_rce/files/generate_custom_trails.rb b/modules/vulnerabilities/unix/http/maltrail_rce/files/generate_custom_trails.rb
new file mode 100644
index 000000000..d38cafe7f
--- /dev/null
+++ b/modules/vulnerabilities/unix/http/maltrail_rce/files/generate_custom_trails.rb
@@ -0,0 +1,64 @@
+#!/usr/bin/env ruby
+# Script to generate MalTrail custom trails file from JSON data
+# Called by Puppet during provisioning
+require 'json'
+
+# Read JSON input from command line argument
+if ARGV.empty?
+ puts "Usage: #{$0} ''"
+ exit 1
+end
+
+begin
+ # Parse the JSON data
+ data = JSON.parse(ARGV[0])
+
+ # Generate custom trails content
+ trails = []
+
+ # Add header comment
+ trails << "# Custom trails for SecGen training scenario"
+ trails << "# Generated automatically by Puppet provisioning"
+ trails << "#"
+ trails << "# Malicious IPs"
+
+ # Check for flag trail first
+ flag_trail = data['custom_trails'].find { |t| t.include?('update.flag.local') }
+
+ # Add IP-based trails
+ data['custom_trails'].each do |trail_entry|
+ # Check if it's an IP (starts with a number)
+ if trail_entry =~ /^\d/
+ trails << trail_entry
+ end
+ end
+
+ trails << "#"
+ trails << "# Malicious domains"
+
+ # Add domain-based trails
+ data['custom_trails'].each do |trail_entry|
+ # Check if it's a domain (starts with a letter)
+ if trail_entry =~ /^[a-z]/i && !trail_entry.include?('update.flag.local')
+ trails << trail_entry
+ end
+ end
+
+ # Insert flag trail in the middle of the file if found
+ if flag_trail
+ insert_position = (trails.length / 2).to_i
+ trails.insert(insert_position, "#")
+ trails.insert(insert_position + 1, "# SUSPICIOUS DOMAIN")
+ trails.insert(insert_position + 2, flag_trail)
+ end
+
+ # Output the trails content
+ puts trails.join("\n")
+
+rescue JSON::ParserError => e
+ STDERR.puts "Error parsing JSON: #{e.message}"
+ exit 1
+rescue => e
+ STDERR.puts "Error: #{e.message}"
+ exit 1
+end
\ No newline at end of file
diff --git a/modules/vulnerabilities/unix/http/maltrail_rce/files/generate_maltrail_log.rb b/modules/vulnerabilities/unix/http/maltrail_rce/files/generate_maltrail_log.rb
new file mode 100644
index 000000000..0d7ddb874
--- /dev/null
+++ b/modules/vulnerabilities/unix/http/maltrail_rce/files/generate_maltrail_log.rb
@@ -0,0 +1,34 @@
+#!/usr/bin/env ruby
+# Script to generate MalTrail log entries from JSON data
+# Called by Puppet during provisioning
+require 'json'
+
+# Read JSON input from command line argument
+if ARGV.empty?
+ puts "Usage: #{$0} ''"
+ exit 1
+end
+
+begin
+ # Parse the JSON data
+ data = JSON.parse(ARGV[0])
+
+ # Generate log entries
+ log_lines = []
+
+ data['events'].each do |event|
+ # Format: "timestamp" sensor src_ip src_port dst_ip dst_port proto trail_type trail trail_info reference
+ log_line = "\"#{event['timestamp']}\" #{event['sensor']} #{event['src_ip']} #{event['src_port']} #{event['dst_ip']} #{event['dst_port']} #{event['proto']} #{event['trail_type']} #{event['trail']} #{event['trail_info']} #{event['reference']}"
+ log_lines << log_line
+ end
+
+ # Output the log content
+ puts log_lines.join("\n")
+
+rescue JSON::ParserError => e
+ STDERR.puts "Error parsing JSON: #{e.message}"
+ exit 1
+rescue => e
+ STDERR.puts "Error: #{e.message}"
+ exit 1
+end
\ No newline at end of file
diff --git a/modules/vulnerabilities/unix/http/maltrail_rce/files/maltrail-0.52.tar.gz b/modules/vulnerabilities/unix/http/maltrail_rce/files/maltrail-0.52.tar.gz
new file mode 100644
index 000000000..65a6f577b
Binary files /dev/null and b/modules/vulnerabilities/unix/http/maltrail_rce/files/maltrail-0.52.tar.gz differ
diff --git a/modules/vulnerabilities/unix/http/maltrail_rce/maltrail_rce.pp b/modules/vulnerabilities/unix/http/maltrail_rce/maltrail_rce.pp
new file mode 100644
index 000000000..c6021731b
--- /dev/null
+++ b/modules/vulnerabilities/unix/http/maltrail_rce/maltrail_rce.pp
@@ -0,0 +1,5 @@
+# MalTrail <= 0.54 - Unauthenticated RCE (CVE-2025-34073)
+# https://github.com/stamparm/maltrail/issues/19146
+include maltrail_rce::install
+include maltrail_rce::config
+include maltrail_rce::service
diff --git a/modules/vulnerabilities/unix/http/maltrail_rce/manifests/config.pp b/modules/vulnerabilities/unix/http/maltrail_rce/manifests/config.pp
new file mode 100644
index 000000000..5307598cb
--- /dev/null
+++ b/modules/vulnerabilities/unix/http/maltrail_rce/manifests/config.pp
@@ -0,0 +1,87 @@
+class maltrail_rce::config {
+ $secgen_parameters = secgen_functions::get_parameters($::base64_inputs_file)
+ $modulename = 'maltrail_rce'
+ $user = $secgen_parameters['unix_username'][0]
+ $leaked_filenames = $secgen_parameters['leaked_filenames']
+ $strings_to_leak = $secgen_parameters['strings_to_leak']
+
+ # MalTrail data from generator (JSON format, base64 encoded)
+ $maltrail_data_json_array = $secgen_parameters['maltrail_data']
+ $maltrail_data_json = $maltrail_data_json_array[0]
+
+ Exec { path => [ '/bin/', '/sbin/' , '/usr/bin/', '/usr/sbin/' ] }
+
+ # Leak flag files to user's home directory for exploitation verification (Flag 1)
+ ::secgen_functions::leak_files { 'maltrail-flag-leak':
+ storage_directory => "/home/${user}",
+ leaked_filenames => $leaked_filenames,
+ strings_to_leak => $strings_to_leak,
+ owner => $user,
+ mode => '0600',
+ leaked_from => 'maltrail_rce',
+ }
+
+ # Deploy Ruby helper scripts for generating MalTrail data
+ file { '/tmp/generate_maltrail_log.rb':
+ ensure => file,
+ source => "puppet:///modules/${modulename}/generate_maltrail_log.rb",
+ owner => 'root',
+ group => 'root',
+ mode => '0755',
+ }
+
+ file { '/tmp/generate_custom_trails.rb':
+ ensure => file,
+ source => "puppet:///modules/${modulename}/generate_custom_trails.rb",
+ owner => 'root',
+ group => 'root',
+ mode => '0755',
+ }
+
+ # Create MalTrail log directory
+ file { '/var/log/maltrail':
+ ensure => directory,
+ owner => $user,
+ group => $user,
+ mode => '0755',
+ require => User[$user],
+ }
+
+ # Create custom trails directory
+ file { '/opt/maltrail/trails':
+ ensure => directory,
+ owner => $user,
+ group => $user,
+ mode => '0755',
+ }
+
+ # Generate today's log file with synthetic events using Ruby script
+ # The script reads from maltrail_data_json and generates properly formatted log entries
+ exec { 'generate-maltrail-log':
+ command => "ruby /tmp/generate_maltrail_log.rb '${maltrail_data_json}' > /var/log/maltrail/$(date +%Y-%m-%d).log",
+ unless => "test -f /var/log/maltrail/$(date +%Y-%m-%d).log",
+ require => [File['/var/log/maltrail'], File['/tmp/generate_maltrail_log.rb']],
+ }
+
+ # Set permissions on log file
+ exec { 'set-maltrail-log-permissions':
+ command => "chown ${user}:${user} /var/log/maltrail/*.log && chmod 644 /var/log/maltrail/*.log",
+ require => Exec['generate-maltrail-log'],
+ }
+
+ # Generate custom trails file using Ruby script
+ exec { 'generate-custom-trails':
+ command => "ruby /tmp/generate_custom_trails.rb '${maltrail_data_json}' > /opt/maltrail/trails/custom.txt",
+ unless => 'test -f /opt/maltrail/trails/custom.txt',
+ require => [File['/opt/maltrail/trails'], File['/tmp/generate_custom_trails.rb']],
+ }
+
+ # Set permissions on custom trails file
+ file { '/opt/maltrail/trails/custom.txt':
+ ensure => file,
+ owner => $user,
+ group => $user,
+ mode => '0644',
+ require => Exec['generate-custom-trails'],
+ }
+}
diff --git a/modules/vulnerabilities/unix/http/maltrail_rce/manifests/install.pp b/modules/vulnerabilities/unix/http/maltrail_rce/manifests/install.pp
new file mode 100644
index 000000000..5f49406b0
--- /dev/null
+++ b/modules/vulnerabilities/unix/http/maltrail_rce/manifests/install.pp
@@ -0,0 +1,73 @@
+class maltrail_rce::install {
+ Exec { path => [ '/bin/', '/sbin/' , '/usr/bin/', '/usr/sbin/' ] }
+ $modulename = 'maltrail_rce'
+
+ $secgen_parameters = secgen_functions::get_parameters($::base64_inputs_file)
+ $port = $secgen_parameters['port'][0]
+ $user = $secgen_parameters['unix_username'][0]
+ $user_home = "/home/${user}"
+
+ # MalTrail tarball name (single file, no splitting needed)
+ $tarball = 'maltrail-0.52.tar.gz'
+
+ # Create dedicated user for MalTrail service
+ user { $user:
+ ensure => present,
+ home => $user_home,
+ managehome => true,
+ shell => '/bin/bash',
+ }
+
+ group { $user:
+ ensure => present,
+ }
+
+ ensure_packages(['python3', 'python3-pip', 'python3-dev', 'libpcap-dev', 'build-essential', 'python3-pcapy'])
+
+ # Create user home directory with proper permissions
+ file { $user_home:
+ ensure => directory,
+ owner => $user,
+ group => $user,
+ mode => '0750',
+ require => User[$user],
+ }
+
+ # OFFLINE: Copy MalTrail tarball
+ file { "/tmp/${tarball}":
+ ensure => file,
+ source => "puppet:///modules/${modulename}/${tarball}",
+ }
+
+ # Extract MalTrail tarball (maltrail-0.52 directory format)
+ exec { 'extract-maltrail':
+ cwd => '/tmp',
+ command => "tar -xzf ${tarball}",
+ creates => '/opt/maltrail',
+ require => File["/tmp/${tarball}"],
+ }
+
+ # Move MalTrail to installation directory
+ exec { 'install-maltrail':
+ cwd => '/tmp',
+ command => 'mv maltrail-0.53 /opt/maltrail',
+ creates => '/opt/maltrail',
+ require => Exec['extract-maltrail'],
+ }
+
+ # Set ownership
+ exec { 'chown-maltrail':
+ command => "chown -R ${user}:${user} /opt/maltrail",
+ require => Exec['install-maltrail'],
+ }
+
+ # Copy custom configuration file from template
+ file { '/opt/maltrail/maltrail.conf':
+ ensure => file,
+ content => template("${modulename}/maltrail.conf.erb"),
+ owner => $user,
+ group => $user,
+ mode => '0644',
+ require => Exec['install-maltrail'],
+ }
+}
diff --git a/modules/vulnerabilities/unix/http/maltrail_rce/manifests/service.pp b/modules/vulnerabilities/unix/http/maltrail_rce/manifests/service.pp
new file mode 100644
index 000000000..43a9ca6f7
--- /dev/null
+++ b/modules/vulnerabilities/unix/http/maltrail_rce/manifests/service.pp
@@ -0,0 +1,28 @@
+# Service management for MalTrail
+class maltrail_rce::service {
+ require maltrail_rce::config
+
+ $secgen_parameters = secgen_functions::get_parameters($::base64_inputs_file)
+ $user = $secgen_parameters['unix_username'][0]
+
+ Exec { path => [ '/bin/', '/sbin/' , '/usr/bin/', '/usr/sbin/' ] }
+
+ file { '/etc/systemd/system/maltrail.service':
+ content => template('maltrail_rce/maltrail.service.erb'),
+ owner => 'root',
+ group => 'root',
+ mode => '0644',
+ }
+
+ exec { 'daemon-reload':
+ command => 'systemctl daemon-reload',
+ refreshonly => true,
+ subscribe => File['/etc/systemd/system/maltrail.service'],
+ }
+
+ service { 'maltrail':
+ ensure => running,
+ enable => true,
+ require => [File['/etc/systemd/system/maltrail.service'], Exec['daemon-reload']],
+ }
+}
diff --git a/modules/vulnerabilities/unix/http/maltrail_rce/secgen_metadata.xml b/modules/vulnerabilities/unix/http/maltrail_rce/secgen_metadata.xml
new file mode 100644
index 000000000..054d619f5
--- /dev/null
+++ b/modules/vulnerabilities/unix/http/maltrail_rce/secgen_metadata.xml
@@ -0,0 +1,85 @@
+
+
+
+ MalTrail RCE
+ Rosie Fletcher
+ MIT
+ MalTrail versions <= 0.52 contain an unauthenticated command injection vulnerability
+ in the login endpoint. A remote attacker can execute arbitrary operating system commands
+ via the 'username' parameter in a POST request to the /login endpoint. This occurs
+ due to unsafe handling of user-supplied input passed to subprocess.check_output() in
+ core/http.py, allowing injection of shell metacharacters. Exploitation does not require
+ authentication and commands are executed with the privileges of the MalTrail server process.
+
+ This module creates a dual-flag CTF challenge: Flag 1 is found via RCE exploitation and
+ file system access, while Flag 2 is hidden within MalTrail's threat data and discovered
+ via web UI analysis.
+
+ http
+ user_rwx
+ remote
+ linux
+ low
+
+ port
+ strings_to_leak
+ leaked_filenames
+ unix_username
+ maltrail_data
+
+
+
+ 8338
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ medium
+
+
+
+
+
+
+
+
+ CVE-2025-34073
+ 10
+ AV:N/AC:L/Au:N/C:C/I:C/A:C
+ https://github.com/stamparm/maltrail
+ https://github.com/stamparm/maltrail/issues/19146
+ https://nvd.nist.gov/vuln/detail/CVE-2025-34073
+ https://vulncheck.com/advisories/stamparm-maltrail-rce
+ MalTrail
+ MIT
+
+
+
+ server-side misconfiguration and vulnerable components
+
+
+ EXPLOITATION
+ EXPLOITATION FRAMEWORKS
+
+
+ CVEs and CWEs
+
+
+ Penetration Testing - Software Tools
+ Penetration Testing - Active Penetration
+
+
\ No newline at end of file
diff --git a/modules/vulnerabilities/unix/http/maltrail_rce/templates/maltrail.conf.erb b/modules/vulnerabilities/unix/http/maltrail_rce/templates/maltrail.conf.erb
new file mode 100644
index 000000000..5da09824e
--- /dev/null
+++ b/modules/vulnerabilities/unix/http/maltrail_rce/templates/maltrail.conf.erb
@@ -0,0 +1,12 @@
+# [Server]
+HTTP_ADDRESS 0.0.0.0
+HTTP_PORT <%= @port %>
+USE_SSL false
+LOG_DIR /var/log/maltrail
+UPDATE_PERIOD 24
+# USERS disabled for passwordless access to web UI
+
+# [Sensor]
+MONITOR_INTERFACE any
+CAPTURE_BUFFER 1GB
+USE_MULTIPROCESSING false
diff --git a/modules/vulnerabilities/unix/http/maltrail_rce/templates/maltrail.service.erb b/modules/vulnerabilities/unix/http/maltrail_rce/templates/maltrail.service.erb
new file mode 100644
index 000000000..875a9de83
--- /dev/null
+++ b/modules/vulnerabilities/unix/http/maltrail_rce/templates/maltrail.service.erb
@@ -0,0 +1,15 @@
+[Unit]
+Description=MalTrail Server v0.52 (Vulnerable to CVE-2025-34073)
+After=network.target
+
+[Service]
+Type=simple
+User=<%= @user %>
+Group=<%= @user %>
+WorkingDirectory=/opt/maltrail
+ExecStart=/usr/bin/python3 /opt/maltrail/server.py -c /opt/maltrail/maltrail.conf
+Restart=always
+RestartSec=10
+
+[Install]
+WantedBy=multi-user.target
diff --git a/scenarios/ctf/off_the_trail.xml b/scenarios/ctf/off_the_trail.xml
new file mode 100644
index 000000000..639a58e70
--- /dev/null
+++ b/scenarios/ctf/off_the_trail.xml
@@ -0,0 +1,135 @@
+
+
+
+
+ Off the trail
+ Rosie Fletcher
+ A multi-stage penetration testing exercise where students:
+ 1. Exploit CVE-2025-34073 (MalTrail RCE) for initial access
+ 2. Find the first flag via file system exploration
+ 3. Analyze MalTrail's web interface to discover a second hidden flag embedded in threat data
+ This lab teaches the full attack lifecycle from reconnaissance to root compromise.
+
+
+ ctf
+ attack-ctf
+ pwn-ctf
+ low
+
+
+
+ EXPLOITATION
+ EXPLOITATION FRAMEWORKS
+
+
+ PENETRATION TESTING - SOFTWARE TOOLS
+ PENETRATION TESTING - ACTIVE PENETRATION
+
+
+
+
+ Elevated privileges
+
+
+
+
+ kill chains
+
+
+ cyber kill chain
+
+
+
+
+ Injection vulnerabilities
+ Command injection
+
+
+
+ attack_vm
+
+
+
+ 172.16.0.2
+ 172.16.0.3
+
+
+
+
+ {"username":"kali","password":"kali","super_user":"true","strings_to_leak":[],"leaked_filenames":[]}
+
+
+
+
+
+ {"username":"kali","password":"kali","super_user":"true","strings_to_leak":[],"leaked_filenames":[]}
+
+
+ false
+
+
+
+
+
+
+
+
+ IP_addresses
+
+
+
+
+
+
+
+ spoiler_admin_pass
+
+
+
+
+
+
+ server
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ maltrail_flag
+
+
+
+
+
+
+
+
+
+
+
+
+
+ IP_addresses
+
+
+
+
+ spoiler_admin_pass
+
+
+
+
+