About chouchen
An overview of the purpose of each module is as follows:
system_graph describe the scenario on a technical level (using attack graphs).
graphics offers graphical representation of system graphs.
constraints describes architectural constraints associated to procedure and how they are combined.
subconstraints helps with that.
file_generation generates files to populate the resulting machines.
secret_generation generates secrets to populate the resulting machines (passwords, keys…).
procedural_refinement combines all the above modules to generate a procedural-level scenario based on a technical-level description.
This API may be implemented as a PyPi library in the future.
chouchen.system_graph
System Graph definitions.
This module codifies what a scenario is on a technical level, by representing them as attack graphs (nodes and transitions).
- This module contains the following classes:
SystemGraph, the scenario itself.
Node, the nodes of the attack graph. Represented as a tuple of (machine, user) representing an attack position.
Transition, the transitions of the attack graph. Labelled using MITRE ATT&CK techniques and/or procedures.
- This module also provides basic simulation of the path of an attacker throughout the scenario:
AttackState, representing the state of an attacker (controlled nodes, acquired secrets) at a given time.
AttackPath, representing the journey of an attacker through the scenario (list of attack states).
This module can also import or export scenarios in a json format. Scenarios generated using this module may then be represented using the graphics module, or refined into procedural scenarios using the procedural_refinement module.
- class chouchen.system_graph.AttackPath(attack_states, transitions)
A class used to represent the journey of an attacker in the scenario.
- attack_states
The list of all attack states representing the attack path.
- transitions
The list of all transitions that were taken to lead to these attack states.
- check_if_valid(system_graph)
Checks whether the attack path is valid for a given scenario. This is done by checking that each attack state does result from taking each transition one by one.
- Parameters:
system_graph (SystemGraph) – the scenario to check against
- Return type:
bool- Returns:
True if the path is valid, False otherwise
- check_if_winning(system_graph)
Checks if an attack path corresponds to a winning attack path for a given scenario.
- Parameters:
system_graph (SystemGraph) – The scenario to check against.
- Return type:
bool- Returns:
True if the attack path is winning, False otherwise.
- static generate_random_attack_path(system_graph, number_of_actions)
Generates a random attack path for an attacker with a randomized starting position taking n transitions. Only transitions that lead to a different attack state will be taken. Therefore, if number_of_actions is too big this algorithm may end early. The attacker will take a valid transition at random at every step.
- Parameters:
system_graph (
SystemGraph) – The scenario to walk.number_of_actions (
int) – The number of transitions the attacker will take at random.
- Return type:
Returns:
- class chouchen.system_graph.AttackState(nodes, transitions, secrets)
A class used to represent the state of an attacker in the scenario.
- transitions
The list of transitions that were taken by the attacker.
- Type:
list[Transition]
- secrets
The list of secrets that were acquired by the attacker.
- Type:
list[str]
- static default_attack_state(system_graph)
Returns an attack state for the given system_graph where the attacker controls all starting nodes and has no secrets.
- Parameters:
system_graph (SystemGraph) – The system graph object to generate an attack state for.
- Return type:
- Returns:
The corresponding AttackState object
- is_transition_valid(transition)
Checks if an attacker may effectively take a given transition. This is done by checking that he controls the starting node and knows the required secrets.
- Parameters:
transition (Transition) – The transition to check.
- Return type:
bool- Returns:
True if the transition is possible to take, False otherwise.
- is_winning(system_graph)
Checks if the AttackState corresponds to a winning AttackState
- Parameters:
system_graph (SystemGraph) – the scenario being played
- Return type:
bool- Returns:
True if the scenario is won, False otherwise.
- take_random_action(system_graph)
Makes the attacker take a random transition among all the ones he has available.
- Parameters:
system_graph (SystemGraph) – The scenario actions are taken from.
- Raises:
NoValidTransitionError – The attacker has no valid moves left.
- Return type:
None
- take_transition(transition)
Updates the AttackState by taking a transition, and acquiring new attack positions and secrets as a result.
- Parameters:
transition (Transition) – The transition to take
- Raises:
InvalidTransitionError – The attacker is not actually able to take this transition.
- Return type:
None
- exception chouchen.system_graph.InvalidTransitionError
Raised when an attacker attempts to take a transition he is not actually able to.
- exception chouchen.system_graph.NoValidTransitionsError
Raised when an attacker has no valid moves left.
- class chouchen.system_graph.Node(machine, user, node_id=-1)
A class used to represent an attack position.
- machine
The name of the machine corresponding to the attack position.
- Type:
str
- user
The name of the user corresponding to the attack position.
- Type:
str
- node_id
A unique node identifier, generated automatically for every node created.
- Type:
int
- class chouchen.system_graph.SystemGraph(nodes, starting_nodes, winning_nodes, transitions)
A class used to represent an attack scenario through a System Graph.
- transitions
The list of all transitions available between attack positions.
- Type:
list[Transition]
- add_node_to_scenario(machine, user, is_starting, is_winning)
Adds a node to the scenario. This node will initially have no transitions attached. Useful for GUI applications.
- Parameters:
machine (
str) – the machine name of the attack position.user (
str) – the user name of the attack position.is_starting (
bool) – whether this is a starting node.is_winning (
bool) – whether this is a winning node.
- Return type:
None
- add_transition_to_scenario(entry_node, exit_node, technique, required, rewarded)
Adds a transition to the scenario. Inputs is described as strings and converted into objects.
- Parameters:
entry_node (
str) – the entry node, written as “User, Machine”exit_node (
str) – the exit node, written as “User, Machine”technique (
str) – the associated technique, written as “TXXXX: Technique name”required (
str) – the required secrets, written as secret1, secret2, secret3… None for no secret.rewarded (
str) – the rewarded secrets, written as secret1, secret2, secret3… None for no secret.
- Return type:
None
- static empty_scenario()
Creates an empty SystemGraph object object.
- Return type:
- find_transition_index(transition)
Returns the index of the transition equal to the given argument. Returns -1 if not found. 2 transitions are equal if they have the same transition_id.
- Parameters:
transition (Transition) – a transition object
- Return type:
int- Returns:
The index of the transition in self.transitions
- find_transitions_requiring_secret(secret_name)
Gets all transitions where secret_name is a requirement. Useful for secret shenanigans later when we need to get all related users.
- Parameters:
secret_name (
str) – The name of the secret to find- Return type:
list[Transition]- Returns:
The list of all transitions where the secret_name is in the required secrets.
- get_new_node_id()
Returns the lowest node_id that hasn’t been used already.
- Return type:
int
- is_winnable()
Checks whether the scenario is winnable or not. This is done by bruteforcing every transition available until either the scenario is won or there is no transition left and by only taking transitions that lead to a different attack state. This also calculates a winning attack path at random (list of transitions taken by the attacker leading to his winning position).
- Return type:
Tuple[bool,list[Transition]]- Returns:
(True, list[Transitions]) if the scenario is winnable, (False, []) otherwise.
- remove_node_from_scenario(node)
Removes a node from the scenario based on a string representation. Note that this will also remove any transition associated with that node.
- Parameters:
node (
Node) – a Node
- remove_transition_from_scenario(transition)
Removes a transition from the scenario based on a string representation.
- Parameters:
transition (
Transition) – a transition
- class chouchen.system_graph.Transition(entry_node, exit_node, technique, requires=[], rewards=[], procedure=None)
A class used to represent an transition on the technical level.
- technique
An ATT&CK technique, represented by its technique number (such as T1021).
- Type:
str
- procedure
A procedure instantiating the technique.
- Type:
str
- requires
A list of secrets required for the transition, can be empty.
- Type:
list[str]
- rewards
A list of secrets rewarded by the transition, can be empty.
- Type:
list[str]
- transition_id
A unique transition identifier, generated automatically for every transition.
- Type:
int
- chouchen.system_graph.export_scenario_as_json(sys_graph, output_file_name)
Exports a SystemGraph object as a json file, which may be imported in the future using import_scenario_as_json.
- Parameters:
sys_graph (SystemGraph) – The scenario to export.
output_file_name (str) – The name of the output json file. Will be exported in the ../json directory.
- Return type:
None
- chouchen.system_graph.get_scenario_difficulty(scenario)
Gets the difficulty of a given scenario by summing the difficulty score of all its transitions that already have transitions attached to them.
- Parameters:
scenario (
SystemGraph) – the scenario to check.- Return type:
int- Returns:
The difficulty score.
- chouchen.system_graph.import_scenario_from_json(json_file_path)
Imports a json file into a SystemGraph object. TODO: check if this still works. :type json_file_path:
str:param json_file_path: The path of the json file to import. :type json_file_path: str- Return type:
- Returns:
A SystemGraph object, corresponding to the information stored in json_file_path.
- Raises:
FileNotFoundError – The inputted file path is not a json file
chouchen.graphics
Graphics utility module.
This is used to convert a scenario described using the SystemGraph class and model into a proper graph representation.
- Options include:
Showing technique names alongside their number on the transitions.
Show attacker state.
Show procedure names instead of technique names.
Save the graph at a given location.
In particular, refining a scenario will call this module in order to provide a graphical aid on which procedures were taken.
- chouchen.graphics.draw_graph(sys_graph, attack_state=Nodes : [], Transitions : [], Secrets : [], technique_names=False, proc_mode=False, save_mode=None, hide_requires_rewards=False)
Provides a graphical representation of the scenario, showing off all attack positions and transitions alongside their attack techniques. Starting and winning nodes are also indicated.
If an attack state is provided, nodes controlled and transitions taken by the attacker will be indicated, alongside the list of secrets acquired by the attacker.
- Parameters:
sys_graph (<module ‘chouchen.system_graph’ from ‘/home/docs/checkouts/readthedocs.org/user_builds/ursid/checkouts/latest/chouchen/system_graph.py’>) – The scenario to graphically represent.
attack_state (
AttackState) – An optional attack state.technique_names (
bool) – Show full technique names if available (default: False)proc_mode (
bool) – Show procedure names instead of technique names (default: False)save_mode (str) – Saves the graph at this location instead of showing if this parameter exists (default: None)
hide_requires_rewards (
bool) – Does not show secret information on the graph (requires, rewards, attacker information)
- Return type:
None
Returns:
chouchen.constraints
Architectural constraint definitions.
This module codifies what an architectural constraint is using the ArchitecturalConstraint class. An ArchitecturalConstraint object is composed of 4 constraints:
OSConstraint, a class responsible for operating system conditions applying to procedures, including its version and type.
AccountConstraint, the same for accounts, including its name, group, privilege, credentials and services.
SoftwareConstraint, the same for software, including its version and type.
FileConstraint, the same for files, including its path, permissions and content.
This module is also responsible for SecretPrecondition objects, which make sure that procedures are picked accordingly to the technical scenario.
Once procedures are loaded from their json files, their corresponding ArchitecturalConstraint objects are generated. They are then combined according to our refinement backtracking algorithm.
- class chouchen.constraints.AccountConstraints(name, group='*', privilege='*', credentials={'Credential Type': '*', 'Requires Secret': False}, services=None)
A class to represent account constraints and their compatibilities. Account constraints consist of a name (“Alice”, “root” …), group (“Bob”, “root” …), permissions (“USER” or “SUPERUSER”) and credentials (“WEAK_RANDOM_PASSWORD”, …). TODO: list of acceptable credential types. .. attribute:: name
the name constraint.
- type:
subconstraints.AccountName
- group
the group constraint.
- Type:
subconstraints.AccountGroup
- privilege
the permission constraint.
- Type:
str
- credentials
the credentials constraint.
- Type:
dict[str]
- services
the service constraint
- Type:
list[str]
- assigned_secrets
the list of assigned secrets
- Type:
list[str]
- Examples:
>>> credential_dic = {"Credential Type": "RANDOM_WEAK_PASSWORD", "Requires Secret": False} >>> alice_weak_password = AccountConstraints("alice", group = "alice", credentials=credential_dic)
- static fuse_duplicates(first, other)
Fuses 2 entries with the same user name. This is done by getting the smallest constraint for each of Group, Privilege and Credentials (usually the one that is not a wildcard). :type first:
AccountConstraints:param first: the first contraint to be fused :type first: AccountConstraints :type other:AccountConstraints:param other: the constraint to fuse with. :type other: AccountConstraintsReturns: The fused entry
- Return type:
- is_compatible(other)
Checks if 2 account constraints are compatible with each other, i.e that they may coexist on the same architecture.
2 account constraints are comaptible if: - They have different names. - They have compatible group, permissions and credentials subconstraints.
- Parameters:
other (AccountConstraints) – the constraint to check against.
- Return type:
bool- Returns:
True if the 2 constraints are compatible, False otherwise.
- set_assigned_secrets(lsecrets)
Sets the list of secrets assignated to the constraint to the given value.
- Parameters:
lsecrets (list[str]) – The list of secrets to set.
- to_dic(secret_dictionary)
Convert the AccountConstraints object into a format compatible with our final configuration file. This requires the secret dictionary in argument in order to generate the credentials.
- Return type:
dict- Returns:
a dictionnary representing the constraint
- class chouchen.constraints.ArchitecturalConstraints(proc_dic, transition, machine_name=None, user_name=None)
- A class to represent procedure constraints and their compatibilities. Procedure constraints consist of
OS constraints, Account constraints, Software constraints and File constraints. A instance of this class may be created by providing it a procedure stored in a dictionary format.
- os_constraints
the list of OS constraints.
- Type:
- account_constraints
the list of Account constraints.
- Type:
- software_constraints
the list of Software constraints.
- Type:
- file_constraints
the list of File constraints.
- Type:
- assignated_secrets
the list of all secrets related to the transition associated to this procedure.
- Type:
list[str]
- assign_secret(secret_list)
Adds secrets to this architectural constraint. See README for more details on why this is done.
- Parameters:
secret_list (list) – the list of secrets to add.
- assign_secrets_to_subconstraints()
Assigns secrets to each account (and later file) constraints. This is called when the architectural constraint just got created and only contains information about a procedure’s constraints. There should not be account AND file constraints requiring secrets in a single procedure. First this gives secrets to each account constraint that require specific numbers of them. Then it gives the leftover secrets to the constraints with a wildcard amount of them (at least 1 for each). TODO: This function is currently very janky and unpractical. Needs a rewrite. This should be rewritten from scratch eventually, the concept of giving secrets to subconstraints seems sound though. IMPORTANT: IN PROCEDURES, FILES REQUIRING SECRETS SHOULD BE WRITTEN BEFORE FILES REQUIRING REWARDS THE ORDER MATTERS Returns:
- Return type:
None
- combine_constraints(other)
Combines 2 constraint objects by combining all of their subconstraints. This first checks whether the constraints are compatible, and will raise an error if not.
- Parameters:
other (ArchitecturalConstraints) – the constraint to fuse with.
- Return type:
- Returns:
An architectural constraint object combining both constraints.
- static default_unix_template_with_account(machine_name, user_name, machine_additional_config)
Creates a default ArchitecturalConstraints objects with a few software preinstalled.
The software are as follows: - rsync for file copy purposes - acl for script execution purposes
machine_additional_config is a dictionary. The relevant entry is [“options”], which contains strings.
- Return type:
- Returns:
An ArchitecturalConstraints object with every attribute empty except software.
- edit_special_cases(scenario)
Edits a few static values to their corresponding real value. For now this is only used for the /etc/shadow procedure, which requires setting the account constraint to a specific value which can only be found somewhere else in the scenario
- Parameters:
scenario (sg.SystemGraph) – the scenario
- static empty_constraint()
Creates an ArchitecturalConstraints object with no constraints inside.
- Return type:
- Returns:
An ArchitecturalConstraints object with every attribute empty.
- static empty_constraint_with_account(account_name)
Creates an ArchitecturalConstraints object with no constraints inside.
- Return type:
- Returns:
An ArchitecturalConstraints object with every attribute empty.
- fuse_duplicates()
Fuses duplicate entries in the architectural constraints. This is done by checking for every user constraints if an other user constraint has the same name, and combining them into one if so. All these constraints should be combinable - if not an incompatibility would have been raised This is called at the very end of the refinement (could be done at every step though if you want). This also fuses every entry in the OS constraint using the smae process - comparing every constraint and returning the smallest one.
- Return type:
None
- generate_credentials_and_files(secret_dictionary)
Goes through all the file and account constraints, in particular their FileContent et AccountCredentials subconstraints, and generates the appropriate secrets based on the secret names contained in self.assignated_secrets. For now this only works if the constraint has either an account or a file constraint requiring secrets (but not both). Reparting secrets when that is not the case is surprisingly annoying, but this should cover most cases. This function thus will give all secrets it has been assignated to the subconstraint it has. Then based on the value of secret_value, secret_type, credentials/content and the secret dictionary, this will call the secret and file generating module.
- Parameters:
secret_dictionary (dict) – the secret dictionary. Will be updated by this function.
- Return type:
list[dict]- Returns:
A list of all configuration dictionaries related to the generated secrets and files.
- static get_constraint_from_procedure_name(proc_name, transition, entry_or_exit)
Browses all procedure jsons (defined in ../json/procedures) looking for a procedure with the corresponding name, and returns the 2 ArchitecturalConstraint object corresponding to the entry and exit machine constraints. If several procedures share the same name (should NOT happen), only the first one will be returned.
- Parameters:
entry_or_exit (
str) – whether to return the entry or exit constraints of this proceduretransition (
Transition) – The related transitionproc_name (str) – The name of the procedure.
- Returns:
The 2 corresponding ArchitecturalConstraint objects.
- Return type:
corresponding_entry_proc, corresponding_exit_proc
- static get_dictionary_from_procedure_name(transition)
Gets the dictionary corresponding to a procedure constraint from its name. Raises an error if the name can’t be found in the ./json/fused_procedures.json file.
- Parameters:
transition (
Transition) – the related transition (contains a technique name).- Return type:
dict- Returns:
a dictionary containing the related procedure constraints.
- static get_list_from_procedure_pattern(transition)
Gets the list of all dictionaries corresponding to a procedure pattern from its name. Raises an error if noi pattern matching can’t be found in the ./json/fused_procedures.json file.
- Parameters:
transition (
Transition) – the related transition (contains a technique name).- Return type:
list[dict]- Returns:
a list of dictionaries containing the related procedures constraints.
- is_compatible(other)
2 architectural constraints are compatible if all their subconstraints are compatible with each other. :type other:
ArchitecturalConstraints:param other: the architectural constraint to check against. :type other: ArchitecturalConstraints- Return type:
bool- Returns:
True if the two constraints are compatible, False otherwise.
- is_empty()
Checks whether an architectural constraint is empty by checking the length of all its constraints.
- Return type:
bool- Returns:
True if the constraint is empty, false otherwise.
- is_user_in_constraint(user_name)
Checks whether a given username has an entry in the architectural constraints already. This is useful for initializing the refinement process.
- Parameters:
user_name (
str) – The name to look for- Return type:
bool
Returns: True if the name already exists, False otherwise.
- to_dic(name, secret_dictionary)
Converts all the architectural constraints into a dictionary which will be added to the final json output :type name:
str:param name: the name of the machine corresponding to these constraints :type name: str :type secret_dictionary:dict:param secret_dictionary: the secret dictionary :type secret_dictionary: dict- Return type:
dict- Returns:
A dictionary containing all information necessary to deploy this specific machine.
- exception chouchen.constraints.CombiningIncompatibleConstraintsError(constraint_1, constraint_2, message='Incompatible constraints')
Exception raised for errors when trying to combine ArchitecturalConstraints objects
- constraint_1
The first constraint.
- Type:
- constraint_2
The second constraint.
- Type:
- message
explanation of the error.
- Type:
str
- class chouchen.constraints.FileConstraints(path, perm, user, group, content)
A class to represent file constraints and their compatibilities. File constraints consist of a path constraint (where the file will be stored on the machine), permission constraints (written in the UNIX numbered format) and content constraints (hardcoded in subconstraints.FileContent). During the refinement process this will also remember which secret name in the scenario (ie the associated transition) is needed for the file construction (if there is any).
- path
the path of the file.
- Type:
str
- perm
the permissions required for the file.
- Type:
subconstraints.FilePermission
- content
a dictionary containing information related to the content of the file.
- Type:
dict
- assigned_secrets
the list of secrets associated to the file. Can be empty.
- Type:
list[str]
- machine_name
the name of the associated machine.
- Type:
str
- user_name
the name of the associated user.
- Type:
str
- is_compatible(other)
Checks if two file constraints objects are compatible.
2 file constraints are compatible if: - They link to different file paths. - They link to the same file path but have compatible permissions and content constraints. :type other:
FileConstraints:param other: the file constraint to check against. :type other: FileConstraints- Return type:
bool- Returns:
True if the two constraints are compatible, False otherwise.
- set_assigned_secrets(lsecrets)
Sets the list of associated secrets to this procedure.
- Parameters:
lsecrets (list[str]) – the list of assignated secrets.
- set_machine_name(machine_name)
Sets the machine_name attribute to a new value. TODO: remember what this is used for. :type machine_name:
str:param machine_name: the machine name to assign. :type machine_name: str
- set_user_name(user_name)
Sets the user_name attribute to a new value. TODO: remember what this is used for. :type user_name:
str:param user_name: the username to assign. :type user_name: str
- to_dic(secret_dictionary, already_generated=False)
Convert the FileConstraints object into a format compatible with our final configuration file NOTE: This will also generate the related file (which may generate secrets itself!). This function will actually be called twice. We do not want to regenerate files we already generated before, hence the already_generated parameter. :rtype:
dict:returns: a dictionnary representing the constraint- Parameters:
secret_dictionary (dict) – the secret dictionary. Necessary to generate credentials inside files.
already_generated (bool) – indicates whether files will need to be regenerated.
- Returns:
A dictionary representing the constraint.
- class chouchen.constraints.OSConstraints(os_type, os_version)
A class to represent OSConstraints and check their mutual compatibilities. OS constraints contain a type (Windows, Ubuntu…) and a version (<=20.04, ==16.04…).
Current limitations: - A procedure may only have a single OS constraint. If you want to have a procedure that can work on Ubuntu AND Debian, you will have to create 2 different procedures. - If you want to specify specific versions (such as 16.04 OR 20.04), you will have to write it as an interval instead (>=16.04, <=20.04).
- os_type
The type of the OS.
- Type:
str
- os_version str
The acceptable OS versions.
Examples
>>> ubuntu_1604 = OSConstraints("canonical:ubuntu_linux", "==16.04") >>> debian_10 = OSConstraints("debian:debian_linux", "==10.0")
- static default_os_constraint()
Creates a wildcard OS constraint, representing a lack of constraints. Used in fuse_duplicates.
- Return type:
- Returns:
a wildcard OS constraint, compatible with everything.
- static fuse_duplicates(first, other)
Fuses 2 OS entries that are compatible with each other This is done by getting the smallest constraint for each of Type and Version :type first:
OSConstraints:param first: the first contraint to be fused :type first: OSConstraints :type other:OSConstraints:param other: the constraint to fuse with. :type other: OSConstraintsReturns: The fused entry
- Return type:
- is_compatible(other)
Checks if two OS constraints are compatible with each other, i.e that they may coexist on the same architecture.
2 os constraints are compatible if: - They represent the same type of OS and have compatible versions. For instance Ubuntu <= 16.04 and Ubuntu == 16.04. - Their OS type are compatible and they have compatible versions. For instance Linux * and Ubuntu == 16.04.
- Parameters:
other (OSConstraints) – The constraint to compare against.
- Return type:
bool- Returns:
True if the 2 constraints are compatible, False otherwise.
Examples
>>> ubuntu_1604 = OSConstraints("canonical:ubuntu_linux", "==16.04") >>> ubuntu_2004 = OSConstraints("canonical:ubuntu_linux", "==20.04") >>> ubuntu_1604.is_compatible(ubuntu_2004) False
- to_dic()
Convert the OSConstraints object into a format compatible with our final configuration file. :rtype:
dict:returns: a dictionnary representing the constraint
- class chouchen.constraints.SecretPreconditions(requires_secrets, rewards_secrets)
A class handling secret preconditions, which limits which kind of procedures are eligible for a given transition.
For instance: - If a transition requires 1 secret and the procedure doesn’t require any, that procedure is inelegible. - If a transition requires 1 secret and the procedure may give * amount of them, that procedure is eligible. - If a transition requires 1 secret and the procedure may give 1 or more amount of them, that proc is eligible. - If a transition requires 1 secret that has already been generated as an SSH_KEY, and the procedure may give 1 PLAINTEXT_PASSWORD, that proc is ineligible. - If a transition requires 1 secret that has already been generated as an SSH_KEY, and the procedure may give 1 SSH_KEY, that proc is eligible.
- self.requires_secrets
a list of SecretPrecondition.
- self.rewards_secrets
a list of SecretPrecondition.
- is_eligible(transition, secret_dictionary)
Checks whether a procedure is eligible for a given transition.
This is done by checking: - If the number of secrets generated or required by the procedure matches the number of secrets generated or required by the transition. - If the type of secrets generated or required by the transition that have already been generated is a subset of the ones applicable to the procedure.
- Parameters:
transition (sg.Transition) – the transition to check against.
secret_dictionary (dict) – a dictionary containing all secrets that have already been generated.
- Return type:
bool
Returns:
- update_secret_dictionary_for_transition(secret_dictionary, transition, scenario)
Looks for all the secrets handled by the transition and checks that types are coherent for the secrets that already exist. Then checks that number matches. If all is correct, updates secret_dictionary with all the secrets that were not already in it. IMPORTANT: For now this assumes that procedures will at best require or reward a single type of secret. While it may be possible to handle more than one type and imagine procedures that could reward more than one type (for instance a text leak containing passwords AND ssh keys), it seems just less tedious to add a second transition to the graph instead. :type secret_dictionary:
dict:param secret_dictionary: the secret dictionary :type secret_dictionary: dict(secg.secret) :type transition:Transition:param transition: the transition being refined :type transition: sg.Transition :type scenario:SystemGraph:param scenario: the overall scenario. Useful to get user names related to the secret
- class chouchen.constraints.SoftwareConstraints(software_type, version, port=-1)
A class to represent software constraints and their compatibilities. Software constraints contain a type (ssh, windows_rdp …), a version (==1.8.2 …) and optionally a port (22 …).
- software_type
the software type.
- Type:
str
- version
the software versions.
- Type:
list[(int, str)])
- port
the port used by the software. Set to -1 if not initialized.
- Type:
int
Examples
>>> sudo_182 = SoftwareConstraints("sudo_project:sudo", "==1.8.27", -1)
- is_compatible(other)
Checks if two software constraints are compatible with each other, i.e that they may coexist on the same architecture.
2 software constraints are compatible if: - They represent the same type of software, have compatible versions and the same port. For instance sudo <= 1.9.5 and sudo >= 1.9.0, both with no ports. - They represent 2 different types of software and don’t use the same port. For instance ssh (port 22) and sudo (no port)
- Parameters:
other (SoftwareConstraints) – The constraint to check against.
- Return type:
bool- Returns:
True if the 2 constraints are compatible, False otherwise.
Examples
>>>
- to_dic()
Convert the SoftwareConstraints object into a format compatible with our final configuration file. This is done by converting the type, version and port to their respective configuration format, and outputting a dictionary containing everything.
- Return type:
dict- Returns:
a dictionnary representing the constraint
chouchen.file_generation
File generation module.
This module is responsible for generating files needed for the scenario, such as password leaks, copying entire directories, or writing flawed configuration files. Note that generating files may imply generating secrets that go along with them (for instance files containing credentials).
- Examples of file generation:
Files are copied directly from the static_files directory.
Files are entirely written here.
Files are naturally present on the machine, and only the line that needs to be changed will be generated here.
- The possible type of file edits are as follows:
WRITE: the file will be entirely copied onto the machine.
APPEND: the contents of the file will be added at the end of a file with the same path on the machine.
EDIT “text”: the contents of this file will replace the line containing “text” of a file with the same path.
Files are generated in the GENERATED_FILE_PATH folder, which by default is ./output/scenario_0/generated_files. If more than one scenario are generated (ie if that folder already exists), new files will be generated in ./output/scenario_i/generated_files, with scenario_i being the first available directory number.
- chouchen.file_generation.generate_file(file_constraint, secret_dictionary, machine_name, user_name, already_generated=False)
Generates a new file according to file_type and returns its local path and the type of file edit it will be. These have to be written manually for now. This may also reuse values within or update the secret dictionary - for instance if a file has to leak a password. If already_generated is True, the file will not be regenerated and only the location + edit values will be output.
- Parameters:
machine_name (
str) – The name of the related machine (useful to create directory)user_name (
str) – The name of the related user (useful for some files)secret_dictionary (
dict) – the dictionary of all secrets.file_constraint (
FileConstraints) – The type of file that needs to be generated.already_generated (
bool) – Indicates whether the file was generated already or not.
- Returns:
The location of the generated file and the edit to make to that file.