Source code for sulley.primitives.string

from base import base

[docs]class string (base): # store fuzz_library as a class variable to avoid copying the ~70MB structure across each instantiated primitive. fuzz_library = [] def __init__ (self, value, size=-1, padding="\x00", encoding="ascii", fuzzable=True, max_len=0, name=None): """ Primitive that cycles through a library of "bad" strings. The class variable 'fuzz_library' contains a list of smart fuzz values global across all instances. The 'this_library' variable contains fuzz values specific to the instantiated primitive. This allows us to avoid copying the near ~70MB fuzz_library data structure across each instantiated primitive. @type value: String @param value: Default string value @type size: Integer @param size: (Optional, def=-1) Static size of this field, leave -1 for dynamic. @type padding: Character @param padding: (Optional, def="\\x00") Value to use as padding to fill static field size. @type encoding: String @param encoding: (Optonal, def="ascii") String encoding, i.e. utf_16_le for Microsoft Unicode. @type fuzzable: Boolean @param fuzzable: (Optional, def=True) Enable/disable fuzzing of this primitive @type max_len: Integer @param max_len: (Optional, def=0) Maximum string length @type name: String @param name: (Optional, def=None) Specifying a name gives you direct access to a primitive """ super(string, self).__init__() self.value = self.original_value = value self.size = size self.padding = padding self.encoding = encoding self.fuzzable = fuzzable self.name = name self.s_type = "string" # for ease of object identification self.rendered = "" # rendered value self.fuzz_complete = False # flag if this primitive has been completely fuzzed self.mutant_index = 0 # current mutation number # add this specific primitives repitition values to the unique fuzz library. self.this_library =\ [ self.value * 2, self.value * 10, self.value * 100, # UTF-8 self.value * 2 + "\xfe", self.value * 10 + "\xfe", self.value * 100 + "\xfe", ] # if the fuzz library has not yet been initialized, do so with all the global values. if not self.fuzz_library: string.fuzz_library =\ [ # omission. "", # strings ripped from spike (and some others I added) "/.:/" + "A"*5000 + "\x00\x00", "/.../" + "A"*5000 + "\x00\x00", "/.../.../.../.../.../.../.../.../.../.../", "/../../../../../../../../../../../../etc/passwd", "/../../../../../../../../../../../../boot.ini", "..:..:..:..:..:..:..:..:..:..:..:..:..:", "\\\\*", "\\\\?\\", "/\\" * 5000, "/." * 5000, "!@#$%%^#$%#$@#$%$$@#$%^^**(()", "%01%02%03%04%0a%0d%0aADSF", "%01%02%03@%04%0a%0d%0aADSF", "/%00/", "%00/", "%00", "%u0000", "%\xfe\xf0%\x00\xff", "%\xfe\xf0%\x01\xff" * 20, # format strings. "%n" * 100, "%n" * 500, "\"%n\"" * 500, "%s" * 100, "%s" * 500, "\"%s\"" * 500, # command injection. "|touch /tmp/SULLEY", ";touch /tmp/SULLEY;", "|notepad", ";notepad;", "\nnotepad\n", # SQL injection. "1;SELECT%20*", "'sqlattempt1", "(sqlattempt2)", "OR%201=1", # some binary strings. "\xde\xad\xbe\xef", "\xde\xad\xbe\xef" * 10, "\xde\xad\xbe\xef" * 100, "\xde\xad\xbe\xef" * 1000, "\xde\xad\xbe\xef" * 10000, "\x00" * 1000, # miscellaneous. "\r\n" * 100, "<>" * 500, # sendmail crackaddr (http://lsd-pl.net/other/sendmail.txt) ] # add some long strings. self.add_long_strings("A") self.add_long_strings("B") self.add_long_strings("1") self.add_long_strings("2") self.add_long_strings("3") self.add_long_strings("<") self.add_long_strings(">") self.add_long_strings("'") self.add_long_strings("\"") self.add_long_strings("/") self.add_long_strings("\\") self.add_long_strings("?") self.add_long_strings("=") self.add_long_strings("a=") self.add_long_strings("&") self.add_long_strings(".") self.add_long_strings(",") self.add_long_strings("(") self.add_long_strings(")") self.add_long_strings("]") self.add_long_strings("[") self.add_long_strings("%") self.add_long_strings("*") self.add_long_strings("-") self.add_long_strings("+") self.add_long_strings("{") self.add_long_strings("}") self.add_long_strings("\x14") self.add_long_strings("\xFE") # expands to 4 characters under utf16 self.add_long_strings("\xFF") # expands to 4 characters under utf16 # add some long strings with null bytes thrown in the middle of it. for length in [128, 256, 1024, 2048, 4096, 32767, 0xFFFF]: s = "B" * length s = s[:len(s)/2] + "\x00" + s[len(s)/2:] string.fuzz_library.append(s) # if the optional file '.fuzz_strings' is found, parse each line as a new entry for the fuzz library. try: fh = open(".fuzz_strings", "r") for fuzz_string in fh.readlines(): fuzz_string = fuzz_string.rstrip("\r\n") if fuzz_string != "": string.fuzz_library.append(fuzz_string) fh.close() except: pass # delete strings which length is greater than max_len. if max_len > 0: if any(len(s) > max_len for s in self.this_library): self.this_library = list(set([s[:max_len] for s in self.this_library])) if any(len(s) > max_len for s in self.fuzz_library): self.fuzz_library = list(set([s[:max_len] for s in self.fuzz_library]))
[docs] def add_long_strings (self, sequence): """ Given a sequence, generate a number of selectively chosen strings lengths of the given sequence and add to the string heuristic library. @type sequence: String @param sequence: Sequence to repeat for creation of fuzz strings. """ for length in [128, 255, 256, 257, 511, 512, 513, 1023, 1024, 2048, 2049, 4095, 4096, 4097, 5000, 10000, 20000, 32762, 32763, 32764, 32765, 32766, 32767, 32768, 32769, 0xFFFF-2, 0xFFFF-1, 0xFFFF, 0xFFFF+1, 0xFFFF+2, 99999, 100000, 500000, 1000000]: long_string = sequence * length string.fuzz_library.append(long_string)
[docs] def mutate (self): """ Mutate the primitive by stepping through the fuzz library extended with the "this" library, return False on completion. :rtype: Boolean :returns: True on success, False otherwise. """ # loop through the fuzz library until a suitable match is found. while 1: # if we've ran out of mutations, raise the completion flag. if self.mutant_index == self.num_mutations(): self.fuzz_complete = True # if fuzzing was disabled or complete, and mutate() is called, ensure the original value is restored. if not self.fuzzable or self.fuzz_complete: self.value = self.original_value return False # update the current value from the fuzz library. self.value = (self.fuzz_library + self.this_library)[self.mutant_index] # increment the mutation count. self.mutant_index += 1 # if the size parameter is disabled, break out of the loop right now. if self.size == -1: break # ignore library items greather then user-supplied length. # XXX - might want to make this smarter. if len(self.value) > self.size: continue # pad undersized library items. if len(self.value) < self.size: self.value += self.padding * (self.size - len(self.value)) break return True
[docs] def num_mutations (self): """ Calculate and return the total number of mutations for this individual primitive. :rtype: Integer :returns: Number of mutated forms this primitive can take """ return len(self.fuzz_library) + len(self.this_library)
[docs] def render (self): """ Render the primitive, encode the string according to the specified encoding. """ # try to encode the string properly and fall back to the default value on failure. try: self.rendered = str(self.value).encode(self.encoding) except: self.rendered = self.value return self.rendered