Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

environment_generation.py not working properly #23

Closed
jaromiru opened this issue Jun 23, 2021 · 9 comments
Closed

environment_generation.py not working properly #23

jaromiru opened this issue Jun 23, 2021 · 9 comments
Labels
bug Something isn't working

Comments

@jaromiru
Copy link
Contributor

jaromiru commented Jun 23, 2021

Environment instantiated with create_random_environment() from environment_generation.py is not working properly.

Minimal code:

from cyberbattle.simulation.environment_generation import create_random_environment
import cyberbattle.simulation.commandcontrol as commandcontrol

env = create_random_environment('test', 10)
c2 = commandcontrol.CommandControl(env)
c2.print_all_attacks()

Exception:

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-3-b4d0a458d7ea> in <module>
      4 env = create_random_environment('test', 10)
      5 c2 = commandcontrol.CommandControl(env)
----> 6 c2.print_all_attacks()

~/Documents/wrk/rrl/CyberBattleSim/notebooks/../cyberbattle/simulation/commandcontrol.py in print_all_attacks(self)
    101     def print_all_attacks(self) -> None:
    102         """Pretty print list of all possible attacks from all the nodes currently owned by the attacker"""
--> 103         return self._actuator.print_all_attacks()
    104 
    105     def run_attack(self,

~/Documents/wrk/rrl/CyberBattleSim/notebooks/../cyberbattle/simulation/actions.py in print_all_attacks(self)
    542     def print_all_attacks(self) -> None:
    543         """Pretty print list of all possible attacks from all the nodes currently owned by the attacker"""
--> 544         d.display(pd.DataFrame.from_dict(self.list_all_attacks()))  # type: ignore
    545 
    546 

~/Documents/wrk/rrl/CyberBattleSim/notebooks/../cyberbattle/simulation/actions.py in list_all_attacks(self)
    525     def list_all_attacks(self) -> List[Dict[str, object]]:
    526         """List all possible attacks from all the nodes currently owned by the attacker"""
--> 527         on_owned_nodes: List[Dict[str, object]] = [
    528             {'id': n['id'],
    529              'status': n['status'],

~/Documents/wrk/rrl/CyberBattleSim/notebooks/../cyberbattle/simulation/actions.py in <listcomp>(.0)
    530              'properties': self._environment.get_node(n['id']).properties,
    531              'local_attacks': self.list_local_attacks(n['id']),
--> 532              'remote_attacks': self.list_remote_attacks(n['id'])
    533              }
    534             for n in self.list_nodes() if n['status'] == 'owned']

~/Documents/wrk/rrl/CyberBattleSim/notebooks/../cyberbattle/simulation/actions.py in list_remote_attacks(self, node_id)
    507     def list_remote_attacks(self, node_id: model.NodeID) -> List[model.VulnerabilityID]:
    508         """Return list of all remote attacks that may be executed onto the specified node."""
--> 509         attacks: List[model.VulnerabilityID] = self.list_vulnerabilities_in_target(
    510             node_id, model.VulnerabilityType.REMOTE)
    511         return attacks

~/Documents/wrk/rrl/CyberBattleSim/notebooks/../cyberbattle/simulation/actions.py in list_vulnerabilities_in_target(self, target, type_filter)
    149         target_node_data: model.NodeInfo = self._environment.get_node(target)
    150 
--> 151         global_vuln: Set[model.VulnerabilityID] = {
    152             vuln_id
    153             for vuln_id, vulnerability in self._environment.vulnerability_library.items()

~/Documents/wrk/rrl/CyberBattleSim/notebooks/../cyberbattle/simulation/actions.py in <setcomp>(.0)
    153             for vuln_id, vulnerability in self._environment.vulnerability_library.items()
    154             if (type_filter is None or vulnerability.type == type_filter)
--> 155             and self._check_prerequisites(target, vulnerability)
    156         }
    157 

~/Documents/wrk/rrl/CyberBattleSim/notebooks/../cyberbattle/simulation/actions.py in _check_prerequisites(self, target, vulnerability)
    131         mapping = {i: ALGEBRA.TRUE if str(i) in node_flags else ALGEBRA.FALSE for i in expr.get_symbols()}
    132 
--> 133         is_true: bool = cast(boolean.Expression, expr.subs(mapping)).simplify() == ALGEBRA.TRUE
    134         return is_true
    135 

~/miniconda3/envs/cyberbattle/lib/python3.8/site-packages/boolean/boolean.py in simplify(self, sort)
   1185         # Create new instance of own class with canonical args.
   1186         # TODO: Only create new class if some args changed.
-> 1187         expr = self.__class__(*args)
   1188 
   1189         # Literalize before doing anything, this also applies De Morgan's Law

~/miniconda3/envs/cyberbattle/lib/python3.8/site-packages/boolean/boolean.py in __init__(self, arg1, arg2, *args)
   1466 
   1467     def __init__(self, arg1, arg2, *args):
-> 1468         super(AND, self).__init__(arg1, arg2, *args)
   1469         self.identity = self.TRUE
   1470         self.annihilator = self.FALSE

~/miniconda3/envs/cyberbattle/lib/python3.8/site-packages/boolean/boolean.py in __init__(self, arg1, arg2, *args)
   1132 
   1133     def __init__(self, arg1, arg2, *args):
-> 1134         super(DualBase, self).__init__(arg1, arg2, *args)
   1135 
   1136         # identity element for the specific operation.

~/miniconda3/envs/cyberbattle/lib/python3.8/site-packages/boolean/boolean.py in __init__(self, *args)
    943         self.operator = None
    944 
--> 945         assert all(isinstance(arg, Expression) for arg in args), \
    946             'Bad arguments: all arguments must be an Expression: %r' % (args,)
    947         self.args = tuple(args)

AssertionError: Bad arguments: all arguments must be an Expression: (FALSE, <class 'boolean.boolean._TRUE'>)

I have traced the error to this potential_linux_vulns:

    "SudoCaching":
    model.VulnerabilityInfo(
        description="Escalating privileges from poorly configured sudo on linux/unix machines",
        type=model.VulnerabilityType.REMOTE,
        URL="https://attack.mitre.org/techniques/T1206/",
        precondition=model.Precondition(f"Linux&(~{ADMINTAG})"),
        outcome=model.AdminEscalation(),
        rates=model.Rates(0, 1.0, 1.0))

The precondition is translated into AND(FALSE, NOT(FALSE)) which fails to be simplified with the boolean library.
The used workaround in actions.py (from bastikr/boolean.py#82) does not help:

ALGEBRA.TRUE.dual = type(ALGEBRA.FALSE)
ALGEBRA.FALSE.dual = type(ALGEBRA.TRUE)
@jaromiru
Copy link
Contributor Author

Temporarily fixed by replacing

precondition=model.Precondition(f"Linux&(~{ADMINTAG})"),

with

        precondition=model.Precondition(f"Linux&(~({ADMINTAG}&{ADMINTAG}))"),

@blumu
Copy link
Contributor

blumu commented Jun 24, 2021

@jaromiru I cannot repro. Could you confirm which version of boolean.py you have installed?
Here is what I have:

~/CyberBattleSim_public$ pip freeze | grep bool
boolean.py==3.8

Repro:

from cyberbattle.simulation.environment_generation import create_random_environment
import cyberbattle.simulation.commandcontrol as commandcontrol

env = create_random_environment('test', 10)
c2 = commandcontrol.CommandControl(env)
c2.print_all_attacks()

Output:

Empty DataFrame
Columns: []
Index: []

@jaromiru
Copy link
Contributor Author

That's weird. I have boolean.py==3.8. The error is present. When I fix it with the fix above, the output looks like:

  id status                                         properties                                  local_attacks  remote_attacks
0  5  owned  [Win7, PortSMBOpen, PortRDPOpen, DomainJoined,...  [UACME55, UACME52, UACME43, UACME61, UACME45]  [RDPBF, SMBBF]

Not an empty DataFrame.

Are you sure the import from cyberbattle.simulation.environment_generation import create_random_environment is importing from your ~/CyberBattleSim_public and not from somewhere else, maybe previously installed with pip install .?

@blumu
Copy link
Contributor

blumu commented Jun 24, 2021

Yes the import is loading from my source directory:

> print(commandcontrol.__file__)
/home/azureuser/CyberBattleSim_public/cyberbattle/simulation/commandcontrol.py

I am also using Python version 3.8.9, not sure if this matters. I'll keep trying to get a repro.

@jaromiru
Copy link
Contributor Author

jaromiru commented Jun 24, 2021

The problem is definitely within the boolean library.

import boolean

ALGEBRA = boolean.BooleanAlgebra()
ALGEBRA.parse('1&~1').simplify() # OK

ALGEBRA.TRUE.dual = type(ALGEBRA.FALSE)
ALGEBRA.FALSE.dual = type(ALGEBRA.TRUE)
ALGEBRA.parse('1&~1').simplify() # Exception
$ pip freeze | grep boolean
boolean.py==3.8

$ python -V
Python 3.8.10

blumu pushed a commit that referenced this issue Jun 24, 2021
@blumu
Copy link
Contributor

blumu commented Jun 24, 2021

I could repro but I had to first fix another bug in environment_generation.py where the agent was not installed on any node of the graph:

e287436#diff-d01efcd79fb6d8316931c9a4f8a9578bc9908c4c69f2c613e606db2ebff3944f

@blumu
Copy link
Contributor

blumu commented Jun 24, 2021

@jaromiru The main branch of the boolean.py package has an alternative fix for this bug. It's not yet released on PyPI though:
https://github.com/bastikr/boolean.py/pull/100/files

@blumu
Copy link
Contributor

blumu commented Jun 24, 2021

@jaromiru I have just verified that the fix in branch master of boolean.py resolves the issue. You do need to remove the other workaround from actions.py and model.py otherwise you'll still get the exception. Since the fix is not release yet on PyPi it needs to be installed from github with:

pip install boolean.py@git+https://github.com/bastikr/boolean.py/@master#egg=numpy_stubs

I'll send a PR with a fix soon.

blumu pushed a commit that referenced this issue Jun 24, 2021
@blumu blumu added the bug Something isn't working label Jun 24, 2021
@jaromiru
Copy link
Contributor Author

I could repro but I had to first fix another bug in environment_generation.py where the agent was not installed on any node of the graph:

e287436#diff-d01efcd79fb6d8316931c9a4f8a9578bc9908c4c69f2c613e606db2ebff3944f

Ouch, my bad. Forgot to mention that :-D

I'll get to test it on Monday.

blumu added a commit that referenced this issue Jun 24, 2021
@blumu blumu closed this as completed Jun 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants