Exemplo n.º 1
0
class Ability(ns.ThreadedAbilityBase):
    _option_list = [
        ns.NICOpt(ns.OptNames.INPUT_INTERFACE,
                  default=None,
                  comment='Input interface', optional=True),
        ns.NICOpt(ns.OptNames.OUTPUT_INTERFACE,
                  default=None, comment='Output interface', optional=True),
        ns.MacOpt(ns.OptNames.MAC_SRC,
                  default=None, comment='Source Mac', optional=True),
        ns.MacOpt(ns.OptNames.MAC_DST,
                  default=None, comment='Destination Mac', optional=True),
        ns.IpOpt(ns.OptNames.IP_SRC,
                 default=None, comment='Source IP', optional=True),
        ns.IpOpt(ns.OptNames.IP_DST,
                 default=None, comment='Destination IP', optional=True),
        ns.PortOpt(ns.OptNames.PORT_SRC,
                   default=None, comment='Source Port', optional=True),
        ns.PortOpt(ns.OptNames.PORT_DST,
                   default=None, comment='Destination Port', optional=True),
        ns.ChoiceOpt(ns.OptNames.L4PROTOCOL, ['tcp', 'udp'],
                     comment='L4 Protocol over IP', optional=True),
    ]

    _info = ns.AbilityInfo(
        name='Netfilter Config',
        description='Configure Ebtables and IPtables rules to drop '
                    'specified traffic',
        authors=['Florian Maury', ],
        tags=[ns.Tag.TCP_STACK_L2, ns.Tag.TCP_STACK_L3],
        type=ns.AbilityType.COMPONENT
    )

    @classmethod
    def check_preconditions(cls, module_factory):
        l_dep = []
        if not ns.HAS_IPTC and not ns.HAS_IPTABLES:
            l_dep.append(
                'IPTC support missing or broken and IPtables CLI missing too. '
                'Please install python-iptables, install iptables or proceed '
                'to an update.')
        l_dep += super(Ability, cls).check_preconditions(module_factory)
        return l_dep

    def _configure_firewall_rules(self, iface, oface, mac_src, mac_dst,
                                  ip_src, ip_dst, proto, port_src, port_dst):
        """ Sets the firewall rules to drop traffic that is intercepted!

        :param mac_src: Source MAC address (may be None)
        :param mac_dst: Destination MAC address (may be None)
        :param ip_src: Source IP address (may be None)
        :param ip_dst: Destination IP address (may be None)
        :param proto: Protocol (either "udp" or "tcp" or None)
        :param port_src: Source Port (may be None)
        :param port_dst: Destination Port (may be None)
        :return: the BPF expression as a string
        """
        if not isinstance(mac_src, type(None))\
                or not isinstance(mac_dst, type(None)):
            ns.drop_frames(iface, oface, mac_src, mac_dst)

        if (
            not isinstance(ip_src, type(None))
            or not isinstance(ip_dst, type(None))
            or not isinstance(proto, type(None))
            or not isinstance(port_src, type(None))
            or not isinstance(port_dst, type(None))
        ):
            ns.drop_packets(iface, oface, ip_src, ip_dst, proto,
                            port_src, port_dst, bridge=True)

    def _unconfigure_firewall_rules(self, iface, oface, mac_src, mac_dst,
                                    ip_src, ip_dst, proto, port_src, port_dst):
        """ Sets the firewall rules to drop traffic that is intercepted!

        :param mac_src: Source MAC address (may be None)
        :param mac_dst: Destination MAC address (may be None)
        :param ip_src: Source IP address (may be None)
        :param ip_dst: Destination IP address (may be None)
        :param proto: Protocol (either "udp" or "tcp" or None)
        :param port_src: Source Port (may be None)
        :param port_dst: Destination Port (may be None)
        :return: the BPF expression as a string
        """
        if not isinstance(mac_src, type(None)) \
                or not isinstance(mac_dst, type(None)):
            ns.undrop_frames(iface, oface, mac_src, mac_dst)

        if (
            not isinstance(ip_src, type(None))
            or not isinstance(ip_dst, type(None))
            or not isinstance(proto, type(None))
            or not isinstance(port_src, type(None))
            or not isinstance(port_dst, type(None))
        ):
            ns.undrop_packets(iface, oface, ip_src, ip_dst, proto,
                              port_src, port_dst, bridge=True)

    def main(self):
        self._configure_firewall_rules(
            self.interface, self.outerface,
            self.mac_src, self.mac_dst,
            self.ip_src, self.ip_dst,
            self.protocol,
            self.port_src, self.port_dst,
        )

        self._wait()

        self._unconfigure_firewall_rules(
            self.interface, self.outerface,
            self.mac_src, self.mac_dst,
            self.ip_src, self.ip_dst,
            self.protocol,
            self.port_src, self.port_dst,
        )
