geneva/actions/tamper.py

115 lines
4.4 KiB
Python

"""
TamperAction
One of the four packet-level primitives supported by Geneva. Responsible for any packet-level
modifications (particularly header modifications). It supports replace and corrupt mode -
in replace mode, it changes a packet field to a fixed value; in corrupt mode, it changes a packet
field to a randomly generated value each time it is run.
"""
from actions.action import Action
import actions.utils
import random
class TamperAction(Action):
"""
Defines the TamperAction for Geneva.
"""
def __init__(self, environment_id=None, field=None, tamper_type=None, tamper_value=None, tamper_proto="TCP"):
Action.__init__(self, "tamper", "both")
self.field = field
self.tamper_value = tamper_value
self.tamper_proto = actions.utils.string_to_protocol(tamper_proto)
self.tamper_proto_str = tamper_proto
self.tamper_type = tamper_type
if not self.tamper_type:
self.tamper_type = random.choice(["corrupt", "replace"])
def tamper(self, packet, logger):
"""
Edits a given packet according to the action settings.
"""
# Return packet untouched if not applicable
if not packet.haslayer(self.tamper_proto_str):
return packet
# Retrieve the old value of the field for logging purposes
old_value = packet.get(self.tamper_proto_str, self.field)
new_value = self.tamper_value
# If corrupting the packet field, generate a value for it
if self.tamper_type == "corrupt":
new_value = packet.gen(self.tamper_proto_str, self.field)
logger.debug(" - Tampering %s field `%s` (%s) by %s (to %s)" %
(self.tamper_proto_str, self.field, str(old_value), self.tamper_type, str(new_value)))
packet.set(self.tamper_proto_str, self.field, new_value)
return packet
def run(self, packet, logger):
"""
The tamper action runs its tamper procedure on the given packet, and
returns the edited packet down the left branch.
Nothing is returned to the right branch.
"""
return self.tamper(packet, logger), None
def __str__(self):
"""
Defines string representation for this object.
"""
s = Action.__str__(self)
if self.tamper_type == "corrupt":
s += "{%s:%s:%s}" % (self.tamper_proto_str, self.field, self.tamper_type)
elif self.tamper_type in ["replace"]:
s += "{%s:%s:%s:%s}" % (self.tamper_proto_str, self.field, self.tamper_type, self.tamper_value)
return s
def parse(self, string, logger):
"""
Parse out a given string representation of this action and initialize
this action to those parameters.
Note that the given logger is a DIFFERENT logger than the logger passed
to the other functions, and they cannot be used interchangeably. This logger
is attached to the main GA driver, and is run outside the evaluator. When the
action is actually run, it's run within the evaluator, which by necessity must
pass in a different logger.
"""
# Different tamper actions will have different number of parameters
# Count the number of params in this given string
num_parameters = string.count(":")
# If num_parameters is greater than 3, it's not a valid tamper action
if num_parameters > 3 or num_parameters < 2:
msg = "Cannot parse tamper action %s" % string
logger.error(msg)
raise Exception(msg)
params = string.split(":")
if num_parameters == 3:
self.tamper_proto_str, self.field, self.tamper_type, self.tamper_value = params
self.tamper_proto = actions.utils.string_to_protocol(self.tamper_proto_str)
if "options" in self.field:
if not self.tamper_value:
self.tamper_value = '' # An empty string instead of an empty byte literal
# tamper_value might be parsed as a string despite being an integer in most cases.
# Try to parse it out here
try:
if "load" not in self.field:
self.tamper_value = int(self.tamper_value)
except:
pass
else:
self.tamper_proto_str, self.field, self.tamper_type = params
self.tamper_proto = actions.utils.string_to_protocol(self.tamper_proto_str)
return True