CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/ip/ipidseq.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'timeout'
7
8
class MetasploitModule < Msf::Auxiliary
9
include Msf::Exploit::Capture
10
include Msf::Auxiliary::Scanner
11
include Msf::Auxiliary::Report
12
13
def initialize
14
super(
15
'Name' => 'IPID Sequence Scanner',
16
'Description' => %q{
17
This module will probe hosts' IPID sequences and classify
18
them using the same method Nmap uses when it's performing
19
its IPID Idle Scan (-sI) and OS Detection (-O).
20
21
Nmap's probes are SYN/ACKs while this module's are SYNs.
22
While this does not change the underlying functionality,
23
it does change the chance of whether or not the probe
24
will be stopped by a firewall.
25
26
Nmap's Idle Scan can use hosts whose IPID sequences are
27
classified as "Incremental" or "Broken little-endian incremental".
28
},
29
'Author' => 'kris katterjohn',
30
'License' => MSF_LICENSE
31
)
32
33
register_options([
34
Opt::RPORT(80),
35
OptInt.new('TIMEOUT', [true, "The reply read timeout in milliseconds", 500]),
36
OptString.new('INTERFACE', [false, 'The name of the interface'])
37
])
38
39
register_advanced_options([
40
OptInt.new('SAMPLES', [true, "The IPID sample size", 6])
41
])
42
43
deregister_options('FILTER','PCAPFILE')
44
end
45
46
def rport
47
datastore['RPORT'].to_i
48
end
49
50
def run_host(ip)
51
open_pcap
52
53
raise "SAMPLES option must be >= 2" if datastore['SAMPLES'] < 2
54
55
pcap = self.capture
56
57
shost = Rex::Socket.source_address(ip)
58
59
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
60
61
ipids = []
62
63
pcap.setfilter(getfilter(shost, ip, rport))
64
65
datastore['SAMPLES'].times do
66
sport = rand(0xffff - 1025) + 1025
67
68
probe = buildprobe(shost, sport, ip, rport)
69
70
next unless capture_sendto(probe, ip)
71
72
reply = probereply(pcap, to)
73
74
next if not reply
75
76
ipids << reply.ip_id
77
end
78
79
close_pcap
80
81
return if ipids.empty?
82
83
print_status("#{ip}'s IPID sequence class: #{analyze(ipids)}")
84
85
#Add Report
86
report_note(
87
:host => ip,
88
:proto => 'ip',
89
:type => 'IPID sequence',
90
:data => "IPID sequence class: #{analyze(ipids)}"
91
)
92
end
93
94
# Based on Nmap's get_ipid_sequence() in osscan2.cc
95
def analyze(ipids)
96
allzeros = true
97
allsame = true
98
mul256 = true
99
inc = true
100
101
# ipids.each do |ipid|
102
# print_status("Got IPID ##{ipid}")
103
# end
104
105
return "Unknown" if ipids.size < 2
106
107
diffs = []
108
i = 1
109
110
while i < ipids.size
111
p = ipids[i - 1]
112
c = ipids[i]
113
114
if p != 0 or c != 0
115
allzeros = false
116
end
117
118
if p <= c
119
diffs[i - 1] = c - p
120
else
121
diffs[i - 1] = c - p + 65536
122
end
123
124
if ipids.size > 2 and diffs[i - 1] > 20000
125
return "Randomized"
126
end
127
128
i += 1
129
end
130
131
return "All zeros" if allzeros
132
133
diffs.each do |diff|
134
if diff > 1000 and ((diff % 256) != 0 or ((diff % 256) == 0 and diff >= 25600))
135
return "Random positive increments"
136
end
137
138
allsame = false if diff != 0
139
140
mul256 = false if diff > 5120 or (diff % 256) != 0
141
142
inc = false if diff >= 10
143
end
144
145
return "Constant" if allsame
146
147
return "Broken little-endian incremental!" if mul256
148
149
return "Incremental!" if inc
150
151
"Unknown"
152
end
153
154
def getfilter(shost, dhost, dport)
155
"tcp and src host #{dhost} and src port #{dport} and " +
156
"dst host #{shost}"
157
end
158
159
# This gets set via the usual capture_sendto interface
160
def buildprobe(shost, sport, dhost, dport)
161
p = PacketFu::TCPPacket.new
162
p.ip_saddr = shost
163
p.ip_daddr = dhost
164
p.tcp_sport = sport
165
p.tcp_dport = dport
166
p.tcp_flags.syn = 1
167
p.recalc
168
p
169
end
170
171
def probereply(pcap, to)
172
reply = nil
173
174
begin
175
Timeout.timeout(to) do
176
pcap.each do |r|
177
pkt = PacketFu::Packet.parse(r)
178
next unless pkt.is_tcp?
179
next unless pkt.tcp_flags.syn == 1 || pkt.tcp_flags.rst == 1
180
reply = pkt
181
break
182
end
183
end
184
rescue Timeout::Error
185
end
186
187
return reply
188
end
189
end
190
191