Exemplo n.º 2
0
class Ability(ns.ThreadedAbilityBase):
    _option_list = [
        ns.NICOpt(ns.OptNames.INPUT_INTERFACE,
                  default=None,
                  comment='Sniffed interface'),
        ns.NICOpt(ns.OptNames.OUTPUT_INTERFACE,
                  default=None,
                  comment='Injection interface',
                  optional=True),
        ns.MacOpt(ns.OptNames.MAC_SRC,
                  default=None,
                  comment='Source Mac',
                  optional=True),
        ns.MacOpt(ns.OptNames.MAC_DST,
                  default=None,
                  comment='Destination Mac',
                  optional=True),
        ns.IpOpt(ns.OptNames.IP_SRC,
                 default=None,
                 comment='Source IP',
                 optional=True),
        ns.IpOpt(ns.OptNames.IP_DST,
                 default=None,
                 comment='Destination IP',
                 optional=True),
        ns.PortOpt(ns.OptNames.PORT_SRC,
                   default=None,
                   comment='Source Port',
                   optional=True),
        ns.PortOpt(ns.OptNames.PORT_DST,
                   default=None,
                   comment='Destination Port',
                   optional=True),
        ns.OptionTemplateEntry(
            lambda x: 0 == len(
                [e for e in x.lower() if e not in "0123456789abcdef"]),
            ns.StrOpt('ether_type',
                      default='0800',
                      comment='Filter by ether_type (hexa)',
                      optional=True)),
        ns.ChoiceOpt(ns.OptNames.L4PROTOCOL, ['tcp', 'udp'],
                     comment='L4 Protocol over IP',
                     optional=True),
        ns.StrOpt('bridge',
                  default=None,
                  comment="""Specify the bridge to use for sniffing.
            If the bridge does not exist, it will be created
            and the input and output interfaces will be bridged together.""",
                  optional=True),
        ns.BoolOpt('mux',
                   default=False,
                   comment="""True if messages to send are prefixed with
          either \\x00 or \\xFF.
        If a prefix is used, \\x00 means the message is to be sent through the
        sniffing interface (supposedly back to the sender, but who knows?!).
        If the prefix values \\xFF, then the message is sent through
        the output interface.
        If no prefix are used and this option values False,
        then messages are always sent through the output interface.
        """),
        ns.BoolOpt('bidirectional',
                   default=False,
                   comment='Whether communications must be intercepted '
                   'in one way or both ways.'),
        ns.BoolOpt('quiet', default=True, comment='Whether to log errors.'),
    ]

    _info = ns.AbilityInfo(
        name='Message Interceptor',
        description=""" This module sniffs some frames and reports them in
            the in_pkt channel.
            Original frames might be dropped and new frames can be
            injected back in.
            If an outerface is specified, the interface and the outerface
            are bridged together and intercepted frames are dropped.""",
        authors=[
            'Florian Maury',
        ],
        tags=[
            ns.Tag.INTRUSIVE,
        ],
        type=ns.AbilityType.COMPONENT)

    _dependencies = ['netfilter', 'capture', 'sendraw', 'demux']

    @classmethod
    def check_preconditions(cls, module_factory):
        l_dep = []
        if not ns.HAS_PYROUTE2:
            l_dep.append('PyRoute2 support missing or broken. '
                         'Please install pyroute2 or proceed to an update.')
        l_dep += super(Ability, cls).check_preconditions(module_factory)
        return l_dep

    def _check_parameter_consistency(self):
        """
        Check whether all provided parameters are sensible, including whether
        related parameters have consistent values

        :return: bool, True if parameter values are consistent
        """
        if (self.port_src is not None or self.port_dst is not None) \
                and self.protocol is None:
            self._view.error('If src port or dst port are defined, '
                             'a protocol must be specified.')
            return False

        if self.outerface is None and self.mux is True:
            self._view.error('Message are supposed to be prefixed, '
                             'but output interface is unspecified!?')
            return False

        if self.interface is None:
            self._view.error('An input channel must be defined.')
            return False

        if self.interface is not None \
                and self.outerface is not None \
                and self.interface == self.outerface:
            self._view.error(
                'Input interface and output interface cannot be the same. '
                'If you are sniffing and T-mode and you want to inject traffic'
                ' back, please instanciate your own send_packet ability')
            return False

        br_name = ns.in_bridge(self.interface)
        if (br_name is not None and self.bridge is not None
                and br_name != self.bridge):
            self._view.error(
                'Input interface is already in a different bridge. '
                'You might be breaking something here :)')
            return False

        if ns.is_bridge(self.interface):
            self._view.error('A bridge cannot be enslaved to another bridge. '
                             'Input interface is a bridge.')
            return False

        if self.outerface is not None and ns.is_bridge(self.outerface):
            self._view.error('A bridge cannot be enslaved to another bridge. '
                             'Output interface is a bridge.')
            return False

        return True

    def _build_bpf(self, mac_src, mac_dst, ether_type, ip_src, ip_dst, proto,
                   port_src, port_dst):
        """ Builds a BPF from the provided parameters
        :param mac_src: Source MAC address (may be None)
        :param mac_dst: Destination MAC address (may be None)
        :param ip_src: Source IP address (may be None)
        :param ip_dst: Destination IP address (may be None)
        :param proto: Protocol (either "udp" or "tcp" or None)
        :param port_src: Source Port (may be None)
        :param port_dst: Destination Port (may be None)
        :param bidirectional: Bool telling whether the connection must be
            extracted in one way or in both ways
        :return: the BPF expression as a string
        """
        bpf = set()
        bpf.add('ether proto 0x{}'.format(ether_type))

        if self.bidirectional:
            if mac_src is not None and mac_dst is not None:
                bpf.add('(ether src {} and ether dst {}) '
                        'or (ether src {} and ether dst {})'.format(
                            mac_src, mac_dst, mac_dst, mac_src))
            elif mac_src is not None and mac_dst is None:
                bpf.add('ether {}'.format(mac_src))
            elif mac_dst is not None and mac_src is None:
                bpf.add('ether {}'.format(mac_src))
            if ip_src is not None and ip_dst is not None:
                bpf.add('(src host {} and dst host {}) '
                        'or (src host {} and dst host {})'.format(
                            ip_src, ip_dst, ip_dst, ip_src))
            elif ip_src is not None and ip_dst is None:
                bpf.add('host {}'.format(ip_src))
            elif ip_dst is not None and ip_src is None:
                bpf.add('host {}'.format(ip_dst))
            if proto is not None:
                bpf.add(proto)
            if port_src is not None and port_dst is not None:
                bpf.add('(src port {} and dst port {}) '
                        'or (src port {} and dst port {})'.format(
                            port_src, port_dst, port_dst, port_src))
            elif port_src is not None and port_dst is None:
                bpf.add('port {}'.format(port_src))
            elif port_dst is not None and port_src is None:
                bpf.add('port {}'.format(port_dst))
        else:
            if not isinstance(mac_src, type(None)):
                bpf.add('ether src {}'.format(mac_src))
            if not isinstance(mac_dst, type(None)):
                bpf.add('ether dst {}'.format(mac_dst))
            if not isinstance(ip_src, type(None)):
                bpf.add('src host {}'.format(ip_src))
                bpf.add('ip or ip6')
            if not isinstance(ip_dst, type(None)):
                bpf.add('dst host {}'.format(ip_dst))
                bpf.add('ip or ip6')
            if not isinstance(proto, type(None)):
                bpf.add(proto)
            if not isinstance(port_src, type(None)):
                bpf.add('src port {}'.format(port_src))
            if not isinstance(port_dst, type(None)):
                bpf.add('dst port {}'.format(port_dst))
        return '({})'.format(') and ('.join(list(bpf)))

    def main(self):
        if not self._check_parameter_consistency():
            self._view.warning('Inconsistent parameters')
            return

        bpf_expr = self._build_bpf(self.mac_src, self.mac_dst, self.ether_type,
                                   self.ip_src, self.ip_dst, self.protocol,
                                   self.port_src, self.port_dst)

        if self.outerface is not None:
            # Bridge only the output NIC at the moment,
            # to create the bridge but not let the traffic go through
            bridge_name = ns.bridge_iface_together(self.outerface,
                                                   bridge=self.bridge)

            # Configure the firewall to drop relevant frames/packets
            fw_abl = self.get_dependency('netfilter',
                                         interface=self.interface,
                                         outerface=self.outerface,
                                         mac_src=self.mac_src,
                                         mac_dst=self.mac_dst,
                                         ip_src=self.ip_src,
                                         ip_dst=self.ip_dst,
                                         protocol=self.protocol,
                                         port_src=self.port_src,
                                         port_dst=self.port_dst)
            fw_abl.start()

            # Configure the sniffing ability
            sniff_abl = self.get_dependency('capture',
                                            bpf=bpf_expr,
                                            interface=bridge_name)
            self._transfer_out(sniff_abl)
            sniff_abl.start()

            # Configure the sending ability, if a pipe is provided
            was_source = self._is_source()
            if not was_source:
                if self.mux is True:
                    out1, in1 = multiprocessing.Pipe()
                    send_raw_abl1 = self.get_dependency(
                        'sendraw', outerface=self.interface)
                    send_raw_abl1.add_in_pipe(in1)
                    send_raw_abl1.start()

                    out2, in2 = multiprocessing.Pipe()
                    send_raw_abl2 = self.get_dependency(
                        'sendraw', outerface=self.outerface)
                    send_raw_abl2.add_in_pipe(in2)
                    send_raw_abl2.start()

                    demux_abl = self.get_dependency('demux')
                    self._transfer_in(demux_abl)
                    demux_abl.start(demux={
                        '\x00': out1,
                        '\xFF': out2
                    },
                                    quiet=self.quiet,
                                    deepcopy=False)
                else:
                    send_raw_abl = self.get_dependency(
                        'sendraw', outerface=self.outerface)
                    self._transfer_in(send_raw_abl)
                    send_raw_abl.start()
            else:
                send_raw_abl = None

            # Finally adds the input NIC to the bridge, now that relevant
            # packets are dropped, to let through all irrelevant packets
            ns.bridge_iface_together(self.interface, bridge=bridge_name)

            # Wait for the stop event
            self._wait()

            # Stopping Ability
            sniff_abl.stop()
            sniff_abl.join()

            if not was_source:
                if self.mux is True:
                    demux_abl.stop()
                    send_raw_abl1.stop()
                    send_raw_abl2.stop()
                    demux_abl.join()
                    send_raw_abl1.join()
                    send_raw_abl2.join()
                else:
                    send_raw_abl.stop()
                    send_raw_abl.join()

            fw_abl.stop()
            fw_abl.join()

            ns.unbridge(bridge_name)

        else:  # We are only acting on a single interface
            # Configure the sniffing ability
            sniff_abl = self.get_dependency('capture',
                                            bpf=bpf_expr,
                                            interface=self.interface)
            self._transfer_out(sniff_abl)
            sniff_abl.start()

            was_source = self._is_source()
            if not was_source:
                send_raw_abl = self.get_dependency('sendraw',
                                                   outerface=self.interface)
                self._transfer_in(send_raw_abl)
                send_raw_abl.start()

            # Wait for the stop event
            self._wait()

            # Stopping Ability
            sniff_abl.stop()
            sniff_abl.join()

            if not was_source:
                send_raw_abl.stop()
                send_raw_abl.join()
