├── .DS_Store ├── README.md ├── go.mod ├── go.sum ├── impacts_ransomware.json ├── main_codevuln.go ├── main_ransomware.go ├── probabilities_ransomware.json ├── probabilities_vulnerability.json └── risk ├── analysis ├── helper.go └── simulation.go ├── statistics └── distributions.go ├── types.go └── utils ├── gen.go ├── search.go └── timeadjust.go /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcdannyboy/DGWR/403dda789f9e9e1105b8621461c70a3c1920e2c5/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # *D*ont *G*amble *W*ith *R*isk 2 | 3 | **Don't Gamble With Risk (DGWR)** is a Monte Carlo simulation system designed for complex quantitative risk modeling, focusing on risks, costs, and benefits. Inspired by the FAIR (Factor Analysis of Information Risk) model and leveraging the principles of Monte Carlo simulations, DGWR offers the basis for a robust framework for analyzing and quantifying risk probabilities and impacts. This system could be particularly useful for organizations looking to make informed decisions based on quantitative risk analysis. 4 | 5 | ## Key Features 6 | 7 | - **Quantitative Risk Analysis**: DGWR provides a structured approach to quantifying risks, enabling an understanding of potential impacts in numerical terms. 8 | - **Monte Carlo Simulations**: By running simulations multiple times (e.g., 100,000 iterations), DGWR offers a comprehensive view of possible outcomes, helping to identify the probability of various events. 9 | - **Flexible Event Modeling**: The system supports complex event trees, allowing for the modeling of events with varying timeframes, probabilities, impacts, and dependencies. 10 | 11 | ## Benefits of Quantitative Risk Analysis 12 | 13 | Quantitative risk analysis offers several advantages over qualitative analysis, including: 14 | 15 | - **Objective Decision Making**: By quantifying risks, organizations can make decisions based on data, reducing bias and subjectivity. 16 | - **Prioritization of Risks**: Quantitative analysis helps in identifying and prioritizing risks based on their potential impact, enabling more effective risk management strategies. 17 | - **Resource Allocation**: Understanding the numerical impact of risks allows for better allocation of resources to mitigate high-priority risks. 18 | 19 | ## Current Implementation and Future Directions 20 | 21 | Currently, DGWR utilizes the PERT (Program Evaluation and Review Technique) distribution for modeling the probabilities and impacts of each risk event. This choice was made to balance the need for accuracy with computational efficiency. However, the system's design allows for the integration of other distributions in the future, enhancing its flexibility and applicability to a wide range of scenarios. 22 | 23 | ## System Usage 24 | 25 | There are 2 main files which can be used as implementation examples. 26 | 27 | ### Ransomware Event Tree Example 28 | `main_ransomware.go` is an example of a complex phishing -> major ransomware event event tree. This file can be used as a reference for implementing event trees and using the DGWR system to run simulations and analyze the results. This file also contains detailed code comments for the qualification and explanation of input variables. 29 | 30 | This example also comes with 2 pre-generated output files: `probabilities_ransomware.json` and `impacts_ransomware.json` which contain the probabilities and impacts of the ransomware event tree. 31 | 32 | ### Code Vulnerability Event Tree Example 33 | `main_codevuln.go` contains a much more simplistic example of a code vulnerability event tree. This file can be used as a reference for implementing event trees and using the DGWR system to run simulations and analyze the results. 34 | 35 | This example comes with a single pre-generated output file, `probabilities_vulnerability.json` which contains the probabilities of the code vulnerability event tree nodes. -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bcdannyboy/dgws 2 | 3 | go 1.19 4 | 5 | require ( 6 | golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect 7 | gonum.org/v1/gonum v0.14.0 // indirect 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= 2 | golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= 3 | gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= 4 | gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= 5 | -------------------------------------------------------------------------------- /impacts_ransomware.json: -------------------------------------------------------------------------------- 1 | { 2 | "Behavioral Anomaly Alert": -10, 3 | "Host Control Alert": -10, 4 | "Network Control Alert": -10, 5 | "Phishing Email Detected": -1, 6 | "Phishing Email Received": 25, 7 | "Phishing Email Reported": -1 8 | } -------------------------------------------------------------------------------- /main_codevuln.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "math/rand" 7 | "os" 8 | "time" 9 | 10 | "github.com/bcdannyboy/dgws/risk" 11 | "github.com/bcdannyboy/dgws/risk/analysis" 12 | "github.com/bcdannyboy/dgws/risk/utils" 13 | ) 14 | 15 | func main() { 16 | rand.Seed(time.Now().UnixNano()) 17 | 18 | // Assuming IDs are generated correctly 19 | vulnerabilityIntroductionID, err := utils.GenerateID() 20 | if err != nil { 21 | panic(fmt.Errorf("error generating ID for vulnerability introduction: %w", err)) 22 | } 23 | vulnerabilityDiscoveryID, err := utils.GenerateID() 24 | if err != nil { 25 | panic(fmt.Errorf("error generating ID for vulnerability discovery: %w", err)) 26 | } 27 | vulnerabilityExploitID, err := utils.GenerateID() 28 | if err != nil { 29 | panic(fmt.Errorf("error generating ID for vulnerability exploit: %w", err)) 30 | } 31 | 32 | vulnerabilityIntroduction := &risk.Event{ 33 | ID: vulnerabilityIntroductionID, 34 | Name: "Vulnerability Introduction", 35 | Description: "New vulnerabilities are introduced into the codebase monthly.", 36 | Probability: &risk.Probability{ 37 | ExpectedFrequency: "monthly", 38 | Minimum: 0.27, 39 | MinimumConfidence: 0.9, 40 | Maximum: 0.27, 41 | MaximumConfidence: 0.9, 42 | }, 43 | } 44 | 45 | vulnerabilityDiscovery := &risk.Event{ 46 | ID: vulnerabilityDiscoveryID, 47 | Name: "Vulnerability Discovery", 48 | Description: "Some vulnerabilities are discovered before being exploited.", 49 | Probability: &risk.Probability{ 50 | ExpectedFrequency: "yearly", 51 | Minimum: 0.5, // Assuming a better scenario for discovery 52 | MinimumConfidence: 0.8, 53 | Maximum: 0.8, 54 | MaximumConfidence: 0.8, 55 | }, 56 | } 57 | 58 | vulnerabilityExploit := &risk.Event{ 59 | ID: vulnerabilityExploitID, 60 | Name: "Vulnerability Exploit", 61 | Description: "Undiscovered vulnerabilities may be exploited.", 62 | Probability: &risk.Probability{ 63 | ExpectedFrequency: "yearly", 64 | Minimum: 0.05, // Assuming lower exploit rate due to effective mitigations 65 | MinimumConfidence: 0.7, 66 | Maximum: 0.2, 67 | MaximumConfidence: 0.7, 68 | }, 69 | } 70 | 71 | events := []*risk.Event{vulnerabilityIntroduction, vulnerabilityDiscovery, vulnerabilityExploit} 72 | 73 | probabilityMap, _, err := analysis.MonteCarlo(events, 100000) 74 | if err != nil { 75 | panic(fmt.Errorf("error running Monte Carlo analysis: %w", err)) 76 | } 77 | 78 | outputResults(events, probabilityMap) 79 | } 80 | 81 | func outputResults(events []*risk.Event, probabilityMap map[int]float64) { 82 | ProbMap := make(map[string]float64) 83 | for k, v := range probabilityMap { 84 | for _, e := range events { 85 | if e.ID == k { 86 | ProbMap[e.Name] = v 87 | } 88 | } 89 | } 90 | 91 | probJSON, _ := json.MarshalIndent(ProbMap, "", " ") 92 | 93 | fmt.Println("Yearly Probabilities:", string(probJSON)) 94 | 95 | err := os.WriteFile("yearly_probabilities.json", probJSON, 0644) 96 | if err != nil { 97 | fmt.Println("Error writing yearly probabilities to file:", err) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /main_ransomware.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/bcdannyboy/dgws/risk" 9 | "github.com/bcdannyboy/dgws/risk/analysis" 10 | "github.com/bcdannyboy/dgws/risk/utils" 11 | ) 12 | 13 | // This is an example of a ransomware event tree stemming from a phishing email 14 | // The goal of this example is to show how the risk package can be used to model the probabilities and impacts of complex event trees 15 | // Each event in the tree has their own expected time frame, probability, impacts, and dependencies. 16 | // All timeframes are scaled up to a yearly basis. 17 | // 18 | // In this example, Beta distributions and Latin Hypercube Sampling are used to model the probabilities and impacts of each event. 19 | // 20 | // In the output, all probabilities and impacts are represented in a scaled yearly frequency basis, regardless of the original timeframe. 21 | // 22 | // +--------------------------------+ 23 | // | | 24 | // | Attempted Phishing Emails | 25 | // | | 26 | // +----------------|---------------+ 27 | // | 28 | // Anti-Phish Filter ------- | 29 | // \-------+ 30 | // | 31 | // | ------- Employee Reports Phish 32 | // +------/ 33 | // | 34 | // | 35 | // +----------------|---------------+ 36 | // | | 37 | // | Employee's Account Details | 38 | // | Are Successfully Phished | 39 | // | | 40 | // +----------------|---------------+ 41 | // | 42 | // +-- Behavioural Controls Identify -------- | 43 | // | Anomalous Account Activity \------+ 44 | // | | | | 45 | // | | | | 46 | // | | | +----------------|---------------+ 47 | // | | | | | 48 | // | | | | Employee Accepts Malicious | 49 | // | | | | Duo Prompt | 50 | // | | | | | 51 | // | | | +----------------|---------------+ 52 | // | | | | 53 | // | | | | ------- Host Based Controls Identify 54 | // | | --------------------------+------/ Malicious Activity or Code 55 | // | | | | | 56 | // | | | | | 57 | // | | +----------------|---------------+ | | 58 | // | | | | | | 59 | // | | | Threat Actor Establishes | | | 60 | // | | | Code Execution | | | 61 | // | | | | | | 62 | // | | +----------------|---------------+ | | 63 | // | +----------------------\ | | | 64 | // | -----\ | | | 65 | // | Network Controls Identify ------+---------------------+ | 66 | // | Command & Control Activity ------/ | | 67 | // | | | | 68 | // | | +----------------|---------------+ | 69 | // | | | | | 70 | // | | | Threat Actor Delivers | | 71 | // | | | Ransomware Payload | | 72 | // | | | | | 73 | // | | +----------------|---------------+ | 74 | // | | | | 75 | // | | | | 76 | // +-------------------------+----------------------+------------------------+ 77 | // | 78 | // | 79 | // +----------------|---------------+ 80 | // | | 81 | // | Ransomware Propogates on | 82 | // | Network Without Detection | 83 | // | | 84 | // +----------------|---------------+ 85 | // | 86 | // | 87 | // | 88 | // +----------------|---------------+ 89 | // | | 90 | // | MAJOR RANSOMWARE EVENT | 91 | // | | 92 | // +--------------------------------+ 93 | 94 | func main_ransomware() { 95 | 96 | // --- ID GENERATION, SKIP AHEAD TO SEE THE EVENT DEFINITIONS --- 97 | EmployeeGetsPhishedID, err := utils.GenerateID() 98 | if err != nil { 99 | panic(fmt.Errorf("error generating ID for phishing event: %s", err.Error())) 100 | } 101 | 102 | PhishingID, err := utils.GenerateID() 103 | if err != nil { 104 | panic(fmt.Errorf("error generating ID for phishing event: %s", err.Error())) 105 | } 106 | 107 | AntiPhishFilterID, err := utils.GenerateID() 108 | if err != nil { 109 | panic(fmt.Errorf("error generating ID for anti-phishing filter event: %s", err.Error())) 110 | } 111 | 112 | EmployeeReportsPhishID, err := utils.GenerateID() 113 | if err != nil { 114 | panic(fmt.Errorf("error generating ID for employee reports phishing event: %s", err.Error())) 115 | } 116 | 117 | EmployeeAcceptsMaliciousDuoPushID, err := utils.GenerateID() 118 | if err != nil { 119 | panic(fmt.Errorf("error generating ID for employee accepts malicious Duo Push event: %s", err.Error())) 120 | } 121 | 122 | BehavioralControlsCatchAnomalousAccountBehaviorID, err := utils.GenerateID() 123 | if err != nil { 124 | panic(fmt.Errorf("error generating ID for behavioral controls catch anomalous account behavior event: %s", err.Error())) 125 | } 126 | 127 | ThreatActorEstablishesCodeExecutionCapabilitiesID, err := utils.GenerateID() 128 | if err != nil { 129 | panic(fmt.Errorf("error generating ID for threat actor establishes code execution capabilities event: %s", err.Error())) 130 | } 131 | 132 | HostBasedControlsCatchMaliciousActivityOrCodeID, err := utils.GenerateID() 133 | if err != nil { 134 | panic(fmt.Errorf("error generating ID for host-based controls catch malicious activity or code event: %s", err.Error())) 135 | } 136 | 137 | NetworkBasedControlsCatchMaliciousCommandAndControlTrafficID, err := utils.GenerateID() 138 | if err != nil { 139 | panic(fmt.Errorf("error generating ID for network-based controls catch malicious command and control traffic event: %s", err.Error())) 140 | } 141 | 142 | ThreatActorDeliversRansomwarePayloadID, err := utils.GenerateID() 143 | if err != nil { 144 | panic(fmt.Errorf("error generating ID for threat actor delivers ransomware payload event: %s", err.Error())) 145 | } 146 | 147 | RansomwarePropogatesThroughoutNetworkWithoutDetectionID, err := utils.GenerateID() 148 | if err != nil { 149 | panic(fmt.Errorf("error generating ID for ransomware propogates throughout network without detection event: %s", err.Error())) 150 | } 151 | 152 | MajorRansomwareEventID, err := utils.GenerateID() 153 | if err != nil { 154 | panic(fmt.Errorf("error generating ID for major ransomware event: %s", err.Error())) 155 | } 156 | 157 | impactPhishingEmailsDetectedID, err := utils.GenerateID() 158 | if err != nil { 159 | panic(fmt.Errorf("error generating ID for phishing emails detected impact: %s", err.Error())) 160 | } 161 | 162 | impactPhishingEmailsReportedID, err := utils.GenerateID() 163 | if err != nil { 164 | panic(fmt.Errorf("error generating ID for phishing emails reported impact: %s", err.Error())) 165 | } 166 | 167 | impactBehavioralAnomalyAlertsID, err := utils.GenerateID() 168 | if err != nil { 169 | panic(fmt.Errorf("error generating ID for behavioral anomaly alerts impact: %s", err.Error())) 170 | } 171 | 172 | impactHostControlAlertsID, err := utils.GenerateID() 173 | if err != nil { 174 | panic(fmt.Errorf("error generating ID for host control alerts impact: %s", err.Error())) 175 | } 176 | 177 | impactNetworkControlAlertsID, err := utils.GenerateID() 178 | if err != nil { 179 | panic(fmt.Errorf("error generating ID for network control alerts impact: %s", err.Error())) 180 | } 181 | 182 | impactCompromisedAccountsID, err := utils.GenerateID() 183 | if err != nil { 184 | panic(fmt.Errorf("error generating ID for compromised accounts impact: %s", err.Error())) 185 | } 186 | 187 | impactMaliciousDuoPromptsAcceptedID, err := utils.GenerateID() 188 | if err != nil { 189 | panic(fmt.Errorf("error generating ID for malicious Duo prompts accepted impact: %s", err.Error())) 190 | } 191 | 192 | impactPhishingEmailsReceivedID, err := utils.GenerateID() 193 | if err != nil { 194 | panic(fmt.Errorf("error generating ID for phishing emails received impact: %s", err.Error())) 195 | } 196 | 197 | impactThreatActorsWithAccessID, err := utils.GenerateID() 198 | if err != nil { 199 | panic(fmt.Errorf("error generating ID for threat actors with access impact: %s", err.Error())) 200 | } 201 | 202 | impactMalwareOnNetworkID, err := utils.GenerateID() 203 | if err != nil { 204 | panic(fmt.Errorf("error generating ID for malware on network impact: %s", err.Error())) 205 | } 206 | 207 | impactLateralMovementEventsID, err := utils.GenerateID() 208 | if err != nil { 209 | panic(fmt.Errorf("error generating ID for lateral movement events impact: %s", err.Error())) 210 | } 211 | 212 | impactRebuildingNetworkID, err := utils.GenerateID() 213 | if err != nil { 214 | panic(fmt.Errorf("error generating ID for rebuilding network impact: %s", err.Error())) 215 | } 216 | 217 | impactCustomerRenumerationID, err := utils.GenerateID() 218 | if err != nil { 219 | panic(fmt.Errorf("error generating ID for customer renumeration impact: %s", err.Error())) 220 | } 221 | 222 | impactCustomerLossID, err := utils.GenerateID() 223 | if err != nil { 224 | panic(fmt.Errorf("error generating ID for customer loss impact: %s", err.Error())) 225 | } 226 | // --- EVENT DEFINITIONS --- 227 | 228 | // Controls 229 | AntiPhishFilter := &risk.Event{ 230 | ID: AntiPhishFilterID, 231 | Name: "Anti-Phishing Filter", 232 | Description: "An anti-phishing filter blocks a phishing email.", 233 | Probability: &risk.Probability{ 234 | // We are 90% confident that the anti-phishing filter will block a phishing email at least 80% of the time, and at most 90% of the time 235 | // We expect phishing emails very commonly, so we're considering these predictions on a weekly frequency 236 | ExpectedFrequency: "weekly", 237 | Minimum: 0.8, 238 | MinimumConfidence: 0.9, 239 | Maximum: 0.9, 240 | MaximumConfidence: 0.9, 241 | }, 242 | Impact: []*risk.Impact{ 243 | { 244 | // The Threat Detection & Response group wants to track phishing emails detected as a metric. 245 | // For each filter event, the team expects to detect exactly 1 phishing email, so the minimum and maximum individual unit impacts are both 1 246 | // The team is very confident in their predictions, but want to account for any weirdness that might happen, so they've marked both their minimum and maximum predictions at 90% confidence 247 | ImpactID: impactPhishingEmailsDetectedID, 248 | Name: "Phishing Emails Detected", 249 | Unit: "Phishing Email Detected", 250 | PositiveImpact: true, 251 | Description: "The number of phishing emails detected by the anti-phishing filter.", 252 | ExpectedFrequency: "weekly", 253 | MinimumIndividualUnitImpact: 1, 254 | MinimumIndividualUnitImpactConfidence: 0.9, 255 | MaximumIndividualUnitImpact: 1, 256 | MaximumIndividualUnitImpactConfidence: 0.9, 257 | MinimumImpactEvents: 1, 258 | MinimumImpactEventsConfidence: 0.9, 259 | MaximumImpactEvents: 1, 260 | MaximumImpactEventsConfidence: 0.9, 261 | }, 262 | }, 263 | } 264 | 265 | EmployeeReportsPhish := &risk.Event{ 266 | ID: EmployeeReportsPhishID, 267 | Name: "Employee Reports Phishing", 268 | Description: "An employee reports a phishing email.", 269 | Probability: &risk.Probability{ 270 | // Based on historical data, we find that employees report phishing emails at least 20% of the time, and at most 60% of the time 271 | // The team does phishing assessments once a quarter, so this data is on a quarterly frequency 272 | ExpectedFrequency: "quarterly", 273 | Minimum: 0.2, 274 | MinimumConfidence: 0.9, 275 | Maximum: 0.6, 276 | MaximumConfidence: 0.9, 277 | }, 278 | Impact: []*risk.Impact{ 279 | { 280 | // The Threat Detection & Response group wants to track phishing emails reported as a metric. 281 | // For each report event, the team expects to report exactly 1 phishing email, so the minimum and maximum individual unit impacts are both 1 282 | // The team is very confident in their predictions, but want to account for any weirdness that might happen, so they've marked both their minimum and maximum predictions at 90% confidence 283 | ImpactID: impactPhishingEmailsReportedID, 284 | Name: "Phishing Emails Reported", 285 | Unit: "Phishing Email Reported", 286 | PositiveImpact: true, 287 | Description: "The number of phishing emails reported by employees.", 288 | ExpectedFrequency: "quarterly", 289 | MinimumIndividualUnitImpact: 1, 290 | MinimumIndividualUnitImpactConfidence: 0.9, 291 | MaximumIndividualUnitImpact: 1, 292 | MaximumIndividualUnitImpactConfidence: 0.9, 293 | MinimumImpactEvents: 1, 294 | MinimumImpactEventsConfidence: 0.9, 295 | MaximumImpactEvents: 1, 296 | MaximumImpactEventsConfidence: 0.9, 297 | }, 298 | }, 299 | } 300 | 301 | BehavioralControlsCatchAnomalousAccountBehavior := &risk.Event{ 302 | ID: BehavioralControlsCatchAnomalousAccountBehaviorID, 303 | Name: "Behavioral Controls Catch Anomalous Account Behavior", 304 | Description: "Behavioral controls are triggered by anomalous account behavior.", 305 | Probability: &risk.Probability{ 306 | // The Threat Detection and Response team just recently got a new behavioral control set, they're not sure how well it will work, but they're confident it will catch anomalous account behavior at least 50% of the time, and at most 80% of the time 307 | // The team is less confident in their maximum prediction, so they've marked it at 80% confidence but they're more confident in their minimum prediction, so they've marked it at 90% confidence 308 | // The team expects to get alerts about potentially anomalous account behavior throughout each month but they're unsure of the exact frequency within any given month, so they've marked it as a monthly frequency 309 | ExpectedFrequency: "monthly", 310 | Minimum: 0.5, 311 | MinimumConfidence: 0.9, 312 | Maximum: 0.8, 313 | MaximumConfidence: 0.8, 314 | }, 315 | Impact: []*risk.Impact{ 316 | { 317 | // The Threat Detection & Response group wants to track behavioral anomaly alerts as a metric. 318 | // For each event, the team knows that the behavioral controls will generate anywhere between 1 and 10 alerts, so they've marked their minimum and maximum impact events impacts at 1 and 10 respectively 319 | // The team is very confident in their predictions, but want to account for any weirdness that might happen, so they've marked both their minimum and maximum predictions at 90% confidence 320 | ImpactID: impactBehavioralAnomalyAlertsID, 321 | Name: "Behavioral Anomaly Alerts", 322 | Unit: "Behavioral Anomaly Alert", 323 | PositiveImpact: true, 324 | Description: "The number of alerts generated by the behavioral controls.", 325 | ExpectedFrequency: "monthly", 326 | MinimumIndividualUnitImpact: 1, 327 | MinimumIndividualUnitImpactConfidence: 0.9, 328 | MaximumIndividualUnitImpact: 1, 329 | MaximumIndividualUnitImpactConfidence: 0.9, 330 | MinimumImpactEvents: 1, 331 | MinimumImpactEventsConfidence: 0.9, 332 | MaximumImpactEvents: 10, 333 | MaximumImpactEventsConfidence: 0.9, 334 | }, 335 | }, 336 | } 337 | 338 | HostBasedControlsCatchMaliciousActivityOrCode := &risk.Event{ 339 | ID: HostBasedControlsCatchMaliciousActivityOrCodeID, 340 | Name: "Host-Based Controls Catch Malicious Activity or Code", 341 | Description: "Host-based controls catch malicious activity or code.", 342 | Probability: &risk.Probability{ 343 | // The Threat Detection & Response team is confident that their host-based controls will catch malicious activity or code at least 60% of the time, and at most 90% of the time 344 | // The team is very confident in their predictions, as they've worked dilligently to test and implement these controls, so they've marked both their minimum and maximum predictions at 90% confidence 345 | // The team expects to get alerts about potentially malicious activity or code throughout each week, so they've marked it as a weekly frequency 346 | ExpectedFrequency: "weekly", 347 | Minimum: 0.6, 348 | MinimumConfidence: 0.9, 349 | Maximum: 0.90, 350 | MaximumConfidence: 0.9, 351 | }, 352 | Impact: []*risk.Impact{ 353 | { 354 | // The Threat Detection & Response group wants to track host control alerts as a metric. 355 | // For each event, the team knows that the host-based controls will generate between 1 and 10 alerts, so they've marked their minimum and maximum impact events impacts at 1 and 10 respectively 356 | // The team is very confident in their predictions, but want to account for any weirdness that might happen, so they've marked both their minimum and maximum predictions at 90% confidence 357 | ImpactID: impactHostControlAlertsID, 358 | Name: "Host Control Alerts", 359 | Unit: "Host Control Alert", 360 | PositiveImpact: true, 361 | Description: "The number of alerts generated by the host-based controls.", 362 | ExpectedFrequency: "weekly", 363 | MinimumIndividualUnitImpact: 1, 364 | MinimumIndividualUnitImpactConfidence: 0.9, 365 | MaximumIndividualUnitImpact: 1, 366 | MaximumIndividualUnitImpactConfidence: 0.9, 367 | MinimumImpactEvents: 1, 368 | MinimumImpactEventsConfidence: 0.9, 369 | MaximumImpactEvents: 10, 370 | MaximumImpactEventsConfidence: 0.9, 371 | }, 372 | }, 373 | } 374 | 375 | NetworkBasedControlsCatchMaliciousCommandAndControlTraffic := &risk.Event{ 376 | ID: NetworkBasedControlsCatchMaliciousCommandAndControlTrafficID, 377 | Name: "Network-Based Controls Catch Malicious Command and Control Traffic", 378 | Description: "Network-based controls catch malicious command and control traffic.", 379 | Probability: &risk.Probability{ 380 | // The Threat Detection & Response team is less confident in their network-based controls, as they have multiple known gaps 381 | // The team is confident that their network-based controls will catch malicious command and control traffic at least 40% of the time, and at most 70% of the time 382 | // The team is not very confident in their maximum prediction, so they've marked it at 60% confidence but they're more confident in their minimum prediction, so they've marked it at 80% confidence 383 | // The team expects to get alerts about potentially malicious command and control traffic throughout each month but they're unsure of the exact frequency within any given month, so they've marked it as a monthly frequency 384 | ExpectedFrequency: "monthly", 385 | Minimum: 0.4, 386 | MinimumConfidence: 0.8, 387 | Maximum: 0.7, 388 | MaximumConfidence: 0.6, 389 | }, 390 | Impact: []*risk.Impact{ 391 | { 392 | // The Threat Detection & Response group wants to track network control alerts as a metric. 393 | // For each event, the team knows that the network-based controls will generate between 1 and 10 alerts, so they've marked their minimum and maximum impact events impacts at 1 and 10 respectively 394 | // The team is very confident in their predictions, but want to account for any weirdness that might happen, so they've marked both their minimum and maximum predictions at 90% confidence 395 | ImpactID: impactNetworkControlAlertsID, 396 | Name: "Network Control Alerts", 397 | Unit: "Network Control Alert", 398 | PositiveImpact: true, 399 | Description: "The number of alerts generated by the network-based controls.", 400 | ExpectedFrequency: "monthly", 401 | MinimumIndividualUnitImpact: 1, 402 | MinimumIndividualUnitImpactConfidence: 0.9, 403 | MaximumIndividualUnitImpact: 1, 404 | MaximumIndividualUnitImpactConfidence: 0.9, 405 | MinimumImpactEvents: 1, 406 | MinimumImpactEventsConfidence: 0.9, 407 | MaximumImpactEvents: 10, 408 | MaximumImpactEventsConfidence: 0.9, 409 | }, 410 | }, 411 | } 412 | 413 | // Internal Employee Interaction Requirements 414 | EmployeeGetsPhished := &risk.Event{ 415 | ID: EmployeeGetsPhishedID, 416 | Name: "Employee Falls for Phishing Email", 417 | Description: "An employee falls for a phishing email.", 418 | Probability: &risk.Probability{ 419 | // Based on historical data, the team knows employees report phishing between 20% and 60% of the time 420 | // This means that employees fall for phishing emails between 40% and 80% of the time 421 | // since the team also wants to account for simply not reporting phishing emails, they've marked down their confidence to 70% for both their minimum and maximum predictions 422 | // The team does phishing assessments once a quarter, so this data is on a quarterly frequency 423 | ExpectedFrequency: "quarterly", 424 | Minimum: 0.2, 425 | MinimumConfidence: 0.7, 426 | Maximum: 0.6, 427 | MaximumConfidence: 0.7, 428 | }, 429 | Impact: []*risk.Impact{ 430 | { 431 | // The Threat Detection & Response group wants to estimate the number of compromised accounts as a result of successful phishing emails. 432 | // on average users have between 1 and 3 accounts when the team considers non-unique IDs or segmented admin accounts. 433 | // The team is very confident in their predictions, but want to account for any weirdness that might happen, so they've marked both their minimum and maximum predictions at 90% confidence 434 | // The team knows that users often re-use passwords so they think anywhere from 1 to 3 accounts can be compromised by a single phishing email 435 | // The team is a bit hopeful that password re-use isn't SO common so they've marked their maximum individual unit impact confidence at 70% 436 | ImpactID: impactCompromisedAccountsID, 437 | Name: "Compromised Accounts", 438 | Unit: "Compromised Account", 439 | PositiveImpact: false, 440 | Description: "The number of accounts compromised as a result of successful phishing emails.", 441 | ExpectedFrequency: "quarterly", 442 | MinimumIndividualUnitImpact: 1, 443 | MinimumIndividualUnitImpactConfidence: 0.9, 444 | MaximumIndividualUnitImpact: 3, 445 | MaximumIndividualUnitImpactConfidence: 0.7, 446 | MinimumImpactEvents: 1, 447 | MinimumImpactEventsConfidence: 0.9, 448 | MaximumImpactEvents: 3, 449 | MaximumImpactEventsConfidence: 0.9, 450 | }, 451 | }, 452 | Dependencies: []*risk.Dependency{ 453 | { 454 | // A phishing attempt has to happen for an employee to get phished 455 | DependsOnEventID: PhishingID, 456 | Happens: true, 457 | }, 458 | { 459 | // The anti-phishing filter has to not block the phishing email for the employee to get phished 460 | DependsOnEventID: AntiPhishFilterID, 461 | Happens: false, 462 | }, 463 | }, 464 | } 465 | 466 | EmployeeAcceptsMaliciousDuoPush := &risk.Event{ 467 | ID: EmployeeAcceptsMaliciousDuoPushID, 468 | Name: "Employee Accepts Malicious Duo Push", 469 | Description: "An employee accepts a malicious Duo Push notification.", 470 | Probability: &risk.Probability{ 471 | // The Threat Detection & Response team has not directly observed an employee accepting a malicious Duo Push notification, but they have observed employees accepting legitimate Duo Push notifications without thinking twice about it or verifying the request 472 | // The team thinks that an employee will accept a malicious Duo Push notification at least 30% of the time, and at most 70% of the time 473 | // The team is really unsure about their maximum prediction, so they've marked it at a 50% (meaning it could be a coin flip if they're right or wrong), but they're a bit more confident in their minimum prediction, so they've marked it at 70% confidence 474 | ExpectedFrequency: "yearly", 475 | Minimum: 0.3, 476 | MinimumConfidence: 0.7, 477 | Maximum: 0.7, 478 | MaximumConfidence: 0.5, 479 | }, 480 | Impact: []*risk.Impact{ 481 | { 482 | // The Threat Detection & Response group wants to estimate the number of accounts compromised as a result of accepting a malicious Duo Push notification. 483 | // The team knows that despite user's having multiple accounts, duo push notifications are only ever used to authenticate a single account 484 | // The team is very confident in their predictions, but want to account for any weirdness that might happen, so they've marked both their minimum and maximum predictions at 90% confidence 485 | ImpactID: impactMaliciousDuoPromptsAcceptedID, 486 | Name: "Malicious Duo Prompts Accepted", 487 | Unit: "Malicious Duo Prompt Accepted", 488 | PositiveImpact: false, 489 | Description: "The number of accounts compromised as a result of accepting a malicious Duo Push notification.", 490 | ExpectedFrequency: "yearly", 491 | MinimumIndividualUnitImpact: 1, 492 | MinimumIndividualUnitImpactConfidence: 0.9, 493 | MaximumIndividualUnitImpact: 1, 494 | MaximumIndividualUnitImpactConfidence: 0.9, 495 | MinimumImpactEvents: 1, 496 | MinimumImpactEventsConfidence: 0.9, 497 | MaximumImpactEvents: 1, 498 | MaximumImpactEventsConfidence: 0.9, 499 | }, 500 | }, 501 | Dependencies: []*risk.Dependency{ 502 | { 503 | // The employee has to get phished for them to accept a malicious Duo Push notification 504 | DependsOnEventID: EmployeeGetsPhishedID, 505 | Happens: true, 506 | }, 507 | { 508 | // The behavioral controls have to not catch the anomalous account behavior for the employee to accept a malicious Duo Push notification 509 | DependsOnEventID: BehavioralControlsCatchAnomalousAccountBehaviorID, 510 | Happens: false, 511 | }, 512 | }, 513 | } 514 | 515 | // Threat Actor Activities 516 | AttemptedPhishingEmail := &risk.Event{ 517 | ID: PhishingID, 518 | Name: "Phishing Attempt", 519 | Description: "A threat actor sends a phishing email.", 520 | Probability: &risk.Probability{ 521 | // The Threat Detection & Response team observes attempted phishing emails on a daily to every other day basis 522 | // The team is confident that they will observe attempted phishing emails at least 50% of the time, and at most 100% of the time (i.e. they either see some or they don't on any given day) 523 | ExpectedFrequency: "daily", 524 | Minimum: 0.5, 525 | MinimumConfidence: 0.9, 526 | Maximum: 1, 527 | MaximumConfidence: 0.9, 528 | }, 529 | Impact: []*risk.Impact{ 530 | { 531 | // The Threat Detection & Response group wants to estimate the number of phishing emails received as a result of attempted phishing emails. 532 | // The team knows that on average, users receive between 1 and 5 phishing emails a day, so they've marked their minimum and maximum impact events at 1 and 5 respectively 533 | // The team is very confident in their predictions, but want to account for any weirdness that might happen, so they've marked both their minimum and maximum predictions at 90% confidence 534 | ImpactID: impactPhishingEmailsReceivedID, 535 | Name: "Phishing Emails Received", 536 | Unit: "Phishing Email Received", 537 | PositiveImpact: false, 538 | Description: "The number of phishing emails received as a result of attempted phishing emails.", 539 | ExpectedFrequency: "daily", 540 | MinimumIndividualUnitImpact: 1, 541 | MinimumIndividualUnitImpactConfidence: 0.9, 542 | MaximumIndividualUnitImpact: 5, 543 | MaximumIndividualUnitImpactConfidence: 0.9, 544 | MinimumImpactEvents: 1, 545 | MinimumImpactEventsConfidence: 0.9, 546 | MaximumImpactEvents: 5, 547 | MaximumImpactEventsConfidence: 0.9, 548 | }, 549 | }, 550 | } 551 | 552 | ThreatActorEstablishesCodeExecutionCapabilities := &risk.Event{ 553 | ID: ThreatActorEstablishesCodeExecutionCapabilitiesID, 554 | Name: "Threat Actor Establishes Code Execution Capabilities", 555 | Description: "A threat actor establishes code execution capabilities on a system.", 556 | Probability: &risk.Probability{ 557 | // The Threat Detection & response team has never observed a threat actor establishing code execution capabilities and is pretty confident in their host defenses 558 | // However, they're not sure what the threat actor is capable of or what would really happen if they did establish code execution capabilities, so they're not very confident in their predictions 559 | // The team assessed this prediction on a yearly basis, as they don't expect to see this event very often 560 | ExpectedFrequency: "yearly", 561 | Minimum: 0.1, 562 | MinimumConfidence: 0.5, 563 | Maximum: 0.5, 564 | MaximumConfidence: 0.5, 565 | }, 566 | Impact: []*risk.Impact{ 567 | { 568 | // The Threat Detection & Response group wants to estimate the number of threat actors with access to the network as a result of establishing code execution capabilities. 569 | // The team knows that threat actors often sell access to networks, the team estimates between 1 and 5 unique threat actors will have access to the network but they're not very confident in their predictions 570 | // The team also knows that the threat actor cal sell access to the network multiple times, so they've marked their minimum and maximum impact events at 1 and 5 respectively 571 | ImpactID: impactThreatActorsWithAccessID, 572 | Name: "Threat Actors with Access", 573 | Unit: "Threat Actor", 574 | PositiveImpact: false, 575 | Description: "The number of threat actors with access to the network as a result of establishing code execution capabilities.", 576 | ExpectedFrequency: "yearly", 577 | MinimumIndividualUnitImpact: 1, 578 | MinimumIndividualUnitImpactConfidence: 0.9, 579 | MaximumIndividualUnitImpact: 5, 580 | MaximumIndividualUnitImpactConfidence: 0.5, 581 | MinimumImpactEvents: 1, 582 | MinimumImpactEventsConfidence: 0.9, 583 | MaximumImpactEvents: 5, 584 | MaximumImpactEventsConfidence: 0.5, 585 | }, 586 | }, 587 | Dependencies: []*risk.Dependency{ 588 | { 589 | // The employee has to accept a malicious Duo Push notification for the threat actor to establish code execution capabilities 590 | DependsOnEventID: EmployeeAcceptsMaliciousDuoPushID, 591 | Happens: true, 592 | }, 593 | { 594 | // the host-based controls have to not catch the malicious activity or code for the threat actor to establish code execution capabilities 595 | DependsOnEventID: HostBasedControlsCatchMaliciousActivityOrCodeID, 596 | Happens: false, 597 | }, 598 | { 599 | // The behavioral controls have to not catch the anomalous account behavior for the threat actor to establish code execution capabilities 600 | DependsOnEventID: BehavioralControlsCatchAnomalousAccountBehaviorID, 601 | Happens: false, 602 | }, 603 | }, 604 | } 605 | 606 | ThreatActorDeliversRansomwarePayload := &risk.Event{ 607 | ID: ThreatActorDeliversRansomwarePayloadID, 608 | Name: "Threat Actor Delivers Ransomware Payload", 609 | Description: "A threat actor delivers a ransomware payload to a system.", 610 | Probability: &risk.Probability{ 611 | // The Threat Detection and Response group is pretty sure that at between 60% and 90% of the time, when possible a threat group will deliver ransomware to a system 612 | // The team is confident in their predictions, so they've marked both their minimum and maximum predictions at 80% confidence 613 | // The team assessed this prediction on a yearly basis, as they don't expect to see this event very often 614 | ExpectedFrequency: "yearly", 615 | Minimum: 0.6, 616 | MinimumConfidence: 0.8, 617 | Maximum: 0.9, 618 | MaximumConfidence: 0.8, 619 | }, 620 | Impact: []*risk.Impact{ 621 | { 622 | // The Threat Detection & Response group wants to estimate the number of malware instances on the network as a result of a ransomware payload being delivered. 623 | // The team knows that threat actors may deliver ransomware to multiple systems at once or multiple different ransomware payloads to the same system 624 | // since the team believes on average users have access to between 1 and 5 systems, they've marked their minimum and maximum impact events at 1 and 5 respectively 625 | // The team has read that on average threat actors will drop between 1 and 3 different ransomware payloads on a system, so they've marked their minimum and maximum individual unit impacts at 1 and 3 respectively 626 | ImpactID: impactMalwareOnNetworkID, 627 | Name: "Malware on Network", 628 | Unit: "Malware Instance", 629 | PositiveImpact: false, 630 | Description: "The number of malware instances on the network as a result of a ransomware payload being delivered.", 631 | ExpectedFrequency: "yearly", 632 | MinimumIndividualUnitImpact: 1, 633 | MinimumIndividualUnitImpactConfidence: 0.9, 634 | MaximumIndividualUnitImpact: 3, 635 | MaximumIndividualUnitImpactConfidence: 0.6, 636 | MinimumImpactEvents: 1, 637 | MinimumImpactEventsConfidence: 0.9, 638 | MaximumImpactEvents: 5, 639 | MaximumImpactEventsConfidence: 0.9, 640 | }, 641 | }, 642 | Dependencies: []*risk.Dependency{ 643 | { 644 | // The threat actor has to establish code execution capabilities for them to deliver a ransomware payload 645 | DependsOnEventID: ThreatActorEstablishesCodeExecutionCapabilitiesID, 646 | Happens: true, 647 | }, 648 | { 649 | // The network-based controls have to not catch the malicious command and control traffic for the threat actor to deliver a ransomware payload 650 | DependsOnEventID: NetworkBasedControlsCatchMaliciousCommandAndControlTrafficID, 651 | Happens: false, 652 | }, 653 | { 654 | // The host-based controls have to not catch the malicious activity or code for the threat actor to deliver a ransomware payload 655 | DependsOnEventID: HostBasedControlsCatchMaliciousActivityOrCodeID, 656 | Happens: false, 657 | }, 658 | { 659 | // The behavioral controls have to not catch the anomalous account behavior for the threat actor to deliver a ransomware payload 660 | DependsOnEventID: BehavioralControlsCatchAnomalousAccountBehaviorID, 661 | Happens: false, 662 | }, 663 | }, 664 | } 665 | 666 | RansomwarePropogatesThroughoutNetworkWithoutDetection := &risk.Event{ 667 | ID: RansomwarePropogatesThroughoutNetworkWithoutDetectionID, 668 | Name: "Ransomware Propogates Throughout Network Without Detection", 669 | Description: "Ransomware propogates throughout the network without detection.", 670 | Probability: &risk.Probability{ 671 | // The Threat Detection and Response group is pretty sure that at between 10% and 40% of the time, ransomware will propogate throughout the network without detection 672 | // Overall they're pretty confident in their controls but they have some uncertainty and known gaps so they're not very confident in their predictions 673 | // The team assessed this prediction on a yearly basis, as they don't expect to see this event very often 674 | ExpectedFrequency: "yearly", 675 | Minimum: 0.1, 676 | MinimumConfidence: 0.6, 677 | Maximum: 0.4, 678 | MaximumConfidence: 0.6, 679 | }, 680 | Impact: []*risk.Impact{ 681 | { 682 | // The Threat Detection & Response group wants to estimate the number of lateral movement events as a result of ransomware propogating throughout the network without detection. 683 | // For any given event, the team believes ransomware can gain access to anywhere from 1 to 100 systems depending on what sort of access they identify 684 | // this is because the organization knows developers often keep keys, etc. on their systems or application servers 685 | // The team is very confident that at least 1 lateral movement event would occur, but they think theres not much chance of 100 systems being hit at once so they've marked their confidences accordingly 686 | // The team also thinks that ransomware can move laterally to multiple systems at once, so they've marked their minimum and maximum impact events at 1 and 100 respectively 687 | ImpactID: impactLateralMovementEventsID, 688 | Name: "Lateral Movement Events", 689 | Unit: "Lateral Movement Event", 690 | PositiveImpact: false, 691 | Description: "The number of lateral movement events as a result of ransomware propogating throughout the network without detection.", 692 | ExpectedFrequency: "yearly", 693 | MinimumIndividualUnitImpact: 1, 694 | MinimumIndividualUnitImpactConfidence: 0.9, 695 | MaximumIndividualUnitImpact: 100, 696 | MaximumIndividualUnitImpactConfidence: 0.5, 697 | MinimumImpactEvents: 1, 698 | MinimumImpactEventsConfidence: 0.9, 699 | MaximumImpactEvents: 100, 700 | MaximumImpactEventsConfidence: 0.5, 701 | }, 702 | }, 703 | Dependencies: []*risk.Dependency{ 704 | { 705 | // The threat actor has to deliver a ransomware payload for the ransomware to propogate throughout the network without detection 706 | DependsOnEventID: ThreatActorDeliversRansomwarePayloadID, 707 | Happens: true, 708 | }, 709 | { 710 | // The network-based controls have to not catch the malicious command and control traffic for the ransomware to propogate throughout the network without detection 711 | DependsOnEventID: NetworkBasedControlsCatchMaliciousCommandAndControlTrafficID, 712 | Happens: false, 713 | }, 714 | { 715 | // The host-based controls have to not catch the malicious activity or code for the ransomware to propogate throughout the network without detection 716 | DependsOnEventID: HostBasedControlsCatchMaliciousActivityOrCodeID, 717 | Happens: false, 718 | }, 719 | { 720 | // The behavioral controls have to not catch the anomalous account behavior for the ransomware to propogate throughout the network without detection 721 | DependsOnEventID: BehavioralControlsCatchAnomalousAccountBehaviorID, 722 | Happens: false, 723 | }, 724 | }, 725 | } 726 | 727 | // Top Level Event 728 | MajorRansomwareEvent := &risk.Event{ 729 | ID: MajorRansomwareEventID, 730 | Name: "Major Ransomware Event", 731 | Description: "A major ransomware event occurs.", 732 | Probability: &risk.Probability{ 733 | // The team is pretty sure a major ransomware outbreak would be detected most of the time before it gets our of hand, but they're not 100% sure 734 | // The team has low confidence in their predictions but they're pretty sure that a major ransomware event would be detected at least 70% of the time, and at most 90% of the time 735 | // The team assessed this prediction on a yearly basis, as they don't expect to see this event very often 736 | ExpectedFrequency: "yearly", 737 | Minimum: 0.1, 738 | MinimumConfidence: 0.1, 739 | Maximum: 0.3, 740 | MaximumConfidence: 0.1, 741 | }, 742 | Impact: []*risk.Impact{ 743 | { 744 | // The organization wants to estimate the cost of rebuilding the network as a result of a major ransomware event. 745 | // The team knows that rebuilding the network will cost between $1,000,000 and $10,000,000 based on the size of the organization and the amount of data lost 746 | // The team is confident in their predictions 747 | ImpactID: impactRebuildingNetworkID, 748 | Name: "Rebuilding Network", 749 | Unit: "USD", 750 | PositiveImpact: false, 751 | Description: "The cost of rebuilding the network as a result of a major ransomware event.", 752 | ExpectedFrequency: "yearly", 753 | MinimumIndividualUnitImpact: 1000000, 754 | MinimumIndividualUnitImpactConfidence: 0.9, 755 | MaximumIndividualUnitImpact: 10000000, 756 | MaximumIndividualUnitImpactConfidence: 0.9, 757 | MinimumImpactEvents: 1, 758 | MinimumImpactEventsConfidence: 0.9, 759 | MaximumImpactEvents: 1, 760 | MaximumImpactEventsConfidence: 0.9, 761 | }, 762 | { 763 | // The organization wants to estimate the cost of customer renumeration as a result of a major ransomware event. 764 | // The organization estimates that between 1 and 25000 customers would need to be renumerated based on the amount of data lost 765 | // The organization estimates renumeration costs per customer at between $100 and $1000 766 | // The team is fairly confident in their predictions 767 | ImpactID: impactCustomerRenumerationID, 768 | Name: "Customer Renumeration", 769 | Unit: "USD", 770 | PositiveImpact: false, 771 | Description: "The cost of customer renumeration as a result of a major ransomware event.", 772 | ExpectedFrequency: "yearly", 773 | MinimumIndividualUnitImpact: 100, 774 | MinimumIndividualUnitImpactConfidence: 0.7, 775 | MaximumIndividualUnitImpact: 1000, 776 | MaximumIndividualUnitImpactConfidence: 0.7, 777 | MinimumImpactEvents: 1, 778 | MinimumImpactEventsConfidence: 0.7, 779 | MaximumImpactEvents: 25000, 780 | MaximumImpactEventsConfidence: 0.7, 781 | }, 782 | { 783 | // The organization wants to estimate the cost of customer loss as a result of a major ransomware event. 784 | // The organization estimates that between 1 and 10000 customers would stop using the service based on the amount of data lost 785 | // Each loss would cost the organization between $10 and $100 depending on the customer's subscription level 786 | // subscriptions are paid on a monthly basis 787 | // The team is fairly confident in their customer predictions but knows the subscription costs of their customers are set by the organization so they are very confident in their subscription cost predictions 788 | ImpactID: impactCustomerLossID, 789 | Name: "Customer Loss", 790 | Unit: "USD", 791 | PositiveImpact: false, 792 | Description: "The cost of customer loss as a result of a major ransomware event.", 793 | ExpectedFrequency: "monthly", 794 | MinimumIndividualUnitImpact: 10, 795 | MinimumIndividualUnitImpactConfidence: 1, 796 | MaximumIndividualUnitImpact: 100, 797 | MaximumIndividualUnitImpactConfidence: 1, 798 | MinimumImpactEvents: 1, 799 | MinimumImpactEventsConfidence: 0.7, 800 | MaximumImpactEvents: 10000, 801 | MaximumImpactEventsConfidence: 0.7, 802 | }, 803 | }, 804 | Dependencies: []*risk.Dependency{ 805 | { 806 | // The ransomware has to propogate throughout the network without detection for a major ransomware event to occur 807 | DependsOnEventID: RansomwarePropogatesThroughoutNetworkWithoutDetectionID, 808 | Happens: true, 809 | }, 810 | }, 811 | } 812 | 813 | // --- RISK ASSESSMENT --- 814 | 815 | Events := []*risk.Event{ 816 | AntiPhishFilter, 817 | EmployeeReportsPhish, 818 | BehavioralControlsCatchAnomalousAccountBehavior, 819 | HostBasedControlsCatchMaliciousActivityOrCode, 820 | NetworkBasedControlsCatchMaliciousCommandAndControlTraffic, 821 | EmployeeGetsPhished, 822 | EmployeeAcceptsMaliciousDuoPush, 823 | AttemptedPhishingEmail, 824 | ThreatActorEstablishesCodeExecutionCapabilities, 825 | ThreatActorDeliversRansomwarePayload, 826 | RansomwarePropogatesThroughoutNetworkWithoutDetection, 827 | MajorRansomwareEvent, 828 | } 829 | 830 | ProbabilityMap, ImpactMap, err := analysis.MonteCarlo(Events, 100_000) 831 | 832 | if err != nil { 833 | panic(fmt.Errorf("error running monte carlo analysis: %w", err)) 834 | } 835 | 836 | ProbMap := make(map[string]float64) 837 | for k, v := range ProbabilityMap { 838 | for _, e := range Events { 839 | if e.ID == k { 840 | ProbMap[e.Name] = v 841 | } 842 | } 843 | } 844 | 845 | jProbabilityMap, err := json.Marshal(ProbMap) 846 | if err != nil { 847 | panic(err) 848 | } 849 | 850 | jImpactMap, err := json.Marshal(ImpactMap) 851 | if err != nil { 852 | panic(err) 853 | } 854 | 855 | pmf_file, err := os.Create("probabilities.json") 856 | if err != nil { 857 | panic(err) 858 | } 859 | 860 | _, err = pmf_file.Write(jProbabilityMap) 861 | if err != nil { 862 | panic(err) 863 | } 864 | 865 | impact_file, err := os.Create("impacts.json") 866 | if err != nil { 867 | panic(err) 868 | } 869 | 870 | _, err = impact_file.Write(jImpactMap) 871 | if err != nil { 872 | panic(err) 873 | } 874 | 875 | } 876 | -------------------------------------------------------------------------------- /probabilities_ransomware.json: -------------------------------------------------------------------------------- 1 | { 2 | "Anti-Phishing Filter": 1, 3 | "Behavioral Controls Catch Anomalous Account Behavior": 1, 4 | "Employee Accepts Malicious Duo Push": 0, 5 | "Employee Falls for Phishing Email": 0, 6 | "Employee Reports Phishing": 0.95966, 7 | "Host-Based Controls Catch Malicious Activity or Code": 1, 8 | "Major Ransomware Event": 0, 9 | "Network-Based Controls Catch Malicious Command and Control Traffic": 1, 10 | "Phishing Attempt": 1, 11 | "Ransomware Propogates Throughout Network Without Detection": 0, 12 | "Threat Actor Delivers Ransomware Payload": 0, 13 | "Threat Actor Establishes Code Execution Capabilities": 0 14 | } -------------------------------------------------------------------------------- /probabilities_vulnerability.json: -------------------------------------------------------------------------------- 1 | { 2 | "Vulnerability Discovery": 0.52621, 3 | "Vulnerability Exploit": 0.05204, 4 | "Vulnerability Introduction": 1 5 | } -------------------------------------------------------------------------------- /risk/analysis/helper.go: -------------------------------------------------------------------------------- 1 | package analysis 2 | 3 | func weightedAverage(min, max, minConf, maxConf float64) float64 { 4 | return (min*minConf + max*maxConf) / (minConf + maxConf) 5 | } 6 | 7 | func averageConfidence(minConf, maxConf float64) float64 { 8 | return (minConf + maxConf) / 2.0 9 | } 10 | 11 | // adjustStddevBasedOnConfidence adjusts the standard deviation based on the confidence levels. 12 | func adjustStddevBasedOnConfidence(min, max, minConf, maxConf float64) float64 { 13 | rangeVal := max - min 14 | avgConf := (minConf + maxConf) / 2 15 | // This is a heuristic: lower confidence means higher uncertainty, thus larger standard deviation. 16 | return rangeVal * (1 - avgConf) / 2 17 | } 18 | 19 | func max(a, b float64) float64 { 20 | if a > b { 21 | return a 22 | } 23 | return b 24 | } 25 | 26 | func clampProbability(p float64) float64 { 27 | if p < 0 { 28 | return 0 29 | } 30 | if p > 1 { 31 | return 1 32 | } 33 | return p 34 | } 35 | 36 | func calcAvg(values []float64) float64 { 37 | var sum float64 38 | for _, v := range values { 39 | sum += v 40 | } 41 | return sum / float64(len(values)) 42 | } 43 | 44 | func clamp(value, min, max float64) float64 { 45 | if value < min { 46 | return min 47 | } 48 | if value > max { 49 | return max 50 | } 51 | return value 52 | } 53 | 54 | // weightedAverageWithConfidence calculates a weighted average of min and max values based on their confidences. 55 | func weightedAverageWithConfidence(min, max, minConf, maxConf float64) float64 { 56 | weightedMin := min * minConf 57 | weightedMax := max * maxConf 58 | totalConf := minConf + maxConf 59 | if totalConf == 0 { // Avoid division by zero. 60 | return (min + max) / 2 61 | } 62 | return (weightedMin + weightedMax) / totalConf 63 | } 64 | -------------------------------------------------------------------------------- /risk/analysis/simulation.go: -------------------------------------------------------------------------------- 1 | package analysis 2 | 3 | import ( 4 | "math" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/bcdannyboy/dgws/risk" 9 | "github.com/bcdannyboy/dgws/risk/statistics" 10 | "github.com/bcdannyboy/dgws/risk/utils" 11 | ) 12 | 13 | // UpdateEventProbabilityWithDependency updates the event probability based on the outcome of its dependencies using Bayesian principles. 14 | func UpdateEventProbabilityWithDependency(event *risk.Event, eventsOccurred map[int]bool, eventProbabilities map[int]float64) float64 { 15 | if len(event.Dependencies) == 0 { 16 | return eventProbabilities[event.ID] 17 | } 18 | 19 | // Adjust event probability based on dependencies 20 | for _, dependency := range event.Dependencies { 21 | dependencyEventProbability := eventProbabilities[dependency.DependsOnEventID] 22 | dependencyOccurred := eventsOccurred[dependency.DependsOnEventID] 23 | 24 | if dependency.Happens && !dependencyOccurred { 25 | // If the event depended on this happening and it didn't, event cannot happen 26 | return 0 27 | } else if !dependency.Happens && dependencyOccurred { 28 | // If the event depended on this not happening but it did, greatly reduce the probability 29 | return eventProbabilities[event.ID] * (1 - dependencyEventProbability) 30 | } 31 | } 32 | 33 | return eventProbabilities[event.ID] 34 | } 35 | 36 | // SimulateEvent checks if an event happens based on its probability and dependencies. 37 | func SimulateEvent(event *risk.Event, eventsOccurred map[int]bool, eventProbabilities map[int]float64) (bool, map[string]float64) { 38 | rand.Seed(time.Now().UnixNano()) 39 | adjustedProbability := UpdateEventProbabilityWithDependency(event, eventsOccurred, eventProbabilities) 40 | 41 | if rand.Float64() <= adjustedProbability { 42 | eventsOccurred[event.ID] = true 43 | // Adjust impacts based on the event's role in the simulation, such as filtering phishing emails. 44 | impacts := calculateImpacts(event, eventProbabilities) 45 | return true, impacts 46 | } 47 | eventsOccurred[event.ID] = false 48 | return false, nil 49 | } 50 | 51 | // calculateImpacts has been updated to include confidence levels in its calculations. 52 | func calculateImpacts(event *risk.Event, eventProbabilities map[int]float64) map[string]float64 { 53 | impacts := make(map[string]float64) 54 | for _, impact := range event.Impact { 55 | // Adjust the impact calculation to factor in confidence levels for both unit impacts and event numbers. 56 | actualUnitImpact, actualEvents := adjustImpactBasedOnEventProbability(impact, eventProbabilities[event.ID]) 57 | 58 | totalImpact := actualUnitImpact * actualEvents 59 | if impact.PositiveImpact { 60 | totalImpact = -totalImpact // Adjust for positive impacts if necessary. 61 | } 62 | 63 | impacts[impact.Unit] += totalImpact 64 | } 65 | 66 | return impacts 67 | } 68 | 69 | // adjustImpactBasedOnEventProbability is refined to consider confidence levels. 70 | func adjustImpactBasedOnEventProbability(impact *risk.Impact, eventProbability float64) (float64, float64) { 71 | // Calculate the average impact and events considering the confidence intervals. 72 | 73 | scaledMinIndividualUnitImpact := utils.AdjustForTime(impact.MinimumIndividualUnitImpact, impact.ExpectedFrequency) 74 | scaledMaxIndividualUnitImpact := utils.AdjustForTime(impact.MaximumIndividualUnitImpact, impact.ExpectedFrequency) 75 | scaledMinImpactEvents := utils.AdjustForTime(impact.MinimumImpactEvents, impact.ExpectedFrequency) 76 | scaledMaxImpactEvents := utils.AdjustForTime(impact.MaximumImpactEvents, impact.ExpectedFrequency) 77 | 78 | avgUnitImpact := weightedAverageWithConfidence( 79 | scaledMinIndividualUnitImpact, impact.MaximumIndividualUnitImpact, 80 | scaledMaxIndividualUnitImpact, impact.MaximumIndividualUnitImpactConfidence, 81 | ) 82 | 83 | avgEvents := weightedAverageWithConfidence( 84 | scaledMinImpactEvents, impact.MaximumImpactEvents, 85 | scaledMaxImpactEvents, impact.MaximumImpactEventsConfidence, 86 | ) 87 | 88 | // Optionally adjust avgEvents based on the event type and probability. 89 | if impact.Name == "Phishing Emails Detected" { 90 | // Example: Scale the number of detected emails based on the filter's effectiveness and confidence. 91 | confidenceScale := (impact.MinimumImpactEventsConfidence + impact.MaximumImpactEventsConfidence) / 2 92 | avgEvents *= eventProbability * confidenceScale 93 | } 94 | 95 | // Ensure the calculated values are within expected bounds. 96 | avgUnitImpact = clamp(avgUnitImpact, impact.MinimumIndividualUnitImpact, impact.MaximumIndividualUnitImpact) 97 | avgEvents = math.Round(clamp(avgEvents, impact.MinimumImpactEvents, impact.MaximumImpactEvents)) // Round events to nearest integer. 98 | 99 | return avgUnitImpact, avgEvents 100 | } 101 | 102 | // MonteCarlo simulates the risk event network a specified number of times, 103 | // adjusting for dependencies using Bayesian statistics. 104 | func MonteCarlo(events []*risk.Event, iterations int) (map[int]float64, map[string]float64, error) { 105 | eventProbabilities := make(map[int]float64) 106 | totalImpacts := make(map[string]float64) 107 | eventOccurrences := make(map[int]int) 108 | impactOccurrences := make(map[string]int) 109 | 110 | // Initialize probabilities with a reasonable estimate 111 | for _, event := range events { 112 | scaledMin := utils.AdjustForTime(event.Probability.Minimum, event.Probability.ExpectedFrequency) 113 | scaledMax := utils.AdjustForTime(event.Probability.Maximum, event.Probability.ExpectedFrequency) 114 | initMin := statistics.GenerateBetaSample(scaledMin, event.Probability.MinimumConfidence) 115 | initMax := statistics.GenerateBetaSample(scaledMax, event.Probability.MaximumConfidence) 116 | sampleProb := statistics.GenerateLHSSamples(initMin, initMax, 100) 117 | avgProb := calcAvg(sampleProb) 118 | eventProbabilities[event.ID] = clampProbability(avgProb) 119 | } 120 | 121 | for i := 0; i < iterations; i++ { 122 | eventsOccurred := make(map[int]bool) 123 | rand.Seed(time.Now().UnixNano()) 124 | 125 | for _, event := range events { 126 | happened, impacts := SimulateEvent(event, eventsOccurred, eventProbabilities) 127 | eventsOccurred[event.ID] = happened 128 | if happened { 129 | eventOccurrences[event.ID]++ 130 | for impactType, impactValue := range impacts { 131 | totalImpacts[impactType] += impactValue 132 | impactOccurrences[impactType]++ // Increment count for this impact type. 133 | } 134 | } 135 | } 136 | } 137 | 138 | // Normalize the total impacts based on the number of occurrences, not iterations. 139 | for impactType, totalValue := range totalImpacts { 140 | if occurrences, ok := impactOccurrences[impactType]; ok && occurrences > 0 { 141 | totalImpacts[impactType] = totalValue / float64(occurrences) 142 | } 143 | } 144 | 145 | // Adjust probabilities based on occurrences 146 | for eventID := range eventProbabilities { 147 | if occurrences, found := eventOccurrences[eventID]; found { 148 | eventProbabilities[eventID] = float64(occurrences) / float64(iterations) 149 | } else { 150 | eventProbabilities[eventID] = 0 // Event never occurred. 151 | } 152 | } 153 | 154 | return eventProbabilities, totalImpacts, nil 155 | } 156 | -------------------------------------------------------------------------------- /risk/statistics/distributions.go: -------------------------------------------------------------------------------- 1 | package statistics 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | 7 | "gonum.org/v1/gonum/stat/distuv" 8 | ) 9 | 10 | // GenerateBetaSample generates a sample from a beta distribution 11 | // for a given probability p and confidence level c. 12 | func GenerateBetaSample(p float64, c float64) float64 { 13 | rand.Seed(time.Now().UnixNano()) 14 | 15 | // Ensure p is within valid range for a beta distribution 16 | if p <= 0 { 17 | p = 0.01 // Assign a small probability if p is less or equal to 0 18 | } else if p >= 1 { 19 | p = 0.99 // Assign a high probability close to 1 if p is greater or equal to 1 20 | } 21 | 22 | // Ensure confidence level c is positive 23 | if c <= 0 { 24 | c = -c // Use the absolute value of c if it's negative 25 | } 26 | 27 | // Calculate alpha and beta 28 | alpha := p * c 29 | beta := (1 - p) * c 30 | 31 | // Create and sample from the beta distribution 32 | betaDist := distuv.Beta{Alpha: alpha, Beta: beta} 33 | sample := betaDist.Rand() 34 | 35 | return sample 36 | } 37 | 38 | // GenerateLHSSamples generates Latin Hypercube Samples for a given range and sample size. 39 | func GenerateLHSSamples(min, max float64, n int) []float64 { 40 | rand.Seed(time.Now().UnixNano()) 41 | 42 | step := (max - min) / float64(n) 43 | samples := make([]float64, n) 44 | for i := range samples { 45 | samples[i] = rand.Float64()*step + float64(i)*step + min 46 | } 47 | 48 | rand.Shuffle(len(samples), func(i, j int) { samples[i], samples[j] = samples[j], samples[i] }) 49 | return samples 50 | } 51 | -------------------------------------------------------------------------------- /risk/types.go: -------------------------------------------------------------------------------- 1 | package risk 2 | 3 | type Probability struct { 4 | ExpectedFrequency string `json:"ExpectedFrequency"` 5 | Minimum float64 `json:"Minimum"` 6 | MinimumConfidence float64 `json:"MinimumConfidence"` 7 | Maximum float64 `json:"Maximum"` 8 | MaximumConfidence float64 `json:"MaximumConfidence"` 9 | } 10 | 11 | type Impact struct { 12 | ImpactID int `json:"ImpactID"` 13 | Name string `json:"Name"` 14 | Unit string `json:"Unit"` 15 | PositiveImpact bool `json:"PositiveImpact"` 16 | 17 | Description string `json:"Description"` 18 | ExpectedFrequency string `json:"ExpectedFrequency"` 19 | 20 | MinimumIndividualUnitImpact float64 `json:"MinimumIndividualUnitImpact"` 21 | MinimumIndividualUnitImpactConfidence float64 `json:"MinimumIndividualUnitImpactConfidence"` 22 | MaximumIndividualUnitImpact float64 `json:"MaximumIndividualUnitImpact"` 23 | MaximumIndividualUnitImpactConfidence float64 `json:"MaximumIndividualUnitImpactConfidence"` 24 | MinimumImpactEvents float64 `json:"MinimumImpactEvents"` 25 | MinimumImpactEventsConfidence float64 `json:"MinimumImpactEventsConfidence"` 26 | MaximumImpactEvents float64 `json:"MaximumImpactEvents"` 27 | MaximumImpactEventsConfidence float64 `json:"MaximumImpactEventsConfidence"` 28 | } 29 | 30 | type Dependency struct { 31 | DependsOnEventID int `json:"DependsOnEventID"` 32 | Happens bool `json:"Happens"` 33 | } 34 | 35 | type Event struct { 36 | ID int `json:"ID"` 37 | Name string `json:"Name"` 38 | Description string `json:"Description"` 39 | Probability *Probability `json:"Probability"` 40 | Impact []*Impact `json:"Impact,omitempty"` 41 | Dependencies []*Dependency `json:"Dependencies,omitempty"` 42 | } 43 | -------------------------------------------------------------------------------- /risk/utils/gen.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/rand" 5 | "math/big" 6 | ) 7 | 8 | func GenerateID() (int, error) { 9 | maxInt32 := big.NewInt(1<<31 - 1) 10 | n, err := rand.Int(rand.Reader, maxInt32) 11 | if err != nil { 12 | return -1, err 13 | } 14 | 15 | return int(n.Int64()), nil 16 | } 17 | -------------------------------------------------------------------------------- /risk/utils/search.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "github.com/bcdannyboy/dgws/risk" 4 | 5 | func FindEvent(ID int, events []*risk.Event) *risk.Event { 6 | for _, event := range events { 7 | if event.ID == ID { 8 | return event 9 | } 10 | } 11 | return nil 12 | } 13 | -------------------------------------------------------------------------------- /risk/utils/timeadjust.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func AdjustForTime(Value float64, TimeFrame string) float64 { 4 | // we scale everything up or down to a yearly basis 5 | switch TimeFrame { 6 | case "hourly": 7 | return Value * 8760 8 | case "daily": 9 | return Value * 365 10 | case "weekly": 11 | return Value * 52 12 | case "monthly": 13 | return Value * 12 14 | case "quarterly": 15 | return Value * 4 16 | case "yearly": 17 | return Value 18 | case "2years": 19 | return Value / 2 20 | case "5years": 21 | return Value / 5 22 | case "10years": 23 | return Value / 10 24 | default: 25 | return Value 26 | } 27 | } 28 | --------------------------------------------------------------------------------