22
22
from scapy .error import warning , Scapy_Exception , log_runtime
23
23
from scapy .volatile import RandInt , RandByte , RandChoice , RandNum , RandShort , RandString
24
24
from scapy .sendrecv import sniff
25
+ from scapy .modules import six
25
26
from scapy .modules .six .moves import map , range
26
27
if conf .route is None :
27
28
# unused import, only to initialize conf.route
@@ -359,10 +360,7 @@ def p0f_impersonate(pkt, osgenre=None, osdetails=None, signature=None,
359
360
360
361
if not isinstance (pkt , IP ) or not isinstance (pkt .payload , TCP ):
361
362
raise TypeError ("Not a TCP/IP packet" )
362
-
363
- if uptime is None :
364
- uptime = random .randint (120 ,100 * 60 * 60 * 24 * 365 )
365
-
363
+
366
364
db = p0f_selectdb (pkt .payload .flags )
367
365
if osgenre :
368
366
pb = db .get_base ()
@@ -386,50 +384,89 @@ def p0f_impersonate(pkt, osgenre=None, osdetails=None, signature=None,
386
384
pers = pb [random .randint (0 , len (pb ) - 1 )]
387
385
388
386
# options (we start with options because of MSS)
389
- ## TODO: let the options already set if they are valid
387
+ # Take the options already set as "hints" to use in the new packet if we
388
+ # can. MSS, WScale and Timestamp can all be wildcarded in a signature, so
389
+ # we'll use the already-set values if they're valid integers.
390
+ orig_opts = dict (pkt .payload .options )
391
+ int_only = lambda val : val if isinstance (val , six .integer_types ) else None
392
+ mss_hint = int_only (orig_opts .get ('MSS' ))
393
+ wscale_hint = int_only (orig_opts .get ('WScale' ))
394
+ ts_hint = [int_only (o ) for o in orig_opts .get ('Timestamp' , (None , None ))]
395
+
390
396
options = []
391
397
if pers [4 ] != '.' :
392
398
for opt in pers [4 ].split (',' ):
393
399
if opt [0 ] == 'M' :
394
400
# MSS might have a maximum size because of window size
395
401
# specification
396
402
if pers [0 ][0 ] == 'S' :
397
- maxmss = (2 ** 16 - 1 ) / int (pers [0 ][1 :])
403
+ maxmss = (2 ** 16 - 1 ) // int (pers [0 ][1 :])
398
404
else :
399
405
maxmss = (2 ** 16 - 1 )
406
+ # disregard hint if out of range
407
+ if mss_hint and not 0 <= mss_hint <= maxmss :
408
+ mss_hint = None
400
409
# If we have to randomly pick up a value, we cannot use
401
410
# scapy RandXXX() functions, because the value has to be
402
411
# set in case we need it for the window size value. That's
403
412
# why we use random.randint()
404
413
if opt [1 :] == '*' :
405
- options .append (('MSS' , random .randint (1 ,maxmss )))
414
+ if mss_hint is not None :
415
+ options .append (('MSS' , mss_hint ))
416
+ else :
417
+ options .append (('MSS' , random .randint (1 , maxmss )))
406
418
elif opt [1 ] == '%' :
407
419
coef = int (opt [2 :])
408
- options .append (('MSS' , coef * random .randint (1 ,maxmss / coef )))
420
+ if mss_hint is not None and mss_hint % coef == 0 :
421
+ options .append (('MSS' , mss_hint ))
422
+ else :
423
+ options .append ((
424
+ 'MSS' , coef * random .randint (1 , maxmss // coef )))
409
425
else :
410
426
options .append (('MSS' , int (opt [1 :])))
411
427
elif opt [0 ] == 'W' :
428
+ if wscale_hint and not 0 <= wscale_hint < 2 ** 8 :
429
+ wscale_hint = None
412
430
if opt [1 :] == '*' :
413
- options .append (('WScale' , RandByte ()))
431
+ if wscale_hint is not None :
432
+ options .append (('WScale' , wscale_hint ))
433
+ else :
434
+ options .append (('WScale' , RandByte ()))
414
435
elif opt [1 ] == '%' :
415
436
coef = int (opt [2 :])
416
- options .append (('WScale' , coef * RandNum (min = 1 ,
417
- max = (2 ** 8 - 1 )/ coef )))
437
+ if wscale_hint is not None and wscale_hint % coef == 0 :
438
+ options .append (('WScale' , wscale_hint ))
439
+ else :
440
+ options .append ((
441
+ 'WScale' , coef * RandNum (min = 1 , max = (2 ** 8 - 1 )// coef )))
418
442
else :
419
443
options .append (('WScale' , int (opt [1 :])))
420
444
elif opt == 'T0' :
421
445
options .append (('Timestamp' , (0 , 0 )))
422
446
elif opt == 'T' :
423
- if 'T' in pers [5 ]:
447
+ # Determine first timestamp.
448
+ if uptime is not None :
449
+ ts_a = uptime
450
+ elif ts_hint [0 ] and 0 < ts_hint [0 ] < 2 ** 32 :
451
+ # Note: if first ts is 0, p0f registers it as "T0" not "T",
452
+ # hence we don't want to use the hint if it was 0.
453
+ ts_a = ts_hint [0 ]
454
+ else :
455
+ ts_a = random .randint (120 , 100 * 60 * 60 * 24 * 365 )
456
+ # Determine second timestamp.
457
+ if 'T' not in pers [5 ]:
458
+ ts_b = 0
459
+ elif ts_hint [1 ] and 0 < ts_hint [1 ] < 2 ** 32 :
460
+ ts_b = ts_hint [1 ]
461
+ else :
424
462
# FIXME: RandInt() here does not work (bug (?) in
425
463
# TCPOptionsField.m2i often raises "OverflowError:
426
464
# long int too large to convert to int" in:
427
465
# oval = struct.pack(ofmt, *oval)"
428
466
# Actually, this is enough to often raise the error:
429
467
# struct.pack('I', RandInt())
430
- options .append (('Timestamp' , (uptime , random .randint (1 ,2 ** 32 - 1 ))))
431
- else :
432
- options .append (('Timestamp' , (uptime , 0 )))
468
+ ts_b = random .randint (1 , 2 ** 32 - 1 )
469
+ options .append (('Timestamp' , (ts_a , ts_b )))
433
470
elif opt == 'S' :
434
471
options .append (('SAckOK' , '' ))
435
472
elif opt == 'N' :
@@ -457,15 +494,15 @@ def p0f_impersonate(pkt, osgenre=None, osdetails=None, signature=None,
457
494
pkt .payload .window = int (pers [0 ])
458
495
elif pers [0 ][0 ] == '%' :
459
496
coef = int (pers [0 ][1 :])
460
- pkt .payload .window = coef * RandNum (min = 1 ,max = (2 ** 16 - 1 )/ coef )
497
+ pkt .payload .window = coef * RandNum (min = 1 , max = (2 ** 16 - 1 )/ /coef )
461
498
elif pers [0 ][0 ] == 'T' :
462
499
pkt .payload .window = mtu * int (pers [0 ][1 :])
463
500
elif pers [0 ][0 ] == 'S' :
464
501
## needs MSS set
465
502
mss = [x for x in options if x [0 ] == 'MSS' ]
466
503
if not mss :
467
504
raise Scapy_Exception ("TCP window value requires MSS, and MSS option not set" )
468
- pkt .payload .window = filter ( lambda x : x [ 0 ] == 'MSS' , options ) [0 ][1 ] * int (pers [0 ][1 :])
505
+ pkt .payload .window = mss [0 ][1 ] * int (pers [0 ][1 :])
469
506
else :
470
507
raise Scapy_Exception ('Unhandled window size specification' )
471
508
@@ -487,7 +524,7 @@ def p0f_impersonate(pkt, osgenre=None, osdetails=None, signature=None,
487
524
if db == p0fo_kdb :
488
525
pkt .payload .flags |= 0x20 # U
489
526
else :
490
- pkt .payload .flags |= RandChoice ( 8 , 32 , 40 ) #P / U / PU
527
+ pkt .payload .flags |= random . choice ([ 8 , 32 , 40 ]) # P/U/ PU
491
528
elif qq == 'D' and db != p0fo_kdb :
492
529
pkt /= conf .raw_layer (load = RandString (random .randint (1 , 10 ))) # XXX p0fo.fp
493
530
elif qq == 'Q' : pkt .payload .seq = pkt .payload .ack
0 commit comments