## This file is part of Scapy
|
## See http://www.secdev.org/projects/scapy for more informations
|
## Copyright (C) Philippe Biondi <phil@secdev.org>
|
## This program is published under a GPLv2 license
|
|
"""
|
Fields that hold random numbers.
|
"""
|
|
from __future__ import absolute_import
|
import random,time,math
|
from scapy.base_classes import Net
|
from scapy.compat import *
|
from scapy.utils import corrupt_bits,corrupt_bytes
|
from scapy.modules.six.moves import range
|
|
####################
|
## Random numbers ##
|
####################
|
|
|
class RandomEnumeration:
|
"""iterate through a sequence in random order.
|
When all the values have been drawn, if forever=1, the drawing is done again.
|
If renewkeys=0, the draw will be in the same order, guaranteeing that the same
|
number will be drawn in not less than the number of integers of the sequence"""
|
def __init__(self, inf, sup, seed=None, forever=1, renewkeys=0):
|
self.forever = forever
|
self.renewkeys = renewkeys
|
self.inf = inf
|
self.rnd = random.Random(seed)
|
self.sbox_size = 256
|
|
self.top = sup-inf+1
|
|
n=0
|
while (1<<n) < self.top:
|
n += 1
|
self.n =n
|
|
self.fs = min(3,(n+1)//2)
|
self.fsmask = 2**self.fs-1
|
self.rounds = max(self.n,3)
|
self.turns = 0
|
self.i = 0
|
|
def __iter__(self):
|
return self
|
def next(self):
|
while True:
|
if self.turns == 0 or (self.i == 0 and self.renewkeys):
|
self.cnt_key = self.rnd.randint(0,2**self.n-1)
|
self.sbox = [self.rnd.randint(0, self.fsmask)
|
for _ in range(self.sbox_size)]
|
self.turns += 1
|
while self.i < 2**self.n:
|
ct = self.i^self.cnt_key
|
self.i += 1
|
for _ in range(self.rounds): # Unbalanced Feistel Network
|
lsb = ct & self.fsmask
|
ct >>= self.fs
|
lsb ^= self.sbox[ct%self.sbox_size]
|
ct |= lsb << (self.n-self.fs)
|
|
if ct < self.top:
|
return self.inf+ct
|
self.i = 0
|
if not self.forever:
|
raise StopIteration
|
__next__ = next
|
|
|
class VolatileValue:
|
def __repr__(self):
|
return "<%s>" % self.__class__.__name__
|
def __eq__(self, other):
|
x = self._fix()
|
y = other._fix() if isinstance(other, VolatileValue) else other
|
if not isinstance(x, type(y)):
|
return False
|
return x == y
|
def __getattr__(self, attr):
|
if attr in ["__setstate__", "__getstate__"]:
|
raise AttributeError(attr)
|
return getattr(self._fix(),attr)
|
def __str__(self):
|
return str(self._fix())
|
def __bytes__(self):
|
return raw(self._fix())
|
def __len__(self):
|
return len(self._fix())
|
|
def _fix(self):
|
return None
|
|
|
class RandField(VolatileValue):
|
pass
|
|
class RandNum(RandField):
|
"""Instances evaluate to random integers in selected range"""
|
min = 0
|
max = 0
|
def __init__(self, min, max):
|
self.min = min
|
self.max = max
|
def _fix(self):
|
return random.randrange(self.min, self.max+1)
|
|
def __int__(self):
|
return int(self._fix())
|
def __index__(self):
|
return int(self)
|
def __add__(self, other):
|
return self._fix() + other
|
def __radd__(self, other):
|
return other + self._fix()
|
def __sub__(self, other):
|
return self._fix() - other
|
def __rsub__(self, other):
|
return other - self._fix()
|
def __mul__(self, other):
|
return self._fix() * other
|
def __rmul__(self, other):
|
return other * self._fix()
|
def __floordiv__(self, other):
|
return self._fix() / other
|
__div__ = __floordiv__
|
|
class RandNumGamma(RandNum):
|
def __init__(self, alpha, beta):
|
self.alpha = alpha
|
self.beta = beta
|
def _fix(self):
|
return int(round(random.gammavariate(self.alpha, self.beta)))
|
|
class RandNumGauss(RandNum):
|
def __init__(self, mu, sigma):
|
self.mu = mu
|
self.sigma = sigma
|
def _fix(self):
|
return int(round(random.gauss(self.mu, self.sigma)))
|
|
class RandNumExpo(RandNum):
|
def __init__(self, lambd, base=0):
|
self.lambd = lambd
|
self.base = base
|
def _fix(self):
|
return self.base+int(round(random.expovariate(self.lambd)))
|
|
class RandEnum(RandNum):
|
"""Instances evaluate to integer sampling without replacement from the given interval"""
|
def __init__(self, min, max, seed=None):
|
self.seq = RandomEnumeration(min,max,seed)
|
def _fix(self):
|
return next(self.seq)
|
|
class RandByte(RandNum):
|
def __init__(self):
|
RandNum.__init__(self, 0, 2**8-1)
|
|
class RandSByte(RandNum):
|
def __init__(self):
|
RandNum.__init__(self, -2**7, 2**7-1)
|
|
class RandShort(RandNum):
|
def __init__(self):
|
RandNum.__init__(self, 0, 2**16-1)
|
|
class RandSShort(RandNum):
|
def __init__(self):
|
RandNum.__init__(self, -2**15, 2**15-1)
|
|
class RandInt(RandNum):
|
def __init__(self):
|
RandNum.__init__(self, 0, 2**32-1)
|
|
class RandSInt(RandNum):
|
def __init__(self):
|
RandNum.__init__(self, -2**31, 2**31-1)
|
|
class RandLong(RandNum):
|
def __init__(self):
|
RandNum.__init__(self, 0, 2**64-1)
|
|
class RandSLong(RandNum):
|
def __init__(self):
|
RandNum.__init__(self, -2**63, 2**63-1)
|
|
class RandEnumByte(RandEnum):
|
def __init__(self):
|
RandEnum.__init__(self, 0, 2**8-1)
|
|
class RandEnumSByte(RandEnum):
|
def __init__(self):
|
RandEnum.__init__(self, -2**7, 2**7-1)
|
|
class RandEnumShort(RandEnum):
|
def __init__(self):
|
RandEnum.__init__(self, 0, 2**16-1)
|
|
class RandEnumSShort(RandEnum):
|
def __init__(self):
|
RandEnum.__init__(self, -2**15, 2**15-1)
|
|
class RandEnumInt(RandEnum):
|
def __init__(self):
|
RandEnum.__init__(self, 0, 2**32-1)
|
|
class RandEnumSInt(RandEnum):
|
def __init__(self):
|
RandEnum.__init__(self, -2**31, 2**31-1)
|
|
class RandEnumLong(RandEnum):
|
def __init__(self):
|
RandEnum.__init__(self, 0, 2**64-1)
|
|
class RandEnumSLong(RandEnum):
|
def __init__(self):
|
RandEnum.__init__(self, -2**63, 2**63-1)
|
|
class RandEnumKeys(RandEnum):
|
"""Picks a random value from dict keys list. """
|
def __init__(self, enum, seed=None):
|
self.enum = list(enum)
|
self.seq = RandomEnumeration(0, len(self.enum) - 1, seed)
|
|
def _fix(self):
|
return self.enum[next(self.seq)]
|
|
class RandChoice(RandField):
|
def __init__(self, *args):
|
if not args:
|
raise TypeError("RandChoice needs at least one choice")
|
self._choice = args
|
def _fix(self):
|
return random.choice(self._choice)
|
|
class RandString(RandField):
|
def __init__(self, size=None, chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"):
|
if size is None:
|
size = RandNumExpo(0.01)
|
self.size = size
|
self.chars = chars
|
def _fix(self):
|
s = ""
|
for _ in range(self.size):
|
s += random.choice(self.chars)
|
return s
|
def __mul__(self, n):
|
return self._fix()*n
|
|
class RandBin(RandString):
|
def __init__(self, size=None):
|
RandString.__init__(self, size, "".join(map(chr, range(256))))
|
|
|
class RandTermString(RandString):
|
def __init__(self, size, term):
|
RandString.__init__(self, size, "".join(map(chr, range(1,256))))
|
self.term = term
|
def _fix(self):
|
return RandString._fix(self)+self.term
|
|
def __str__(self):
|
return str(self._fix())
|
|
def __bytes__(self):
|
return raw(self._fix())
|
|
|
class RandIP(RandString):
|
def __init__(self, iptemplate="0.0.0.0/0"):
|
self.ip = Net(iptemplate)
|
def _fix(self):
|
return self.ip.choice()
|
|
class RandMAC(RandString):
|
def __init__(self, template="*"):
|
template += ":*:*:*:*:*"
|
template = template.split(":")
|
self.mac = ()
|
for i in range(6):
|
if template[i] == "*":
|
v = RandByte()
|
elif "-" in template[i]:
|
x,y = template[i].split("-")
|
v = RandNum(int(x,16), int(y,16))
|
else:
|
v = int(template[i],16)
|
self.mac += (v,)
|
def _fix(self):
|
return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac
|
|
class RandIP6(RandString):
|
def __init__(self, ip6template="**"):
|
self.tmpl = ip6template
|
self.sp = self.tmpl.split(":")
|
for i,v in enumerate(self.sp):
|
if not v or v == "**":
|
continue
|
if "-" in v:
|
a,b = v.split("-")
|
elif v == "*":
|
a=b=""
|
else:
|
a=b=v
|
|
if not a:
|
a = "0"
|
if not b:
|
b = "ffff"
|
if a==b:
|
self.sp[i] = int(a,16)
|
else:
|
self.sp[i] = RandNum(int(a,16), int(b,16))
|
self.variable = "" in self.sp
|
self.multi = self.sp.count("**")
|
def _fix(self):
|
done = 0
|
nbm = self.multi
|
ip = []
|
for i,n in enumerate(self.sp):
|
if n == "**":
|
nbm -= 1
|
remain = 8-(len(self.sp)-i-1)-len(ip)+nbm
|
if "" in self.sp:
|
remain += 1
|
if nbm or self.variable:
|
remain = random.randint(0,remain)
|
for j in range(remain):
|
ip.append("%04x" % random.randint(0,65535))
|
elif isinstance(n, RandNum):
|
ip.append("%04x" % n)
|
elif n == 0:
|
ip.append("0")
|
elif not n:
|
ip.append("")
|
else:
|
ip.append("%04x" % n)
|
if len(ip) == 9:
|
ip.remove("")
|
if ip[-1] == "":
|
ip[-1] = "0"
|
return ":".join(ip)
|
|
class RandOID(RandString):
|
def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)):
|
self.ori_fmt = fmt
|
if fmt is not None:
|
fmt = fmt.split(".")
|
for i in range(len(fmt)):
|
if "-" in fmt[i]:
|
fmt[i] = tuple(map(int, fmt[i].split("-")))
|
self.fmt = fmt
|
self.depth = depth
|
self.idnum = idnum
|
def __repr__(self):
|
if self.ori_fmt is None:
|
return "<%s>" % self.__class__.__name__
|
else:
|
return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt)
|
def _fix(self):
|
if self.fmt is None:
|
return ".".join(str(self.idnum) for _ in range(1 + self.depth))
|
else:
|
oid = []
|
for i in self.fmt:
|
if i == "*":
|
oid.append(str(self.idnum))
|
elif i == "**":
|
oid += [str(self.idnum) for i in range(1 + self.depth)]
|
elif isinstance(i, tuple):
|
oid.append(str(random.randrange(*i)))
|
else:
|
oid.append(i)
|
return ".".join(oid)
|
|
|
class RandRegExp(RandField):
|
def __init__(self, regexp, lambda_=0.3,):
|
self._regexp = regexp
|
self._lambda = lambda_
|
|
@staticmethod
|
def choice_expand(s): #XXX does not support special sets like (ex ':alnum:')
|
m = ""
|
invert = s and s[0] == "^"
|
while True:
|
p = s.find("-")
|
if p < 0:
|
break
|
if p == 0 or p == len(s)-1:
|
m = "-"
|
if p:
|
s = s[:-1]
|
else:
|
s = s[1:]
|
else:
|
c1 = s[p-1]
|
c2 = s[p+1]
|
rng = "".join(map(chr, range(ord(c1), ord(c2)+1)))
|
s = s[:p-1]+rng+s[p+1:]
|
res = m+s
|
if invert:
|
res = "".join(chr(x) for x in range(256) if chr(x) not in res)
|
return res
|
|
@staticmethod
|
def stack_fix(lst, index):
|
r = ""
|
mul = 1
|
for e in lst:
|
if isinstance(e, list):
|
if mul != 1:
|
mul = mul-1
|
r += RandRegExp.stack_fix(e[1:]*mul, index)
|
# only the last iteration should be kept for back reference
|
f = RandRegExp.stack_fix(e[1:], index)
|
for i,idx in enumerate(index):
|
if e is idx:
|
index[i] = f
|
r += f
|
mul = 1
|
elif isinstance(e, tuple):
|
kind,val = e
|
if kind == "cite":
|
r += index[val-1]
|
elif kind == "repeat":
|
mul = val
|
|
elif kind == "choice":
|
if mul == 1:
|
c = random.choice(val)
|
r += RandRegExp.stack_fix(c[1:], index)
|
else:
|
r += RandRegExp.stack_fix([e]*mul, index)
|
mul = 1
|
else:
|
if mul != 1:
|
r += RandRegExp.stack_fix([e]*mul, index)
|
mul = 1
|
else:
|
r += str(e)
|
return r
|
|
def _fix(self):
|
stack = [None]
|
index = []
|
current = stack
|
i = 0
|
ln = len(self._regexp)
|
interp = True
|
while i < ln:
|
c = self._regexp[i]
|
i+=1
|
|
if c == '(':
|
current = [current]
|
current[0].append(current)
|
elif c == '|':
|
p = current[0]
|
ch = p[-1]
|
if not isinstance(ch, tuple):
|
ch = ("choice",[current])
|
p[-1] = ch
|
else:
|
ch[1].append(current)
|
current = [p]
|
elif c == ')':
|
ch = current[0][-1]
|
if isinstance(ch, tuple):
|
ch[1].append(current)
|
index.append(current)
|
current = current[0]
|
elif c == '[' or c == '{':
|
current = [current]
|
current[0].append(current)
|
interp = False
|
elif c == ']':
|
current = current[0]
|
choice = RandRegExp.choice_expand("".join(current.pop()[1:]))
|
current.append(RandChoice(*list(choice)))
|
interp = True
|
elif c == '}':
|
current = current[0]
|
num = "".join(current.pop()[1:])
|
e = current.pop()
|
if "," not in num:
|
n = int(num)
|
current.append([current]+[e]*n)
|
else:
|
num_min,num_max = num.split(",")
|
if not num_min:
|
num_min = "0"
|
if num_max:
|
n = RandNum(int(num_min),int(num_max))
|
else:
|
n = RandNumExpo(self._lambda,base=int(num_min))
|
current.append(("repeat",n))
|
current.append(e)
|
interp = True
|
elif c == '\\':
|
c = self._regexp[i]
|
if c == "s":
|
c = RandChoice(" ","\t")
|
elif c in "0123456789":
|
c = ("cite",ord(c)-0x30)
|
current.append(c)
|
i += 1
|
elif not interp:
|
current.append(c)
|
elif c == '+':
|
e = current.pop()
|
current.append([current]+[e]*(int(random.expovariate(self._lambda))+1))
|
elif c == '*':
|
e = current.pop()
|
current.append([current]+[e]*int(random.expovariate(self._lambda)))
|
elif c == '?':
|
if random.randint(0,1):
|
current.pop()
|
elif c == '.':
|
current.append(RandChoice(*[chr(x) for x in range(256)]))
|
elif c == '$' or c == '^':
|
pass
|
else:
|
current.append(c)
|
|
return RandRegExp.stack_fix(stack[1:], index)
|
def __repr__(self):
|
return "<%s [%r]>" % (self.__class__.__name__, self._regexp)
|
|
class RandSingularity(RandChoice):
|
pass
|
|
class RandSingNum(RandSingularity):
|
@staticmethod
|
def make_power_of_two(end):
|
sign = 1
|
if end == 0:
|
end = 1
|
if end < 0:
|
end = -end
|
sign = -1
|
end_n = int(math.log(end)/math.log(2))+1
|
return {sign*2**i for i in range(end_n)}
|
|
def __init__(self, mn, mx):
|
sing = {0, mn, mx, int((mn+mx)/2)}
|
sing |= self.make_power_of_two(mn)
|
sing |= self.make_power_of_two(mx)
|
for i in sing.copy():
|
sing.add(i+1)
|
sing.add(i-1)
|
for i in sing.copy():
|
if not mn <= i <= mx:
|
sing.remove(i)
|
self._choice = list(sing)
|
self._choice.sort()
|
|
|
class RandSingByte(RandSingNum):
|
def __init__(self):
|
RandSingNum.__init__(self, 0, 2**8-1)
|
|
class RandSingSByte(RandSingNum):
|
def __init__(self):
|
RandSingNum.__init__(self, -2**7, 2**7-1)
|
|
class RandSingShort(RandSingNum):
|
def __init__(self):
|
RandSingNum.__init__(self, 0, 2**16-1)
|
|
class RandSingSShort(RandSingNum):
|
def __init__(self):
|
RandSingNum.__init__(self, -2**15, 2**15-1)
|
|
class RandSingInt(RandSingNum):
|
def __init__(self):
|
RandSingNum.__init__(self, 0, 2**32-1)
|
|
class RandSingSInt(RandSingNum):
|
def __init__(self):
|
RandSingNum.__init__(self, -2**31, 2**31-1)
|
|
class RandSingLong(RandSingNum):
|
def __init__(self):
|
RandSingNum.__init__(self, 0, 2**64-1)
|
|
class RandSingSLong(RandSingNum):
|
def __init__(self):
|
RandSingNum.__init__(self, -2**63, 2**63-1)
|
|
class RandSingString(RandSingularity):
|
def __init__(self):
|
self._choice = [ "",
|
"%x",
|
"%%",
|
"%s",
|
"%i",
|
"%n",
|
"%x%x%x%x%x%x%x%x%x",
|
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
"%",
|
"%%%",
|
"A"*4096,
|
b"\x00"*4096,
|
b"\xff"*4096,
|
b"\x7f"*4096,
|
b"\x80"*4096,
|
" "*4096,
|
"\\"*4096,
|
"("*4096,
|
"../"*1024,
|
"/"*1024,
|
"${HOME}"*512,
|
" or 1=1 --",
|
"' or 1=1 --",
|
'" or 1=1 --',
|
" or 1=1; #",
|
"' or 1=1; #",
|
'" or 1=1; #',
|
";reboot;",
|
"$(reboot)",
|
"`reboot`",
|
"index.php%00",
|
b"\x00",
|
"%00",
|
"\\",
|
"../../../../../../../../../../../../../../../../../etc/passwd",
|
"%2e%2e%2f" * 20 + "etc/passwd",
|
"%252e%252e%252f" * 20 + "boot.ini",
|
"..%c0%af" * 20 + "etc/passwd",
|
"..%c0%af" * 20 + "boot.ini",
|
"//etc/passwd",
|
r"..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\boot.ini",
|
"AUX:",
|
"CLOCK$",
|
"COM:",
|
"CON:",
|
"LPT:",
|
"LST:",
|
"NUL:",
|
"CON:",
|
r"C:\CON\CON",
|
r"C:\boot.ini",
|
r"\\myserver\share",
|
"foo.exe:",
|
"foo.exe\\", ]
|
|
def __str__(self):
|
return str(self._fix())
|
def __bytes__(self):
|
return raw(self._fix())
|
|
|
class RandPool(RandField):
|
def __init__(self, *args):
|
"""Each parameter is a volatile object or a couple (volatile object, weight)"""
|
pool = []
|
for p in args:
|
w = 1
|
if isinstance(p, tuple):
|
p,w = p
|
pool += [p]*w
|
self._pool = pool
|
def _fix(self):
|
r = random.choice(self._pool)
|
return r._fix()
|
|
# Automatic timestamp
|
|
class AutoTime(VolatileValue):
|
def __init__(self, base=None):
|
if base == None:
|
self.diff = 0
|
else:
|
self.diff = time.time()-base
|
def _fix(self):
|
return time.time()-self.diff
|
|
class IntAutoTime(AutoTime):
|
def _fix(self):
|
return int(time.time()-self.diff)
|
|
|
class ZuluTime(AutoTime):
|
def __init__(self, diff=0):
|
self.diff = diff
|
def _fix(self):
|
return time.strftime("%y%m%d%H%M%SZ",
|
time.gmtime(time.time() + self.diff))
|
|
|
class GeneralizedTime(AutoTime):
|
def __init__(self, diff=0):
|
self.diff = diff
|
def _fix(self):
|
return time.strftime("%Y%m%d%H%M%SZ",
|
time.gmtime(time.time() + self.diff))
|
|
|
class DelayedEval(VolatileValue):
|
""" Example of usage: DelayedEval("time.time()") """
|
def __init__(self, expr):
|
self.expr = expr
|
def _fix(self):
|
return eval(self.expr)
|
|
|
class IncrementalValue(VolatileValue):
|
def __init__(self, start=0, step=1, restart=-1):
|
self.start = self.val = start
|
self.step = step
|
self.restart = restart
|
def _fix(self):
|
v = self.val
|
if self.val == self.restart :
|
self.val = self.start
|
else:
|
self.val += self.step
|
return v
|
|
class CorruptedBytes(VolatileValue):
|
def __init__(self, s, p=0.01, n=None):
|
self.s = s
|
self.p = p
|
self.n = n
|
def _fix(self):
|
return corrupt_bytes(self.s, self.p, self.n)
|
|
class CorruptedBits(CorruptedBytes):
|
def _fix(self):
|
return corrupt_bits(self.s, self.p, self.n)
|