Exemplo n.º 1
0
class TCPDecoder(Plugin, PassiveAudit):
    def start(self, reader):
        self.checksum_check = True
        self.reassembler = None
        self.manager = None

    def stop(self):
        manager = AuditManager()
        manager.remove_decoder(PROTO_LAYER, NL_TYPE_TCP, self._process_tcp)
        manager.remove_injector(1, NL_TYPE_TCP, self._inject_tcp)

        try:
            manager.remove_decoder_hook(PROTO_LAYER, NL_TYPE_ICMP,
                                             self.reassembler.process_icmp, 1)
        except:
            pass

    def register_decoders(self):
        self.manager = AuditManager()
        conf = self.manager.get_configuration('decoder.tcp')

        self.checksum_check = conf['checksum_check']

        self.manager.add_decoder(PROTO_LAYER, NL_TYPE_TCP, self._process_tcp)
        self.manager.add_injector(1, NL_TYPE_TCP, self._inject_tcp)

        if conf['enable_reassemble']:
            self.reassembler = Reassembler(conf['reassemble_workarounds'],
                                           conf['reassemble_maxstreams'])
            self.manager.add_decoder_hook(PROTO_LAYER, NL_TYPE_ICMP,
                                          self.reassembler.process_icmp, 1)

    def _inject_tcp(self, context, mpkt, length):
        """
        Function that manages injection of fragments in active TCP connection
        """

        ident = TCPIdent.create(mpkt)
        sess = SessionManager().get_session(ident)

        if not sess:
            log.debug("No TCP session found.")
            return False, length

        if ident.l3_src == sess.ident.l3_src:
            status = sess.data[1]
            ostatus = sess.data[0]
        else:
            status = sess.data[0]
            ostatus = sess.data[1]

        mpkt.set_fields('tcp', {
            'sport' : mpkt.l4_src,
            'dport' : mpkt.l4_dst,
            'dataofs' : 5,
            'chksum' : None,
            'urgptr' : 0,
            'flags' : TH_PSH,
            'options' : {}})

        if status.injectable & INJ_FIN or \
           not status.injectable & INJ_FWD or \
           not ostatus.injectable & INJ_FWD:
            log.debug("Session is not injectable.")
            return False, length

        mpkt.set_fields('tcp', {
            'seq' : status.last_seq + status.seq_adj,
            'ack' : status.last_ack - ostatus.seq_adj})

        if status.last_ack != 0:
            mpkt.set_field('tcp.flags', mpkt.l4_flags | TH_ACK)

        mpkt.session = sess.prev
        length += 20 + mpkt.l2_len

        injector = AuditManager().get_injector(0, mpkt.session.ident.magic)
        is_ok, length = injector(context, mpkt, length)

        if not is_ok:
            return is_ok, length

        length = context.get_mtu() - length

        if length > mpkt.inject_len:
            length = mpkt.inject_len

        payload = mpkt.inject[:length]
        payload_pkt = MetaPacket.new('raw')
        payload_pkt.set_field('raw.load', payload)
        mpkt.add_to('tcp', payload_pkt)

        status.seq_adj += length
        mpkt.data_len = length

        return True, length

    def _process_tcp(self, mpkt):
        mpkt.l4_src, \
        mpkt.l4_dst, \
        mpkt.l4_ack, \
        mpkt.l4_seq, \
        mpkt.l4_flags = mpkt.get_fields('tcp', ('sport', 'dport', 'ack', \
                                                'seq', 'flags'))
        mpkt.l4_len = mpkt.get_field('tcp.dataofs', 5) * 4

        if mpkt.l4_src is None:
            return None

        tcpraw = mpkt.get_field('tcp')

        if tcpraw:
            mpkt.data_len = mpkt.payload_len - mpkt.l4_len
            mpkt.data = tcpraw[mpkt.l4_len:]

        wrong = False

        if self.checksum_check and tcpraw:
            if mpkt.payload_len == len(tcpraw):
                ip_src = mpkt.l3_src
                ip_dst = mpkt.l3_dst

                psdhdr = pack("!4s4sHH",
                              inet_aton(ip_src),
                              inet_aton(ip_dst),
                              mpkt.l4_proto,
                              mpkt.payload_len)

                chksum = checksum(psdhdr + tcpraw[:16] + \
                                  "\x00\x00" + tcpraw[18:])

                if mpkt.get_field('tcp.chksum', 0) != chksum:
                    wrong = True
                    mpkt.set_cfield('good_checksum', hex(chksum))
                    self.manager.user_msg(
                                _("Invalid TCP packet from %s to %s : " \
                                  "wrong checksum %s instead of %s") %  \
                                (ip_src, ip_dst,                        \
                                 hex(mpkt.get_field('tcp.chksum', 0)),  \
                                 hex(chksum)),
                                5, 'decoder.tcp')

        if wrong:
            self.manager.run_decoder(APP_LAYER, PL_DEFAULT, mpkt)
            return None

        if self.reassembler:
            self.reassembler.process_tcp(mpkt)

        ident = TCPIdent.create(mpkt)
        sess = SessionManager().get_session(ident)

        if not sess:
            sess = Session(ident)
            sess.data = (TCPStatus(), TCPStatus())
            SessionManager().put_session(sess)

        sess.prev = mpkt.session
        mpkt.session = sess

        if ident.l3_src == sess.ident.l3_src:
            status = sess.data[1]
            ostatus = sess.data[0]
        else:
            status = sess.data[0]
            ostatus = sess.data[1]

        status.last_seq = mpkt.l4_seq + mpkt.data_len

        if mpkt.l4_flags & TH_ACK:
            status.last_ack = mpkt.l4_ack

        if mpkt.l4_flags & TH_SYN:
            status.last_seq += 1

        if mpkt.l4_flags & TH_RST:
            status.injectable |= INJ_FIN
            ostatus.injectable |= INJ_FIN

        if mpkt.flags & MPKT_FORWARDABLE:
            status.injectable |= INJ_FWD
        elif status.injectable & INJ_FWD:
            status.injectable ^= INJ_FWD

        self.manager.run_decoder(APP_LAYER, PL_DEFAULT, mpkt)

        if mpkt.l4_flags & TH_FIN:
            status.injectable |= INJ_FIN

        if mpkt.flags & MPKT_DROPPED and mpkt.flags & MPKT_FORWARDABLE:
            status.seq_adj += mpkt.inj_delta
        elif (mpkt.flags & MPKT_MODIFIED or \
             status.seq_adj != 0 or ostatus != 0) and \
             mpkt.flags & MPKT_FORWARDABLE:

            mpkt.set_field('tcp.seq', mpkt.l4_seq + status.seq_adj)
            mpkt.set_field('tcp.ack', mpkt.l4_ack - ostatus.seq_adj)

            status.seq_adj += mpkt.inj_delta

            mpkt.set_field('tcp.chksum', None)

        return None