Changeset 5543
- Timestamp:
- 02/18/10 12:40:31 (5 months ago)
- Files:
-
- 1 modified
-
pm/trunk/audits/passive/http/sources/main.py (modified) (25 diffs)
Legend:
- Unmodified
- Added
- Removed
-
pm/trunk/audits/passive/http/sources/main.py
r5449 r5543 23 23 24 24 This module uses TCP reassembler exposed in TCP decoder. 25 >>> from umit.pm.core.auditutils import audit_unittest 26 >>> audit_unittest('-f ethernet,ip,tcp,http', 'http-digest.pcap') 27 dissector.http.info HTTP DIGEST : 10.0.2.102:1093 <-> 10.0.1.101:80 USERNAME: Susan PASSWORD: realm=INS.COM, qop=auth, algorithm=MD5-sess, uri=/Security/Digest/, nonce=20d1da125f2fc6013eec4a6f4d1cf34133fffe449de04ce7f6b8e6110c0ee23867e01f774a96c8d5, nc=00000001, cnonce=52447499ed25edfa526e88d3623882ed, response=6e33ca77c2acbbc5ffc38df51b2f5702 25 28 """ 26 29 … … 46 49 NTLM_WAIT_RESPONSE = 0 47 50 48 FORM_USERNAME = HTTP_REQUEST = 0 49 FORM_PASSWORD = HTTP_RESPONSE = 1 51 FORM_USERNAME = 0 52 FORM_PASSWORD = 1 53 54 HTTP_REQUEST = 0 55 HTTP_RESPONSE = 1 50 56 51 57 g_fields = None 52 58 53 59 def form_extract(data, type=FORM_USERNAME): 60 global g_fields 61 54 62 if not g_fields: 55 63 return None … … 64 72 65 73 class HTTPRequest(object): 66 http_type = HTTP_REQUEST 67 68 def __init__(self, sess): 74 def __init__(self, sess, manager): 69 75 self.headers_complete = False 70 76 self.content_length = -1 … … 75 81 76 82 self.session = sess 83 self.manager = manager 84 self.http_type = HTTP_REQUEST 77 85 78 86 def feed(self, mpkt, data): … … 85 93 86 94 if self.headers_complete: 87 88 if self.content_length > 0 or self.chunked: 89 if data[end_ptr:]: 90 ret = self._parse_body(data[end_ptr:], end_ptr) 91 92 if ret[0]: 93 self.analyze_headers(mpkt) 94 self._parse_post(mpkt) 95 96 if self.http_type == HTTP_REQUEST: 97 mpkt.set_cfield(HTTP_NAME + '.request', 98 self.body) 99 else: 100 mpkt.set_cfield(HTTP_NAME + '.response', 101 self.body) 102 103 return ret 104 105 return False, end_ptr 95 # If the body is missing continue processing 96 97 finished = self._check_finished() 98 99 if not finished: 100 remaining = data[end_ptr:] 101 102 if not remaining: 103 return False, end_ptr # Not finished. Body missing 104 105 ret = self._parse_body(remaining, end_ptr) 106 107 if ret[0] == True: 108 self.analyze_headers(mpkt) 109 self._parse_post(mpkt) 110 111 if self.http_type == HTTP_REQUEST: 112 mpkt.set_cfield(HTTP_NAME + '.request', 113 self.body) 114 else: 115 mpkt.set_cfield(HTTP_NAME + '.response', 116 self.body) 117 118 self.manager.run_hook_point('http', mpkt) 119 120 # We have finished processing our response/request 121 # at this stage. 122 123 return ret 106 124 107 125 self.analyze_headers(mpkt) 108 return True, end_ptr 126 self.manager.run_hook_point('http', mpkt) 127 128 return finished, end_ptr 109 129 110 130 return False, end_ptr … … 121 141 mpkt.set_cfield(HTTP_NAME + '.response', self.body) 122 142 143 self.manager.run_hook_point('http', mpkt) 144 123 145 return ret 146 147 def _check_finished(self): 148 if self.content_length > 0: 149 return self.content_length == len(self.body) 150 151 if self.chunked: 152 return False 153 154 if 'post' in self.headers: 155 return False 156 157 return True 124 158 125 159 def _parse_headers(self, mpkt, payload): … … 133 167 134 168 if last == 0: 135 return 3169 return 2 136 170 if last == -1: 137 171 return 0 … … 143 177 break 144 178 145 key, value = line.split(' ', 1) 179 try: 180 key, value = line.split(' ', 1) 181 except: 182 # FIXME: dirty hack. 183 # Handle headers like Host:127.0.0.1 184 key, value = line.split(':', 1) 185 key += ':' 146 186 147 187 if key[-1] == ':': … … 158 198 value = int(value) 159 199 self.content_length = value 160 161 200 except ValueError: 162 201 pass … … 169 208 mpkt.set_cfield(HTTP_NAME + '.response_status', value) 170 209 171 elif key == 'authorization': 210 elif key == 'authorization' or \ 211 key == 'www-authenticate': 172 212 if value[0:9].upper() == 'PASSPORT ': 173 213 self._parse_passport(mpkt, value[9:]) … … 178 218 elif value[0:7].upper() == 'DIGEST ': 179 219 self._parse_digest(mpkt, value[7:]) 180 181 elif key == 'www-authenticate': 182 if value[0:5] == 'NTLM ': 220 elif value[0:5] == 'NTLM ': 183 221 self._parse_ntlm(mpkt, value[5:]) 184 222 185 223 self.headers[key].append(value) 224 186 225 187 226 if self.headers_complete: … … 191 230 192 231 def analyze_headers(self, mpkt): 232 if not self.headers: 233 return 234 193 235 mpkt.set_cfield(HTTP_NAME + '.headers', self.headers) 194 236 … … 232 274 mpkt.set_cfield('password', password) 233 275 276 self.report(mpkt, 'GET', username, password) 277 234 278 def _parse_post(self, mpkt): 279 # Export chunked body as list instead as string 280 # and avoid parsing that 281 282 if self.chunked: 283 self.body = map(lambda x: x[1], self.chunks) 284 return 285 286 # No Post header in headers. Don't procede 235 287 if not 'post' in self.headers: 236 288 return … … 242 294 mpkt.set_cfield('username', username) 243 295 mpkt.set_cfield('password', password) 296 297 self.report(mpkt, 'POST', username, password) 244 298 245 299 def _parse_passport(self, mpkt, val): … … 269 323 finally: 270 324 if found: 325 password = ', '.join(values) 326 271 327 mpkt.set_cfield('username', user) 272 mpkt.set_cfield('password', ', '.join(values)) 328 mpkt.set_cfield('password', password) 329 330 self.report(mpkt, 'DIGEST', user, password) 273 331 274 332 def _parse_ntlm(self, mpkt, val): … … 310 368 mpkt.set_cfield('password', password) 311 369 370 self.report(mpkt, 'NTLM', username, password) 371 312 372 self.session.data = None 313 373 314 def _parse_basic(self, val):374 def _parse_basic(self, mpkt, val): 315 375 val = b64decode(val) 316 376 ret = val.split(':', 1) 317 377 318 378 if isinstance(ret, tuple) and len(ret) == 2: 319 mpkt.set_cfield('user ', ret[0])379 mpkt.set_cfield('username', ret[0]) 320 380 mpkt.set_cfield('password', ret[1]) 381 382 self.report(mpkt, 'BASIC', ret[0], ret[1]) 321 383 322 384 def _parse_body(self, payload, end_ptr=0): … … 328 390 idx = payload.find('\r\n') 329 391 330 if not idx: 392 if idx == 0: 393 idx = payload.find('\r\n', 2) 394 395 if idx < 0: 331 396 return False, end_ptr 332 397 … … 335 400 336 401 if clen == 0: 337 self.body = '\r\n'.join(map(lambda x: x[1], self.chunks))402 #self.body = '\r\n'.join(map(lambda x: x[1], self.chunks)) 338 403 return True, idx 339 404 … … 383 448 return True, len(payload) + end_ptr 384 449 450 def report(self, mpkt, typ, username, password): 451 self.manager.user_msg( 452 'HTTP %s : %s:%d <-> %s:%d USERNAME: %s PASSWORD: %s' % \ 453 (typ, mpkt.l3_src, mpkt.l4_src, 454 mpkt.l3_dst, mpkt.l4_dst, 455 username, password), 456 6, HTTP_NAME) 457 385 458 class HTTPResponse(HTTPRequest): 386 http_type = HTTP_RESPONSE 459 def __init__(self, sess, manager): 460 HTTPRequest.__init__(self, sess, manager) 461 462 self.http_type = HTTP_RESPONSE 387 463 388 464 class HTTPSession(object): 389 def __init__(self): 390 self.request = HTTPRequest(self) 391 self.response = HTTPResponse(self) 465 def __init__(self, manager): 466 self.manager = manager 467 self.request = HTTPRequest(self, manager) 468 self.response = HTTPResponse(self, manager) 392 469 393 470 self.requests = [self.request] … … 409 486 self.req_last_len += idx 410 487 411 if ret :412 self.request = HTTPRequest(self )488 if ret == True: 489 self.request = HTTPRequest(self, self.manager) 413 490 self.requests.append(self.request) 414 491 … … 423 500 self.res_last_len += idx 424 501 425 if ret isTrue:426 self.response = HTTPResponse(self )502 if ret == True: 503 self.response = HTTPResponse(self, self.manager) 427 504 self.responses.append(self.response) 505 428 506 429 507 class HTTPDissector(Plugin, PassiveAudit): 430 508 def start(self, reader): 431 509 self.sessions = {} 432 433 conf = AuditManager().get_configuration(HTTP_NAME) 510 self.manager = AuditManager() 511 512 conf = self.manager.get_configuration(HTTP_NAME) 434 513 435 514 self.reassemble = conf['reassemble'] … … 453 532 global g_fields 454 533 455 gflieds = dict(map(lambda x: (x, 0), ufields.split(',')) + \ 456 map(lambda x: (x, 1), pfields.split(','))) 534 g_fields = dict( 535 map(lambda x: (x, FORM_USERNAME), ufields.split(',')) + \ 536 map(lambda x: (x, FORM_PASSWORD), pfields.split(',')) 537 ) 457 538 458 539 def stop(self): 459 conf = AuditManager().get_configuration(HTTP_NAME)540 conf = self.manager.get_configuration(HTTP_NAME) 460 541 461 542 if not self.reassemble: 462 543 for port in HTTP_PORTS: 463 AuditManager().remove_dissector(APP_LAYER_TCP, port,464 self._http_decoder)544 self.manager.remove_dissector(APP_LAYER_TCP, port, 545 self._http_decoder) 465 546 else: 466 547 self.tcpdecoder.remove_analyzer(self._tcp_callback) 467 548 549 self.manager.deregister_hook_point('http') 550 468 551 def register_decoders(self): 469 if self.reassemble: 552 self.manager.register_hook_point('http') 553 554 if not self.reassemble: 470 555 for port in HTTP_PORTS: 471 AuditManager().add_dissector(APP_LAYER_TCP, port,472 self._http_decoder)556 self.manager.add_dissector(APP_LAYER_TCP, port, 557 self._http_decoder) 473 558 474 559 def _http_decoder(self, mpkt): 475 payload = mpkt. get_field('raw.load')560 payload = mpkt.data 476 561 477 562 if not payload: … … 479 564 480 565 try: 481 obj = HTTPRequest(None )566 obj = HTTPRequest(None, self.manager) 482 567 obj.feed(mpkt, payload) 483 568 … … 492 577 obj.http_type = HTTP_RESPONSE 493 578 obj.analyze_headers(mpkt) 494 except :579 except Exception, exc: 495 580 pass 496 581 497 582 def _tcp_callback(self, stream, mpkt): 498 if stream.dport in HTTP_PORTS: 583 if stream.dport in HTTP_PORTS or \ 584 stream.sport in HTTP_PORTS: 499 585 stream.listeners.append(self._process_http) 500 586 501 587 def _process_http(self, stream, mpkt, rcv): 502 if hash(stream)not in self.sessions:503 sess = HTTPSession( )504 self.sessions[ hash(stream)] = sess588 if stream not in self.sessions: 589 sess = HTTPSession(self.manager) 590 self.sessions[stream] = sess 505 591 else: 506 sess = self.sessions[ hash(stream)]592 sess = self.sessions[stream] 507 593 508 594 sess.feed_response(stream.client, mpkt) … … 510 596 511 597 if stream.state in (CONN_RESET, CONN_CLOSE, CONN_TIMED_OUT): 512 513 del self.sessions[hash(stream)] 514 515 return INJ_COLLECT_DATA 598 del self.sessions[stream] 599 600 return REAS_COLLECT_DATA 516 601 517 602 518 603 __plugins__ = [HTTPDissector] 519 __plugins_deps__ = [('HTTPDissector', ['TCPDecoder'], [ ], [])]604 __plugins_deps__ = [('HTTPDissector', ['TCPDecoder'], ['=HTTPDissector-1.0'], [])] 520 605 521 606 __audit_type__ = 0
