Exemplo n.º 1
0
 def randval(self):
     return RandString(RandNum(0, 1000))
Exemplo n.º 2
0
def p0f_impersonate(pkt,
                    osgenre=None,
                    osdetails=None,
                    signature=None,
                    extrahops=0,
                    mtu=1500,
                    uptime=None):
    """Modifies pkt so that p0f will think it has been sent by a
specific OS.  If osdetails is None, then we randomly pick up a
personality matching osgenre. If osgenre and signature are also None,
we use a local signature (using p0f_getlocalsigs). If signature is
specified (as a tuple), we use the signature.

For now, only TCP Syn packets are supported.
Some specifications of the p0f.fp file are not (yet) implemented."""
    pkt = pkt.copy()
    # pkt = pkt.__class__(raw(pkt))
    while pkt.haslayer(IP) and pkt.haslayer(TCP):
        pkt = pkt.getlayer(IP)
        if isinstance(pkt.payload, TCP):
            break
        pkt = pkt.payload

    if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP):
        raise TypeError("Not a TCP/IP packet")

    db = p0f_selectdb(pkt.payload.flags)
    if osgenre:
        pb = db.get_base()
        if pb is None:
            pb = []
        pb = [x for x in pb if x[6] == osgenre]
        if osdetails:
            pb = [x for x in pb if x[7] == osdetails]
    elif signature:
        pb = [signature]
    else:
        pb = p0f_getlocalsigs()[db]
    if db == p0fr_kdb:
        # 'K' quirk <=> RST+ACK
        if pkt.payload.flags & 0x4 == 0x4:
            pb = [x for x in pb if 'K' in x[5]]
        else:
            pb = [x for x in pb if 'K' not in x[5]]
    if not pb:
        raise Scapy_Exception("No match in the p0f database")
    pers = pb[random.randint(0, len(pb) - 1)]

    # options (we start with options because of MSS)
    # Take the options already set as "hints" to use in the new packet if we
    # can. MSS, WScale and Timestamp can all be wildcarded in a signature, so
    # we'll use the already-set values if they're valid integers.
    orig_opts = dict(pkt.payload.options)
    int_only = lambda val: val if isinstance(val, six.integer_types) else None
    mss_hint = int_only(orig_opts.get('MSS'))
    wscale_hint = int_only(orig_opts.get('WScale'))
    ts_hint = [int_only(o) for o in orig_opts.get('Timestamp', (None, None))]

    options = []
    if pers[4] != '.':
        for opt in pers[4].split(','):
            if opt[0] == 'M':
                # MSS might have a maximum size because of window size
                # specification
                if pers[0][0] == 'S':
                    maxmss = (2**16 - 1) // int(pers[0][1:])
                else:
                    maxmss = (2**16 - 1)
                # disregard hint if out of range
                if mss_hint and not 0 <= mss_hint <= maxmss:
                    mss_hint = None
                # If we have to randomly pick up a value, we cannot use
                # scapy RandXXX() functions, because the value has to be
                # set in case we need it for the window size value. That's
                # why we use random.randint()
                if opt[1:] == '*':
                    if mss_hint is not None:
                        options.append(('MSS', mss_hint))
                    else:
                        options.append(('MSS', random.randint(1, maxmss)))
                elif opt[1] == '%':
                    coef = int(opt[2:])
                    if mss_hint is not None and mss_hint % coef == 0:
                        options.append(('MSS', mss_hint))
                    else:
                        options.append(
                            ('MSS', coef * random.randint(1, maxmss // coef)))
                else:
                    options.append(('MSS', int(opt[1:])))
            elif opt[0] == 'W':
                if wscale_hint and not 0 <= wscale_hint < 2**8:
                    wscale_hint = None
                if opt[1:] == '*':
                    if wscale_hint is not None:
                        options.append(('WScale', wscale_hint))
                    else:
                        options.append(('WScale', RandByte()))
                elif opt[1] == '%':
                    coef = int(opt[2:])
                    if wscale_hint is not None and wscale_hint % coef == 0:
                        options.append(('WScale', wscale_hint))
                    else:
                        options.append(('WScale', coef * RandNum(
                            min=1, max=(2**8 - 1) // coef)))  # noqa: E501
                else:
                    options.append(('WScale', int(opt[1:])))
            elif opt == 'T0':
                options.append(('Timestamp', (0, 0)))
            elif opt == 'T':
                # Determine first timestamp.
                if uptime is not None:
                    ts_a = uptime
                elif ts_hint[0] and 0 < ts_hint[0] < 2**32:
                    # Note: if first ts is 0, p0f registers it as "T0" not "T",
                    # hence we don't want to use the hint if it was 0.
                    ts_a = ts_hint[0]
                else:
                    ts_a = random.randint(120, 100 * 60 * 60 * 24 * 365)
                # Determine second timestamp.
                if 'T' not in pers[5]:
                    ts_b = 0
                elif ts_hint[1] and 0 < ts_hint[1] < 2**32:
                    ts_b = ts_hint[1]
                else:
                    # FIXME: RandInt() here does not work (bug (?) in
                    # TCPOptionsField.m2i often raises "OverflowError:
                    # long int too large to convert to int" in:
                    #    oval = struct.pack(ofmt, *oval)"
                    # Actually, this is enough to often raise the error:
                    #    struct.pack('I', RandInt())
                    ts_b = random.randint(1, 2**32 - 1)
                options.append(('Timestamp', (ts_a, ts_b)))
            elif opt == 'S':
                options.append(('SAckOK', ''))
            elif opt == 'N':
                options.append(('NOP', None))
            elif opt == 'E':
                options.append(('EOL', None))
            elif opt[0] == '?':
                if int(opt[1:]) in TCPOptions[0]:
                    optname = TCPOptions[0][int(opt[1:])][0]
                    optstruct = TCPOptions[0][int(opt[1:])][1]
                    options.append(
                        (optname,
                         struct.unpack(
                             optstruct,
                             RandString(struct.calcsize(optstruct))._fix())
                         ))  # noqa: E501
                else:
                    options.append((int(opt[1:]), ''))
            # FIXME: qqP not handled
            else:
                warning("unhandled TCP option %s", opt)
            pkt.payload.options = options

    # window size
    if pers[0] == '*':
        pkt.payload.window = RandShort()
    elif pers[0].isdigit():
        pkt.payload.window = int(pers[0])
    elif pers[0][0] == '%':
        coef = int(pers[0][1:])
        pkt.payload.window = coef * RandNum(min=1, max=(2**16 - 1) // coef)
    elif pers[0][0] == 'T':
        pkt.payload.window = mtu * int(pers[0][1:])
    elif pers[0][0] == 'S':
        # needs MSS set
        mss = [x for x in options if x[0] == 'MSS']
        if not mss:
            raise Scapy_Exception(
                "TCP window value requires MSS, and MSS option not set"
            )  # noqa: E501
        pkt.payload.window = mss[0][1] * int(pers[0][1:])
    else:
        raise Scapy_Exception('Unhandled window size specification')

    # ttl
    pkt.ttl = pers[1] - extrahops
    # DF flag
    pkt.flags |= (2 * pers[2])
    # FIXME: ss (packet size) not handled (how ? may be with D quirk
    # if present)
    # Quirks
    if pers[5] != '.':
        for qq in pers[5]:
            # FIXME: not handled: P, I, X, !
            # T handled with the Timestamp option
            if qq == 'Z':
                pkt.id = 0
            elif qq == 'U':
                pkt.payload.urgptr = RandShort()
            elif qq == 'A':
                pkt.payload.ack = RandInt()
            elif qq == 'F':
                if db == p0fo_kdb:
                    pkt.payload.flags |= 0x20  # U
                else:
                    pkt.payload.flags |= random.choice([8, 32, 40])  # P/U/PU
            elif qq == 'D' and db != p0fo_kdb:
                pkt /= conf.raw_layer(load=RandString(random.randint(
                    1, 10)))  # XXX p0fo.fp  # noqa: E501
            elif qq == 'Q':
                pkt.payload.seq = pkt.payload.ack
            # elif qq == '0': pkt.payload.seq = 0
        # if db == p0fr_kdb:
        # '0' quirk is actually not only for p0fr.fp (see
        # packet2p0f())
    if '0' in pers[5]:
        pkt.payload.seq = 0
    elif pkt.payload.seq == 0:
        pkt.payload.seq = RandInt()

    while pkt.underlayer:
        pkt = pkt.underlayer
    return pkt
Exemplo n.º 3
0
def p0f_impersonate(pkt,
                    osgenre=None,
                    osdetails=None,
                    signature=None,
                    extrahops=0,
                    mtu=1500,
                    uptime=None):
    """Modifies pkt so that p0f will think it has been sent by a
    specific OS. Either osgenre or signature is required to impersonate.
    If signature is specified (as a raw string), we use the signature.
    signature format:
        "ip_ver:ttl:ip_opt_len:mss:window,wscale:opt_layout:quirks:pay_class"

    If osgenre is specified, we randomly pick a signature with a label
    that matches osgenre (and osdetails, if specified).
    Note: osgenre is case sensitive ("linux" -> "Linux" etc.), and osdetails
    is a substring of a label flavor ("7", "8" and "7 or 8" will
    all match the label "s:win:Windows:7 or 8")

    For now, only TCP SYN/SYN+ACK packets are supported."""
    pkt = validate_packet(pkt)

    if not osgenre and not signature:
        raise ValueError("osgenre or signature is required to impersonate!")

    tcp = pkt[TCP]
    tcp_type = tcp.flags & (0x02 | 0x10)  # SYN / SYN+ACK

    if signature:
        if isinstance(signature, string_types):
            sig, _ = TCP_Signature.from_raw_sig(signature)
        else:
            raise TypeError("Unsupported signature type")
    else:
        if not p0fdb.get_base():
            sigs = []
        else:
            direction = "request" if tcp_type == 0x02 else "response"
            sigs = p0fdb.get_sigs_by_os(direction, osgenre, osdetails)

        # If IPv6 packet, remove IPv4-only signatures and vice versa
        sigs = [s for s in sigs if s.ip_ver == -1 or s.ip_ver == pkt.version]
        if not sigs:
            raise ValueError("No match in the p0f database")
        sig = random.choice(sigs)

    if sig.ip_ver != -1 and pkt.version != sig.ip_ver:
        raise ValueError("Can't convert between IPv4 and IPv6")

    quirks = sig.quirks

    if pkt.version == 4:
        pkt.ttl = sig.ttl - extrahops
        if sig.ip_opt_len != 0:
            # FIXME: Non-zero IPv4 options not handled
            warning("Unhandled IPv4 option field")
        else:
            pkt.options = []

        if "df" in quirks:
            pkt.flags |= 0x02  # set DF flag
            if "id+" in quirks:
                if pkt.id == 0:
                    pkt.id = random.randint(1, 2**16 - 1)
            else:
                pkt.id = 0
        else:
            pkt.flags &= ~(0x02)  # DF flag not set
            if "id-" in quirks:
                pkt.id = 0
            elif pkt.id == 0:
                pkt.id = random.randint(1, 2**16 - 1)
        if "ecn" in quirks:
            pkt.tos |= random.randint(0x01, 0x03)
        pkt.flags = pkt.flags | 0x04 if "0+" in quirks else pkt.flags & ~(0x04)
    else:
        pkt.hlim = sig.ttl - extrahops
        if "flow" in quirks:
            pkt.fl = random.randint(1, 2**20 - 1)
        if "ecn" in quirks:
            pkt.tc |= random.randint(0x01, 0x03)

    # Take the options already set as "hints" to use in the new packet if we
    # can. we'll use the already-set values if they're valid integers.
    def int_only(val):
        return val if isinstance(val, integer_types) else None

    orig_opts = dict(tcp.options)
    mss_hint = int_only(orig_opts.get("MSS"))
    ws_hint = int_only(orig_opts.get("WScale"))
    ts_hint = [int_only(o) for o in orig_opts.get("Timestamp", (None, None))]

    options = []
    for opt in sig.olayout.split(","):
        if opt == "mss":
            # MSS might have a maximum size because of WIN_TYPE_MSS
            if sig.win_type == WIN_TYPE_MSS:
                maxmss = (2**16 - 1) // sig.win
            else:
                maxmss = (2**16 - 1)

            if sig.mss == -1:  # wildcard mss
                if mss_hint and 0 <= mss_hint <= maxmss:
                    options.append(("MSS", mss_hint))
                else:  # invalid hint, generate new value
                    options.append(("MSS", random.randint(100, maxmss)))
            else:
                options.append(("MSS", sig.mss))

        elif opt == "ws":
            if sig.wscale == -1:  # wildcard wscale
                maxws = 2**8
                if "exws" in quirks:  # wscale > 14
                    if ws_hint and 14 < ws_hint < maxws:
                        options.append(("WScale", ws_hint))
                    else:  # invalid hint, generate new value > 14
                        options.append(
                            ("WScale",
                             random.randint(15, maxws - 1)))  # noqa: E501
                else:
                    if ws_hint and 0 <= ws_hint < maxws:
                        options.append(("WScale", ws_hint))
                    else:  # invalid hint, generate new value
                        options.append(("WScale", RandByte()))
            else:
                options.append(("WScale", sig.wscale))

        elif opt == "ts":
            ts1, ts2 = ts_hint

            if "ts1-" in quirks:  # own timestamp specified as zero
                ts1 = 0
            elif uptime is not None:  # if specified uptime, override
                ts1 = uptime
            elif ts1 is None or not (0 < ts1 < 2**32):  # invalid hint
                ts1 = random.randint(120, 100 * 60 * 60 * 24 * 365)

            # non-zero peer timestamp on initial SYN
            if "ts2+" in quirks and tcp_type == 0x02:
                if ts2 is None or not (0 < ts2 < 2**32):  # invalid hint
                    ts2 = random.randint(1, 2**32 - 1)
            else:
                ts2 = 0
            options.append(("Timestamp", (ts1, ts2)))

        elif opt == "nop":
            options.append(("NOP", None))
        elif opt == "sok":
            options.append(("SAckOK", ""))
        elif opt[:3] == "eol":
            options.append(("EOL", None))
            # FIXME: opt+ quirk not handled
            if "opt+" in quirks:
                warning("Unhandled opt+ quirk")
        elif opt == "sack":
            # Randomize SAck value in range of 10 <= val <= 34
            sack_len = random.choice([10, 18, 26, 34]) - 2
            optstruct = "!%iI" % (sack_len // 4)
            rand_val = RandString(struct.calcsize(optstruct))._fix()
            options.append(("SAck", struct.unpack(optstruct, rand_val)))
        else:
            warning("Unhandled TCP option %s", opt)
        tcp.options = options

    if sig.win_type == WIN_TYPE_NORMAL:
        tcp.window = sig.win
    elif sig.win_type == WIN_TYPE_MSS:
        mss = [x for x in options if x[0] == "MSS"]
        if not mss:
            raise ValueError(
                "TCP window value requires MSS, and MSS option not set"
            )  # noqa: E501
        tcp.window = mss[0][1] * sig.win
    elif sig.win_type == WIN_TYPE_MOD:
        tcp.window = sig.win * random.randint(1, (2**16 - 1) // sig.win)
    elif sig.win_type == WIN_TYPE_MTU:
        tcp.window = mtu * sig.win
    elif sig.win_type == WIN_TYPE_ANY:
        tcp.window = RandShort()
    else:
        warning("Unhandled window size specification")

    if "seq-" in quirks:
        tcp.seq = 0
    elif tcp.seq == 0:
        tcp.seq = random.randint(1, 2**32 - 1)

    if "ack+" in quirks:
        tcp.flags &= ~(0x10)  # ACK flag not set
        if tcp.ack == 0:
            tcp.ack = random.randint(1, 2**32 - 1)
    elif "ack-" in quirks:
        tcp.flags |= 0x10  # ACK flag set
        tcp.ack = 0

    if "uptr+" in quirks:
        tcp.flags &= ~(0x020)  # URG flag not set
        if tcp.urgptr == 0:
            tcp.urgptr = random.randint(1, 2**16 - 1)
    elif "urgf+" in quirks:
        tcp.flags |= 0x020  # URG flag used

    tcp.flags = tcp.flags | 0x08 if "pushf+" in quirks else tcp.flags & ~(0x08)

    if sig.pay_class:  # signature has payload
        if not tcp.payload:
            pkt /= conf.raw_layer(load=RandString(random.randint(1, 10)))
    else:
        tcp.payload = NoPayload()

    return pkt
Exemplo n.º 4
0
 def __init__(self, template):
     RandString.__init__(self)
     self.bytecount = template.count("*")
     self.format = template.replace("*", "%02X")
Exemplo n.º 5
0
 def randval(self):
     # type: () -> RandString
     return RandString(RandNum(0, 1000))
Exemplo n.º 6
0
def p0f_impersonate(pkt,
                    osgenre=None,
                    osdetails=None,
                    signature=None,
                    extrahops=0,
                    mtu=1500,
                    uptime=None):
    """Modifies pkt so that p0f will think it has been sent by a
specific OS.  If osdetails is None, then we randomly pick up a
personality matching osgenre. If osgenre and signature are also None,
we use a local signature (using p0f_getlocalsigs). If signature is
specified (as a tuple), we use the signature.

For now, only TCP Syn packets are supported.
Some specifications of the p0f.fp file are not (yet) implemented."""
    pkt = pkt.copy()
    #pkt = pkt.__class__(str(pkt))
    while pkt.haslayer(IP) and pkt.haslayer(TCP):
        pkt = pkt.getlayer(IP)
        if isinstance(pkt.payload, TCP):
            break
        pkt = pkt.payload

    if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP):
        raise TypeError("Not a TCP/IP packet")

    if uptime is None:
        uptime = random.randint(120, 100 * 60 * 60 * 24 * 365)

    db = p0f_selectdb(pkt.payload.flags)
    if osgenre:
        pb = db.get_base()
        if pb is None:
            pb = []
        pb = filter(lambda x: x[6] == osgenre, pb)
        if osdetails:
            pb = filter(lambda x: x[7] == osdetails, pb)
    elif signature:
        pb = [signature]
    else:
        pb = p0f_getlocalsigs()[db]
    if db == p0fr_kdb:
        # 'K' quirk <=> RST+ACK
        if pkt.payload.flags & 0x4 == 0x4:
            pb = filter(lambda x: 'K' in x[5], pb)
        else:
            pb = filter(lambda x: 'K' not in x[5], pb)
    if not pb:
        raise Scapy_Exception("No match in the p0f database")
    pers = pb[random.randint(0, len(pb) - 1)]

    # options (we start with options because of MSS)
    ## TODO: let the options already set if they are valid
    options = []
    if pers[4] != '.':
        for opt in pers[4].split(','):
            if opt[0] == 'M':
                # MSS might have a maximum size because of window size
                # specification
                if pers[0][0] == 'S':
                    maxmss = (2L**16 - 1) / int(pers[0][1:])
                else:
                    maxmss = (2L**16 - 1)
                # If we have to randomly pick up a value, we cannot use
                # scapy RandXXX() functions, because the value has to be
                # set in case we need it for the window size value. That's
                # why we use random.randint()
                if opt[1:] == '*':
                    options.append(('MSS', random.randint(1, maxmss)))
                elif opt[1] == '%':
                    coef = int(opt[2:])
                    options.append(
                        ('MSS', coef * random.randint(1, maxmss / coef)))
                else:
                    options.append(('MSS', int(opt[1:])))
            elif opt[0] == 'W':
                if opt[1:] == '*':
                    options.append(('WScale', RandByte()))
                elif opt[1] == '%':
                    coef = int(opt[2:])
                    options.append(
                        ('WScale',
                         coef * RandNum(min=1, max=(2L**8 - 1) / coef)))
                else:
                    options.append(('WScale', int(opt[1:])))
            elif opt == 'T0':
                options.append(('Timestamp', (0, 0)))
            elif opt == 'T':
                if 'T' in pers[5]:
                    # FIXME: RandInt() here does not work (bug (?) in
                    # TCPOptionsField.m2i often raises "OverflowError:
                    # long int too large to convert to int" in:
                    #    oval = struct.pack(ofmt, *oval)"
                    # Actually, this is enough to often raise the error:
                    #    struct.pack('I', RandInt())
                    options.append(
                        ('Timestamp', (uptime, random.randint(1, 2**32 - 1))))
                else:
                    options.append(('Timestamp', (uptime, 0)))
            elif opt == 'S':
                options.append(('SAckOK', ''))
            elif opt == 'N':
                options.append(('NOP', None))
            elif opt == 'E':
                options.append(('EOL', None))
            elif opt[0] == '?':
                if int(opt[1:]) in TCPOptions[0]:
                    optname = TCPOptions[0][int(opt[1:])][0]
                    optstruct = TCPOptions[0][int(opt[1:])][1]
                    options.append(
                        (optname,
                         struct.unpack(
                             optstruct,
                             RandString(struct.calcsize(optstruct))._fix())))
                else:
                    options.append((int(opt[1:]), ''))
            ## FIXME: qqP not handled
            else:
                warning("unhandled TCP option " + opt)
            pkt.payload.options = options

    # window size
    if pers[0] == '*':
        pkt.payload.window = RandShort()
    elif pers[0].isdigit():
        pkt.payload.window = int(pers[0])
    elif pers[0][0] == '%':
        coef = int(pers[0][1:])
        pkt.payload.window = coef * RandNum(min=1, max=(2L**16 - 1) / coef)
    elif pers[0][0] == 'T':
        pkt.payload.window = mtu * int(pers[0][1:])
    elif pers[0][0] == 'S':
        ## needs MSS set
        MSS = filter(lambda x: x[0] == 'MSS', options)
        if not filter(lambda x: x[0] == 'MSS', options):
            raise Scapy_Exception(
                "TCP window value requires MSS, and MSS option not set")
        pkt.payload.window = filter(lambda x: x[0] == 'MSS',
                                    options)[0][1] * int(pers[0][1:])
    else:
        raise Scapy_Exception('Unhandled window size specification')

    # ttl
    pkt.ttl = pers[1] - extrahops
    # DF flag
    pkt.flags |= (2 * pers[2])
    ## FIXME: ss (packet size) not handled (how ? may be with D quirk
    ## if present)
    # Quirks
    if pers[5] != '.':
        for qq in pers[5]:
            ## FIXME: not handled: P, I, X, !
            # T handled with the Timestamp option
            if qq == 'Z': pkt.id = 0
            elif qq == 'U': pkt.payload.urgptr = RandShort()
            elif qq == 'A': pkt.payload.ack = RandInt()
            elif qq == 'F':
                if db == p0fo_kdb:
                    pkt.payload.flags |= 0x20  # U
                else:
                    pkt.payload.flags |= RandChoice(8, 32, 40)  #P / U / PU
            elif qq == 'D' and db != p0fo_kdb:
                pkt /= conf.raw_layer(load=RandString(random.randint(
                    1, 10)))  # XXX p0fo.fp
            elif qq == 'Q':
                pkt.payload.seq = pkt.payload.ack
            #elif qq == '0': pkt.payload.seq = 0
        #if db == p0fr_kdb:
        # '0' quirk is actually not only for p0fr.fp (see
        # packet2p0f())
    if '0' in pers[5]:
        pkt.payload.seq = 0
    elif pkt.payload.seq == 0:
        pkt.payload.seq = RandInt()

    while pkt.underlayer:
        pkt = pkt.underlayer
    return pkt