Changeset 5543

Show
Ignore:
Timestamp:
02/18/10 12:40:31 (5 months ago)
Author:
nopper
Message:

Fixing HTTP dissector. Now username/password info will be printed with user_msg

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • pm/trunk/audits/passive/http/sources/main.py

    r5449 r5543  
    2323 
    2424This 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') 
     27dissector.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 
    2528""" 
    2629 
     
    4649NTLM_WAIT_RESPONSE = 0 
    4750 
    48 FORM_USERNAME = HTTP_REQUEST  = 0 
    49 FORM_PASSWORD = HTTP_RESPONSE = 1 
     51FORM_USERNAME = 0 
     52FORM_PASSWORD = 1 
     53 
     54HTTP_REQUEST  = 0 
     55HTTP_RESPONSE = 1 
    5056 
    5157g_fields = None 
    5258 
    5359def form_extract(data, type=FORM_USERNAME): 
     60    global g_fields 
     61 
    5462    if not g_fields: 
    5563        return None 
     
    6472 
    6573class HTTPRequest(object): 
    66     http_type = HTTP_REQUEST 
    67  
    68     def __init__(self, sess): 
     74    def __init__(self, sess, manager): 
    6975        self.headers_complete = False 
    7076        self.content_length = -1 
     
    7581 
    7682        self.session = sess 
     83        self.manager = manager 
     84        self.http_type = HTTP_REQUEST 
    7785 
    7886    def feed(self, mpkt, data): 
     
    8593 
    8694            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 
    106124 
    107125                self.analyze_headers(mpkt) 
    108                 return True, end_ptr 
     126                self.manager.run_hook_point('http', mpkt) 
     127 
     128                return finished, end_ptr 
    109129 
    110130            return False, end_ptr 
     
    121141                    mpkt.set_cfield(HTTP_NAME + '.response', self.body) 
    122142 
     143                self.manager.run_hook_point('http', mpkt) 
     144 
    123145            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 
    124158 
    125159    def _parse_headers(self, mpkt, payload): 
     
    133167 
    134168            if last == 0: 
    135                 return 3 
     169                return 2 
    136170            if last == -1: 
    137171                return 0 
     
    143177                break 
    144178 
    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 += ':' 
    146186 
    147187            if key[-1] == ':': 
     
    158198                    value = int(value) 
    159199                    self.content_length = value 
    160  
    161200                except ValueError: 
    162201                    pass 
     
    169208                mpkt.set_cfield(HTTP_NAME + '.response_status', value) 
    170209 
    171             elif key == 'authorization': 
     210            elif key == 'authorization' or \ 
     211                 key == 'www-authenticate': 
    172212                if value[0:9].upper() == 'PASSPORT ': 
    173213                    self._parse_passport(mpkt, value[9:]) 
     
    178218                elif value[0:7].upper() == 'DIGEST ': 
    179219                    self._parse_digest(mpkt, value[7:]) 
    180  
    181             elif key == 'www-authenticate': 
    182                 if value[0:5] == 'NTLM ': 
     220                elif value[0:5] == 'NTLM ': 
    183221                    self._parse_ntlm(mpkt, value[5:]) 
    184222 
    185223            self.headers[key].append(value) 
     224 
    186225 
    187226        if self.headers_complete: 
     
    191230 
    192231    def analyze_headers(self, mpkt): 
     232        if not self.headers: 
     233            return 
     234 
    193235        mpkt.set_cfield(HTTP_NAME + '.headers', self.headers) 
    194236 
     
    232274            mpkt.set_cfield('password', password) 
    233275 
     276            self.report(mpkt, 'GET', username, password) 
     277 
    234278    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 
    235287        if not 'post' in self.headers: 
    236288            return 
     
    242294            mpkt.set_cfield('username', username) 
    243295            mpkt.set_cfield('password', password) 
     296 
     297            self.report(mpkt, 'POST', username, password) 
    244298 
    245299    def _parse_passport(self, mpkt, val): 
     
    269323        finally: 
    270324            if found: 
     325                password = ', '.join(values) 
     326 
    271327                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) 
    273331 
    274332    def _parse_ntlm(self, mpkt, val): 
     
    310368                mpkt.set_cfield('password', password) 
    311369 
     370                self.report(mpkt, 'NTLM', username, password) 
     371 
    312372                self.session.data = None 
    313373 
    314     def _parse_basic(self, val): 
     374    def _parse_basic(self, mpkt, val): 
    315375        val = b64decode(val) 
    316376        ret = val.split(':', 1) 
    317377 
    318378        if isinstance(ret, tuple) and len(ret) == 2: 
    319             mpkt.set_cfield('user', ret[0]) 
     379            mpkt.set_cfield('username', ret[0]) 
    320380            mpkt.set_cfield('password', ret[1]) 
     381 
     382            self.report(mpkt, 'BASIC', ret[0], ret[1]) 
    321383 
    322384    def _parse_body(self, payload, end_ptr=0): 
     
    328390                idx = payload.find('\r\n') 
    329391 
    330                 if not idx: 
     392                if idx == 0: 
     393                    idx = payload.find('\r\n', 2) 
     394 
     395                if idx < 0: 
    331396                    return False, end_ptr 
    332397 
     
    335400 
    336401                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)) 
    338403                    return True, idx 
    339404 
     
    383448            return True, len(payload) + end_ptr 
    384449 
     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 
    385458class 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 
    387463 
    388464class 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) 
    392469 
    393470        self.requests = [self.request] 
     
    409486            self.req_last_len += idx 
    410487 
    411             if ret: 
    412                 self.request = HTTPRequest(self) 
     488            if ret == True: 
     489                self.request = HTTPRequest(self, self.manager) 
    413490                self.requests.append(self.request) 
    414491 
     
    423500            self.res_last_len += idx 
    424501 
    425             if ret is True: 
    426                 self.response = HTTPResponse(self) 
     502            if ret == True: 
     503                self.response = HTTPResponse(self, self.manager) 
    427504                self.responses.append(self.response) 
     505 
    428506 
    429507class HTTPDissector(Plugin, PassiveAudit): 
    430508    def start(self, reader): 
    431509        self.sessions = {} 
    432  
    433         conf = AuditManager().get_configuration(HTTP_NAME) 
     510        self.manager = AuditManager() 
     511 
     512        conf = self.manager.get_configuration(HTTP_NAME) 
    434513 
    435514        self.reassemble = conf['reassemble'] 
     
    453532        global g_fields 
    454533 
    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        ) 
    457538 
    458539    def stop(self): 
    459         conf = AuditManager().get_configuration(HTTP_NAME) 
     540        conf = self.manager.get_configuration(HTTP_NAME) 
    460541 
    461542        if not self.reassemble: 
    462543            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) 
    465546        else: 
    466547            self.tcpdecoder.remove_analyzer(self._tcp_callback) 
    467548 
     549        self.manager.deregister_hook_point('http') 
     550 
    468551    def register_decoders(self): 
    469         if self.reassemble: 
     552        self.manager.register_hook_point('http') 
     553 
     554        if not self.reassemble: 
    470555            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) 
    473558 
    474559    def _http_decoder(self, mpkt): 
    475         payload = mpkt.get_field('raw.load') 
     560        payload = mpkt.data 
    476561 
    477562        if not payload: 
     
    479564 
    480565        try: 
    481             obj = HTTPRequest(None) 
     566            obj = HTTPRequest(None, self.manager) 
    482567            obj.feed(mpkt, payload) 
    483568 
     
    492577                obj.http_type = HTTP_RESPONSE 
    493578                obj.analyze_headers(mpkt) 
    494         except: 
     579        except Exception, exc: 
    495580            pass 
    496581 
    497582    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: 
    499585            stream.listeners.append(self._process_http) 
    500586 
    501587    def _process_http(self, stream, mpkt, rcv): 
    502         if hash(stream) not in self.sessions: 
    503             sess = HTTPSession() 
    504             self.sessions[hash(stream)] = sess 
     588        if stream not in self.sessions: 
     589            sess = HTTPSession(self.manager) 
     590            self.sessions[stream] = sess 
    505591        else: 
    506             sess = self.sessions[hash(stream)] 
     592            sess = self.sessions[stream] 
    507593 
    508594        sess.feed_response(stream.client, mpkt) 
     
    510596 
    511597        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 
    516601 
    517602 
    518603__plugins__ = [HTTPDissector] 
    519 __plugins_deps__ = [('HTTPDissector', ['TCPDecoder'], [], [])] 
     604__plugins_deps__ = [('HTTPDissector', ['TCPDecoder'], ['=HTTPDissector-1.0'], [])] 
    520605 
    521606__audit_type__ = 0