Exemplo n.º 3
0
class Ability(ns.AbilityBase):
    _info = ns.AbilityInfo(
        name='Demo options',
        description='Demonstrate all available options',
        tags=[ns.Tag.EXAMPLE],
    )

    _option_list = [
        ns.ChoiceOpt('option', ['normal', 'bypass_cache'],
                     default='normal',
                     comment='Define if cache must be bypassed '
                     'when using generators (except "nb")'),
        ns.NumOpt('nb', default=3, comment='Times to display everything'),
        ns.IpOpt(ns.OptNames.IP_DST,
                 default='127.0.0.1',
                 comment='use as default the standardized dst_ip option name'),
        ns.StrOpt('msg', default='my message', comment='A string message'),
        ns.PortOpt(ns.OptNames.PORT_DST,
                   default=2222,
                   comment='A string message'),
        ns.MacOpt(ns.OptNames.MAC_SRC,
                  default='Mac00',
                  comment='Source MAC address'),
        ns.BoolOpt('a_bool', default=True, comment='A True/False value'),
        ns.PathOpt('path',
                   default='pw.ini',
                   comment='Path to an existing file')  # must_exist=True),
    ]

    def display(self):
        for i in range(self.nb):
            self._view.delimiter('Round {}'.format(i + 1))
            self._view.info('[{}] - {} - {}'.format(self.mac_src, self.ip_dst,
                                                    self.port_dst))
            self._view.progress('{}'.format(self.msg))
            self._view.debug('{}'.format(self.a_bool))
            self._view.warning('{} (abs: {})'.format(
                self.path, os.path.abspath(self.path)))
            self._view.delimiter()
            self._view.info('')

    def display_bypass_cache(self):
        for i in range(self.nb):
            self._view.delimiter('Round {}'.format(i + 1))
            self._view.info('[{}] - {} - {}'.format(
                self.get_opt('mac_src', bypass_cache=True),
                self.get_opt('ip_dst', bypass_cache=True),
                self.get_opt('port_dst', bypass_cache=True),
            ))
            self._view.progress('{}'.format(
                self.get_opt('msg', bypass_cache=True)))
            self._view.debug('{}'.format(
                self.get_opt('a_bool', bypass_cache=True)))
            self._view.warning('{} (abs: {})'.format(
                self.get_opt('path', bypass_cache=True),
                os.path.abspath(self.get_opt('path', bypass_cache=True))))
            self._view.delimiter()
            self._view.info('')

    def main(self):
        if self.nb <= 0:
            self._view.error(
                'The number must be greater than 0 ({} given)'.format(self.nb))
            return
        elif self.nb > 2000:
            self._view.warning('{} rounds is quite a lot! '
                               'Please try with a lower number.'.format(
                                   self.nb))
            return

        if self.option == 'normal':
            self.display()
        elif self.option == 'bypass_cache':
            self.display_bypass_cache()

        self._view.success('Done!')
        return 'Done'

    def howto(self):
        self._view.delimiter('Module option demonstration')
        self._view.info("""
        This ability make use of all the PacketWeaver framework supported
        options.

        Their names are either specified using a label, or a predefined value
        using a OptNames.VAL . The latter solution is preferred as it helps
        getting a clean input interface across different abilities.

        You may play with the different options, modifying their value with
        either:
        - a fixed value
        - a fixed value randomly drawn (e.g RandIP4() for the dst_ip)
        - a random generator (e.g RandIP4)

        The ability will display their value three times so you can see how
        they behave.
        """)