CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/parser/ci_document.rb
Views: 11779
1
# -*- coding: binary -*-
2
require "rex/parser/nokogiri_doc_mixin"
3
4
5
module Rex
6
module Parser
7
8
# If Nokogiri is available, define the document class.
9
load_nokogiri && class CIDocument < Nokogiri::XML::SAX::Document
10
11
include NokogiriDocMixin
12
13
attr_reader :text
14
15
def initialize(*args)
16
super(*args)
17
@state[:has_text] = true
18
end
19
20
# Triggered every time a new element is encountered. We keep state
21
# ourselves with the @state variable, turning things on when we
22
# get here (and turning things off when we exit in end_element()).
23
def start_element(name=nil,attrs=[])
24
attrs = normalize_attrs(attrs)
25
block = @block
26
27
r = { :e => name }
28
attrs.each { |pair| r[pair[0]] = pair[1] }
29
30
if @state[:path]
31
@state[:path].push r
32
end
33
34
case name
35
when "entity"
36
@state[:path] = [ r ]
37
record_device(r)
38
when "property"
39
return if not @state[:address]
40
return if not @state[:props]
41
@state[:props] << [ r["type"], r["key"]]
42
end
43
end
44
45
# When we exit a tag, this is triggered.
46
def end_element(name=nil)
47
block = @block
48
case name
49
when "entity" # Wrap it up
50
if @state[:address]
51
host_object = report_host &block
52
report_services(host_object)
53
report_vulns(host_object)
54
end
55
# Reset the state once we close a host
56
@report_data = {:workspace => @args[:workspace]}
57
@state[:root] = {}
58
when "property"
59
if @state[:props]
60
@text.strip! if @text
61
process_property
62
@state[:props].pop
63
end
64
end
65
@state[:path].pop
66
@text = nil
67
end
68
69
def record_device(info)
70
if info["class"] and info["class"] == "host" and info["name"]
71
address = info["name"].to_s.gsub(/^.*\//, '')
72
return if address !~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/
73
@state[:address] = address
74
@state[:props] = []
75
end
76
end
77
78
def process_property
79
return if not @state[:props]
80
return if not @state[:props].length > 0
81
@state[:root] ||= {}
82
@cobj = @state[:root]
83
property_parser(0)
84
end
85
86
def property_parser(idx)
87
return if not @state[:props][idx]
88
case @state[:props][idx][0]
89
when "container", "ports", "entity", "properties"
90
@cobj[ @state[:props][idx][1] ] ||= {}
91
@cobj = @cobj[ @state[:props][idx][1] ]
92
else
93
@cobj[ state[:props][idx][1] ] = @text
94
end
95
property_parser(idx + 1)
96
end
97
98
def report_host(&block)
99
@report_data = {
100
:ports => [:ignore],
101
:state => Msf::HostState::Alive,
102
:host => @state[:address]
103
}
104
105
if @state[:root]["dns names"] and @state[:root]["dns names"].keys.length > 0
106
@report_data[:name] = @state[:root]["dns names"].keys.first
107
end
108
109
if host_is_okay
110
@report_data.delete(:ports)
111
112
db.emit(:address, @report_data[:host],&block) if block
113
host_object = db_report(:host, @report_data.merge(
114
:workspace => @args[:workspace] ) )
115
if host_object
116
db.report_import_note(host_object.workspace, host_object)
117
end
118
host_object
119
end
120
end
121
122
def report_services(host_object)
123
return unless host_object.kind_of? ::Mdm::Host
124
125
snames = {}
126
( @state[:root]["services"] || {} ).each_pair do |sname, sinfo|
127
sinfo.each_pair do |pinfo,pdata|
128
snames[pinfo] = sname.dup
129
end
130
end
131
132
reported = []
133
if @state[:root]["tcp_ports"]
134
@state[:root]["tcp_ports"].each_pair do |pn, ps|
135
ps = "open" if ps == "listen"
136
svc = { :port => pn.to_i, :state => ps, :proto => 'tcp'}
137
if @state[:root]["Banners"] and @state[:root]["Banners"][pn.to_s]
138
svc[:info] = @state[:root]["Banners"][pn.to_s]
139
end
140
svc[:name] = snames["#{pn}-tcp"] if snames["#{pn}-tcp"]
141
reported << db_report(:service, svc.merge(:host => host_object))
142
end
143
end
144
145
if @state[:root]["udp_ports"]
146
@state[:root]["udp_ports"].each_pair do |pn, ps|
147
ps = "open" if ps == "listen"
148
svc = { :port => pn.to_i, :state => ps, :proto => 'udp'}
149
svc[:name] = snames["#{pn}-udp"] if snames["#{pn}-tcp"]
150
reported << db_report(:service, svc.merge(:host => host_object))
151
end
152
end
153
154
( @state[:root]["services"] || {} ).each_pair do |sname, sinfo|
155
sinfo.each_pair do |pinfo,pdata|
156
sport,sproto = pinfo.split("-")
157
db_report(:note, {
158
:host => host_object,
159
:port => sport.to_i,
160
:proto => sproto,
161
:ntype => "ci.#{sname}.fingerprint",
162
:data => pdata
163
})
164
end
165
end
166
167
reported
168
end
169
170
def report_vulns(host_object)
171
vuln_count = 0
172
block = @block
173
return unless host_object.kind_of? ::Mdm::Host
174
return unless @state[:root]["Vulnerabilities"]
175
@state[:root]["Vulnerabilities"].each_pair do |cve, vinfo|
176
vinfo.each_pair do |vname, vdesc|
177
data = {
178
:workspace => host_object.workspace,
179
:host => host_object,
180
:name => vname,
181
:info => vdesc,
182
:refs => [ cve ]
183
}
184
db_report(:vuln, data)
185
end
186
end
187
end
188
189
end
190
end
191
end
192
193
194