mal-lang / mal-simulator

Apache License 2.0
2 stars 1 forks source link

State does not change for intermediate steps of shortcut steps #22

Closed nkakouros closed 5 months ago

nkakouros commented 6 months ago

Suppose you have a MAL spec containing:

asset BridgeAsset {
  | bridging_step
    -> target_assets.new_step

asset MyAsset {
  | attack_step
    -> bridge_asset.target_assets.new_step

This means that, from MyAsset.attack_step, the attacker can reach new_step of another asset going through bridge_asset via a relationship (not shown in the example above). In the simulator, if the attacker agent selects new_step as the next action, the bridging_step in bridge_asset is not set to 1. This makes sense from the point of view of the MAL spec; bridging step is literally short-circuited.

But bridging_step does show up in the attack graph and the state dict and it never becomes 1, even if the attacker makes use of the bridge, which is confusing. It is confusing for example when visualizing the attack graph and the compromise state at each step. It may also affect the performance of RL agents, although it is a bit hazy for me to see this case all the way through.

If we want to support this (which would be great for my use case - visualization), it would be behind a flag (or not). The attack graph could have a hidden property, a dict, that keeps track of these short-circuits and, before setting the new state at each step, it could consult this dict to set the state of such shortcirtuited steps as well.

andrewbwm commented 6 months ago

In your example there is nothing to hint at that bridging_step is implied by attack_step. So, it doesn't actually bridge anything. The bridging asset does bridge something, but the attack step does not. If you wanted it to follow the same attack chain you could have just had:

asset MyAsset {
   | attack_step
     -> bridge_asset.bridging_step

I'm not entirely opposed to adding new features to the language, but I want us to be clear about it. What you found was not an oversight, it's intended behaviour. There are different ways of interpreting the relationship to bypasses/bridges/alternate paths, the attack steps aren't always a bridge, sometimes they are just alternative ways that do go directly from one asset to one that is multiple associations away.

I really dislike hidden properties and I think the semantics of the language is dense enough. We shouldn't overencumber it or might just collapse under its own weight.

Once again, I am not opposed to introducing features as we need them, I just want to make sure everyone is clear on both the current expected behaviour and the reasoning for the additions.

nkakouros commented 6 months ago

it doesn't actually bridge anything. The bridging asset does bridge something, but the attack step does not

I see what you are saying. I made a demo example:

#id: "org.mal-lang.coreLang"
#version: "0.0.0"

category general {
  asset BridgeAsset {
    | bridge_step
      -> targets.run
  }

  asset EntryAsset {
    | entry
      -> bridge.targets.run
  }

  asset Target {
    | run
  }
}

associations {
  EntryAsset [assets] 1 <-- QQQ --> 1 [bridge] BridgeAsset
  BridgeAsset [bridgee] 1 <-- BA --> * [targets] Target
}
  0: EntryAsset
  1: BridgeAsset
  2: Target
associations:
  - metaconcept: QQQ
    assets: 0
    bridge: 1
  - metaconcept: BA
    bridgee: 1
    targets: 2
attacker:
  1:
    entry_points:
      0:
        attack_steps:
          - entry
    name: Attacker:8
metadata:
  info: Created by the mal-toolbox model python module.
  langID: org.mal-lang.coreLang
  langVersion: 1.0.0
  malVersion: 0.1.0-SNAPSHOT
  name: Petting Zoo Example Model

and in neo4j it looks like this:

graph(1)

The bridge_step in the graph is not accessible to the attacker. Would issue #9 cover this case as well and remove such steps from the attack graph?

andrewbwm commented 6 months ago

Yes, as already mentioned, what you are describing that you are seeing is both the expected and intended behaviour. As such, #9 would not impact directly. In some cases some of intermediary steps, of which you have none in your example, would disappear.

I think there is some sort of misunderstanding here. What is the reason for not changing the code to something like:

#id: "org.mal-lang.coreLang"
#version: "0.0.0"

category general {
  asset BridgeAsset {
    | bridge_step
      -> targets.run
  }

  asset EntryAsset {
    | entry
      -> bridge.bridge_step
  }

  asset Target {
    | run
  }
}

associations {
  EntryAsset [assets] 1 <-- QQQ --> 1 [bridge] BridgeAsset
  BridgeAsset [bridge] 1 <-- BA --> * [targets] Target
}

Maybe we should just discuss this in person/over Zoom, there seems to be some basic mismatch we need to clear up.

andrewbwm commented 5 months ago

Upon in person conversation it was determined that:

  1. This is primarily an issue where the target step is an AND step.
  2. There may be some workarounds with having the bridge_step be an AND step as well.
  3. The issue with the bridge step showing up in the attack graph if it is never used will not be an issue if we have pruning(#9) remove untraversable attack steps.