| 1 | # -*- coding: utf-8 -*- |
|---|
| 2 | # Copyright (C) 2008 Adriano Monteiro Marques. |
|---|
| 3 | # |
|---|
| 4 | # Author: João Paulo de Souza Medeiros <ignotus21@gmail.com> |
|---|
| 5 | # Luis A. Bastiao Silva <luis.kop@gmail.com> |
|---|
| 6 | # |
|---|
| 7 | # This program is free software; you can redistribute it and/or modify |
|---|
| 8 | # it under the terms of the GNU General Public License as published by |
|---|
| 9 | # the Free Software Foundation; either version 2 of the License, or |
|---|
| 10 | # (at your option) any later version. |
|---|
| 11 | # |
|---|
| 12 | # This program is distributed in the hope that it will be useful, |
|---|
| 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | # GNU General Public License for more details. |
|---|
| 16 | # |
|---|
| 17 | # You should have received a copy of the GNU General Public License |
|---|
| 18 | # along with this program; if not, write to the Free Software |
|---|
| 19 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 20 | |
|---|
| 21 | """ |
|---|
| 22 | |
|---|
| 23 | A GraphBuilder is a class that make a Graph across NmapParser |
|---|
| 24 | |
|---|
| 25 | """ |
|---|
| 26 | |
|---|
| 27 | from umit.core.radialnet.Graph import * |
|---|
| 28 | from umit.gui.radialnet.RadialNet import NetNode |
|---|
| 29 | |
|---|
| 30 | |
|---|
| 31 | COLORS = [(0.0, 1.0, 0.0), |
|---|
| 32 | (1.0, 1.0, 0.0), |
|---|
| 33 | (1.0, 0.0, 0.0)] |
|---|
| 34 | |
|---|
| 35 | BASE_RADIUS = 5.5 |
|---|
| 36 | NONE_RADIUS = 4.5 |
|---|
| 37 | |
|---|
| 38 | |
|---|
| 39 | |
|---|
| 40 | class GraphBuilder(Graph): |
|---|
| 41 | def __init__(self): |
|---|
| 42 | Graph.__init__(self) |
|---|
| 43 | |
|---|
| 44 | def __set_default_values(self, node): |
|---|
| 45 | |
|---|
| 46 | node.set_info({'ip':'0.0.0.0', 'hostname':'Umit'}) |
|---|
| 47 | node.set_draw_info({'color':(1,0,0), |
|---|
| 48 | 'radius':BASE_RADIUS, |
|---|
| 49 | 'line-width':3, |
|---|
| 50 | 'line-color':(0.5,0,0)}) |
|---|
| 51 | |
|---|
| 52 | |
|---|
| 53 | def __calc_vulnerability_level(self, node, host): |
|---|
| 54 | """ |
|---|
| 55 | """ |
|---|
| 56 | ports = host.ports |
|---|
| 57 | number_ports = len(host.ports) |
|---|
| 58 | |
|---|
| 59 | node.set_info({'number_of_scanned_ports': number_ports}) |
|---|
| 60 | |
|---|
| 61 | if number_ports < 3: |
|---|
| 62 | node.set_info({'vulnerability_score': 0}) |
|---|
| 63 | |
|---|
| 64 | elif number_ports < 7: |
|---|
| 65 | node.set_info({'vulnerability_score': 1}) |
|---|
| 66 | |
|---|
| 67 | else: |
|---|
| 68 | node.set_info({'vulnerability_score': 2}) |
|---|
| 69 | |
|---|
| 70 | def __set_node_info(self, node, host): |
|---|
| 71 | """ |
|---|
| 72 | """ |
|---|
| 73 | node.set_info({'host_reference': host}) |
|---|
| 74 | |
|---|
| 75 | # getting vulnerability score |
|---|
| 76 | self.__calc_vulnerability_level(node, host) |
|---|
| 77 | |
|---|
| 78 | radius = BASE_RADIUS + node.get_info('number_of_scanned_ports') / 2 |
|---|
| 79 | node.set_draw_info({'color':COLORS[\ |
|---|
| 80 | node.get_info('vulnerability_score')],\ |
|---|
| 81 | 'radius':radius}) |
|---|
| 82 | |
|---|
| 83 | # getting address and hostnames |
|---|
| 84 | for addr in host.address: |
|---|
| 85 | if addr['addrtype'] == 'ipv4': |
|---|
| 86 | host_addresses = addr |
|---|
| 87 | break |
|---|
| 88 | else: |
|---|
| 89 | host_addresses = {} |
|---|
| 90 | if host_addresses.has_key('vendor') and host_addresses['vendor'] == '': |
|---|
| 91 | host_addresses['vendor'] = None |
|---|
| 92 | |
|---|
| 93 | addresses = list() |
|---|
| 94 | |
|---|
| 95 | addresses.append(host_addresses) |
|---|
| 96 | |
|---|
| 97 | node.set_info({'addresses': addresses}) |
|---|
| 98 | node.set_info({'ip': addresses[0]['addr']}) |
|---|
| 99 | |
|---|
| 100 | host_hostnames = host.hostnames |
|---|
| 101 | if host_hostnames: |
|---|
| 102 | hostnames = list() |
|---|
| 103 | for host_hostname in host_hostnames: |
|---|
| 104 | hostnames.append(host_hostname.copy()) |
|---|
| 105 | |
|---|
| 106 | node.set_info({'hostnames': hostnames}) |
|---|
| 107 | node.set_info({'hostname': hostnames[0]['name']}) |
|---|
| 108 | |
|---|
| 109 | # getting uptime |
|---|
| 110 | #xml_uptime = host.search_children('uptime', True) |
|---|
| 111 | host_uptime = host.get_uptime() |
|---|
| 112 | if host_uptime != {}: |
|---|
| 113 | node.set_info({'uptime': host_uptime}) |
|---|
| 114 | |
|---|
| 115 | # getting os fingerprint information |
|---|
| 116 | |
|---|
| 117 | os = {} |
|---|
| 118 | |
|---|
| 119 | host_osfingerprint = host.osfingerprint |
|---|
| 120 | host_osclasses = host.osclass |
|---|
| 121 | host_osmatches = host.osmatch |
|---|
| 122 | host_portsused = host.portused |
|---|
| 123 | os['fingerprint'] = "" |
|---|
| 124 | if host_osfingerprint and host_osfingerprint[0].has_key('fingerprint'): |
|---|
| 125 | os['fingerprint'] = host_osfingerprint[0]['fingerprint'] |
|---|
| 126 | |
|---|
| 127 | if len(host_osclasses) > 0: |
|---|
| 128 | |
|---|
| 129 | types = ['router', 'wap', 'switch', 'firewall'] |
|---|
| 130 | |
|---|
| 131 | for _type in types: |
|---|
| 132 | if _type in host_osclasses[0]['type'].lower(): |
|---|
| 133 | node.set_info({'device_type': _type}) |
|---|
| 134 | |
|---|
| 135 | os_classes = [] |
|---|
| 136 | |
|---|
| 137 | for host_osclass in host_osclasses: |
|---|
| 138 | |
|---|
| 139 | os_class = {} |
|---|
| 140 | |
|---|
| 141 | os_class['type'] = host_osclass['type'] |
|---|
| 142 | os_class['vendor'] = host_osclass['vendor'] |
|---|
| 143 | os_class['accuracy'] = int(host_osclass['accuracy']) |
|---|
| 144 | os_class['os_family'] = host_osclass['osfamily'] |
|---|
| 145 | |
|---|
| 146 | if host_osclass.has_key('osgen'): |
|---|
| 147 | os_class['os_gen'] = host_osclass['osgen'] |
|---|
| 148 | |
|---|
| 149 | os_classes.append(os_class) |
|---|
| 150 | |
|---|
| 151 | os['classes'] = os_classes |
|---|
| 152 | if len(host_osmatches) > 0: |
|---|
| 153 | os_matches = [] |
|---|
| 154 | |
|---|
| 155 | for host_osmatch in host_osmatches: |
|---|
| 156 | |
|---|
| 157 | os_match = {} |
|---|
| 158 | os_match['name'] = host_osmatch['name'] |
|---|
| 159 | if host_osmatch.get('accuracy', None): |
|---|
| 160 | os_match['accuracy'] = int(host_osmatch['accuracy']) |
|---|
| 161 | # TODO/FIXME: |
|---|
| 162 | #os_match['db_line'] = int(host_osmatch['line']) |
|---|
| 163 | os_match['db_line'] = 0 |
|---|
| 164 | os_matches.append(os_match) |
|---|
| 165 | |
|---|
| 166 | os['matches'] = os_matches |
|---|
| 167 | if len(host_portsused) > 0: |
|---|
| 168 | |
|---|
| 169 | os_portsused = [] |
|---|
| 170 | |
|---|
| 171 | for host_portused in host_portsused: |
|---|
| 172 | host_portused['protocol'] = host_portused['proto'] |
|---|
| 173 | host_portused['id'] = int(host_portused['portid']) |
|---|
| 174 | os_portsused.append(host_portused) |
|---|
| 175 | |
|---|
| 176 | os['used_ports'] = os_portsused |
|---|
| 177 | |
|---|
| 178 | node.set_info({'os': os}) |
|---|
| 179 | |
|---|
| 180 | # getting (copies of) sequences information |
|---|
| 181 | host_tcpsequence = host.tcpsequence.copy() |
|---|
| 182 | host_ipidsequence = host.ipidsequence.copy() |
|---|
| 183 | host_tcptssequence = host.tcptssequence.copy() |
|---|
| 184 | |
|---|
| 185 | sequences = {} |
|---|
| 186 | |
|---|
| 187 | if host_tcpsequence: |
|---|
| 188 | if host_tcpsequence['index']: |
|---|
| 189 | host_tcpsequence['index'] = int(host_tcpsequence['index']) |
|---|
| 190 | host_tcpsequence['values'] = host_tcpsequence['values'].split(',') |
|---|
| 191 | sequences['tcp'] = host_tcpsequence |
|---|
| 192 | |
|---|
| 193 | if host_ipidsequence: |
|---|
| 194 | ip_id = host_ipidsequence |
|---|
| 195 | ip_id['values'] = host_ipidsequence['values'].split(',') |
|---|
| 196 | |
|---|
| 197 | sequences['ip_id'] = ip_id |
|---|
| 198 | |
|---|
| 199 | if host_tcptssequence: |
|---|
| 200 | if host_tcptssequence.get('values', None): |
|---|
| 201 | host_tcptssequence['values'] = \ |
|---|
| 202 | host_tcptssequence['values'].split(',') |
|---|
| 203 | |
|---|
| 204 | sequences['tcp_ts'] = host_tcptssequence |
|---|
| 205 | |
|---|
| 206 | node.set_info({'sequences': sequences}) |
|---|
| 207 | |
|---|
| 208 | # host is host filtered |
|---|
| 209 | filtered = False |
|---|
| 210 | |
|---|
| 211 | host_filtered = host.status['state'] |
|---|
| 212 | if host_filtered=="filtered": |
|---|
| 213 | filtered=True |
|---|
| 214 | |
|---|
| 215 | ## Search in ports |
|---|
| 216 | filtered_ports = host.get_filtered_ports() |
|---|
| 217 | |
|---|
| 218 | |
|---|
| 219 | if filtered or filtered_ports > 0: |
|---|
| 220 | node.set_info({'filtered': True}) |
|---|
| 221 | |
|---|
| 222 | # getting ports information |
|---|
| 223 | |
|---|
| 224 | host_ports = host.ports |
|---|
| 225 | host_extraports = host.extraports |
|---|
| 226 | ports = [] |
|---|
| 227 | |
|---|
| 228 | for port in host_ports: |
|---|
| 229 | port = port.copy() # Do not change the original port |
|---|
| 230 | port['id'] = int(port['portid']) |
|---|
| 231 | # TODO: Not ready to integrate right now |
|---|
| 232 | #for script in xml_scripts: |
|---|
| 233 | #scripts.append(dict()) |
|---|
| 234 | #for key in script.get_keys(): |
|---|
| 235 | #scripts[-1][key] = script.get_attr(key) |
|---|
| 236 | |
|---|
| 237 | service = {} |
|---|
| 238 | # TODO: Get another information - NmapParser update need. |
|---|
| 239 | if 'name' in port: |
|---|
| 240 | service['name'] = port.pop('name') |
|---|
| 241 | service['version'] = port.pop('version', '') |
|---|
| 242 | service['method'] = port.pop('method', '') |
|---|
| 243 | service['product'] = port.pop('product', '') |
|---|
| 244 | service['extrainfo'] = port.pop('extrainfo', '') |
|---|
| 245 | service['conf'] = port.pop('conf', '') |
|---|
| 246 | |
|---|
| 247 | port['state'] = {'state': port['state']} |
|---|
| 248 | port['scripts'] = {} |
|---|
| 249 | port['service'] = service |
|---|
| 250 | |
|---|
| 251 | ports.append(port) |
|---|
| 252 | |
|---|
| 253 | node.set_info({'ports':ports}) |
|---|
| 254 | |
|---|
| 255 | all_extraports = list() |
|---|
| 256 | #print host_extraports |
|---|
| 257 | for extraports in host_extraports: |
|---|
| 258 | extraports = extraports.copy() # Do not change the original eport |
|---|
| 259 | extraports['count'] = int(extraports['count']) |
|---|
| 260 | extraports['reason'] = list() |
|---|
| 261 | extraports['all_reason'] = list() |
|---|
| 262 | |
|---|
| 263 | |
|---|
| 264 | # TODO: implement this |
|---|
| 265 | |
|---|
| 266 | #xml_extrareasons = xml_extraport.search_children('extrareasons', |
|---|
| 267 | #deep=True) |
|---|
| 268 | |
|---|
| 269 | #for extrareason in xml_extrareasons: |
|---|
| 270 | |
|---|
| 271 | #extraports['reason'].append(extrareason.get_attr('reason')) |
|---|
| 272 | #extraports['all_reason'].append(dict()) |
|---|
| 273 | |
|---|
| 274 | #for key in extrareason.get_keys(): |
|---|
| 275 | |
|---|
| 276 | #value = extrareason.get_attr(key) |
|---|
| 277 | |
|---|
| 278 | #if key == 'count': |
|---|
| 279 | #value = int(value) |
|---|
| 280 | |
|---|
| 281 | #extraports['all_reason'][-1][key] = value |
|---|
| 282 | |
|---|
| 283 | all_extraports.append(extraports) |
|---|
| 284 | |
|---|
| 285 | node.set_info({'extraports':all_extraports}) |
|---|
| 286 | |
|---|
| 287 | # getting traceroute information |
|---|
| 288 | trace = host.trace.copy() |
|---|
| 289 | if trace and trace['hop']: |
|---|
| 290 | |
|---|
| 291 | host_hops = trace['hop'] |
|---|
| 292 | hops = [] |
|---|
| 293 | |
|---|
| 294 | for host_hop in host_hops: |
|---|
| 295 | hop = host_hop.copy() |
|---|
| 296 | hostname = host_hop.get('host', None) |
|---|
| 297 | hop['ttl'] = int(hop['ttl']) |
|---|
| 298 | hop['host'] = (hostname, '')[hostname is None] |
|---|
| 299 | if 'host' in hop: |
|---|
| 300 | hop.pop('host') |
|---|
| 301 | |
|---|
| 302 | hops.append(hop) |
|---|
| 303 | |
|---|
| 304 | trace['hops'] = hops |
|---|
| 305 | trace['protocol'] = trace['proto'] |
|---|
| 306 | |
|---|
| 307 | node.set_info({'trace':trace}) |
|---|
| 308 | |
|---|
| 309 | |
|---|
| 310 | def make(self, parse): |
|---|
| 311 | """ |
|---|
| 312 | Make a Graph |
|---|
| 313 | """ |
|---|
| 314 | #Get Hosts |
|---|
| 315 | hosts = parse.get_hosts() |
|---|
| 316 | |
|---|
| 317 | nodes = list() |
|---|
| 318 | index = 1 |
|---|
| 319 | |
|---|
| 320 | # setting initial reference host |
|---|
| 321 | nodes.append(NetNode(0)) |
|---|
| 322 | node = nodes[-1] |
|---|
| 323 | |
|---|
| 324 | self.__set_default_values(node) |
|---|
| 325 | |
|---|
| 326 | # for each host in hosts just mount the graph |
|---|
| 327 | for host in hosts: |
|---|
| 328 | trace = host.trace.copy() |
|---|
| 329 | # if host has traceroute information mount graph |
|---|
| 330 | if trace and trace['hop']: |
|---|
| 331 | |
|---|
| 332 | prev_node = nodes[0] |
|---|
| 333 | |
|---|
| 334 | hops = trace['hop'] |
|---|
| 335 | ttls = [int(hop['ttl']) for hop in hops] |
|---|
| 336 | |
|---|
| 337 | # getting nodes of host by ttl |
|---|
| 338 | for ttl in range(1, max(ttls) + 1): |
|---|
| 339 | if ttl in ttls: |
|---|
| 340 | _hop = host.get_hop_by_ttl(ttl) |
|---|
| 341 | if _hop == None: |
|---|
| 342 | continue |
|---|
| 343 | hop = _hop.copy() |
|---|
| 344 | for node in nodes: |
|---|
| 345 | if hop['ipaddr'] == node.get_info('ip'): |
|---|
| 346 | break |
|---|
| 347 | |
|---|
| 348 | else: |
|---|
| 349 | |
|---|
| 350 | nodes.append(NetNode(index)) |
|---|
| 351 | node = nodes[-1] |
|---|
| 352 | index += 1 |
|---|
| 353 | |
|---|
| 354 | node.set_draw_info({'valid':True}) |
|---|
| 355 | node.set_info({'ip':hop['ipaddr']}) |
|---|
| 356 | node.set_draw_info({'color':(1,1,1), |
|---|
| 357 | 'radius':NONE_RADIUS}) |
|---|
| 358 | if hop.has_key('host') and hop['host'] is not None: |
|---|
| 359 | node.set_info(\ |
|---|
| 360 | {'host':hop['host']}) |
|---|
| 361 | |
|---|
| 362 | rtt = hop['rtt'] |
|---|
| 363 | |
|---|
| 364 | if rtt != '--': |
|---|
| 365 | self.set_connection(node, prev_node, float(rtt)) |
|---|
| 366 | |
|---|
| 367 | else: |
|---|
| 368 | self.set_connection(node, prev_node) |
|---|
| 369 | |
|---|
| 370 | else: |
|---|
| 371 | |
|---|
| 372 | nodes.append(NetNode(index)) |
|---|
| 373 | node = nodes[-1] |
|---|
| 374 | index += 1 |
|---|
| 375 | |
|---|
| 376 | node.set_draw_info({'valid':False}) |
|---|
| 377 | node.set_info({'ip':None, 'host':None}) |
|---|
| 378 | node.set_draw_info({'color':(1,1,1), \ |
|---|
| 379 | 'radius':NONE_RADIUS}) |
|---|
| 380 | |
|---|
| 381 | self.set_connection(node, prev_node) |
|---|
| 382 | |
|---|
| 383 | prev_node = node |
|---|
| 384 | |
|---|
| 385 | # for each full scanned host |
|---|
| 386 | for host in hosts: |
|---|
| 387 | |
|---|
| 388 | for addr in host.address: |
|---|
| 389 | if addr['addrtype'] == 'ipv4': |
|---|
| 390 | ip = addr |
|---|
| 391 | break |
|---|
| 392 | else: |
|---|
| 393 | ip = {} |
|---|
| 394 | for node in nodes: |
|---|
| 395 | if ip.has_key('addr') and ip['addr'] == node.get_info('ip'): |
|---|
| 396 | break |
|---|
| 397 | |
|---|
| 398 | else: |
|---|
| 399 | |
|---|
| 400 | nodes.append(NetNode(index)) |
|---|
| 401 | node = nodes[-1] |
|---|
| 402 | index += 1 |
|---|
| 403 | |
|---|
| 404 | node.set_draw_info({'no_route':True}) |
|---|
| 405 | |
|---|
| 406 | self.set_connection(node, nodes[0]) |
|---|
| 407 | |
|---|
| 408 | node.set_draw_info({'valid':True}) |
|---|
| 409 | node.set_info({'scanned':True}) |
|---|
| 410 | self.__set_node_info(node, host) |
|---|
| 411 | |
|---|
| 412 | self.set_nodes(nodes) |
|---|
| 413 | self.set_main_node_by_id(0) |
|---|
| 414 | |
|---|
| 415 | |
|---|
| 416 | # Test Develpment |
|---|
| 417 | def main(): |
|---|
| 418 | from umit.core.NmapParser import NmapParser |
|---|
| 419 | parser = NmapParser("../../umit-within-radialnet/RadialNet2/share/sample/nmap_example.xml") |
|---|
| 420 | #parser = NmapParser("RadialNet2/share/sample/no_trace.xml") |
|---|
| 421 | parser.parse() |
|---|
| 422 | |
|---|
| 423 | graph = GraphBuilder() |
|---|
| 424 | graph.make(parser) |
|---|
| 425 | |
|---|
| 426 | |
|---|
| 427 | if __name__=="__main__": |
|---|
| 428 | main() |
|---|
| 429 | |
|---|
| 430 | |
|---|
| 431 | |
|---|