Skip to content

Commit 839ac11

Browse files
author
Cody Martin
committed
v1.1
1 parent 047f7a5 commit 839ac11

File tree

102 files changed

+1612
-711
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+1612
-711
lines changed

CHANGELOG.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
CHANGELOG
22
============
3+
# October 7th, 2021
34

4-
# July 12th, 2021
5-
6-
ETM v1.0 Official Release
7-
8-
## New Features:
5+
## Changes
96

7+
- Threat Trees
8+
+ Now possible to edit categories in an existing threat tree
9+
- Threat Narrative Graphs
10+
+ Updated to use D3.js library for graphs
1011
- Template Packs
11-
+ Now possible to import and export template packs as JSON files
12+
+ Can import/export template packs as JSON files
13+
- Multiple bug fixes in all areas
1214

1315
# June 8th, 2021
1416

@@ -19,7 +21,7 @@ Techniques have been edited to reflect changes present in ATT&CK v9
1921
## New Features:
2022

2123
- Threat Trees
22-
+ MITRE ATT&CK IDs are pulled from an entire assessment and populate a new design page for threat trees. From the design page you can customize many aspects of how the graph will appear when exported. This can be found by navigating to an Assessment and clicking the THREAT TREE button. Please note that once categories have been created for a threat tree, those are not changeable without deleting the tree and starting over. This was implemented to aid when creating the outbrief graphs we've seen lately
24+
+ MITRE ATT&CK IDs are pulled from an entire assessment and populate a new design page for threat trees. From the design page you can customize many aspects of how the graph will appear when exported. This can be found by navigating to an Assessment and clicking the THREAT TREE button. Please note that once categories have been created for a threat tree, those are not changeable without deleting the tree and starting over. This was implemented to aid when creating the outbrief graphs we've seen lately
2325
- MITRE ATT&CK For ICS
2426
+ Now included in your options when selecting an appropriate ATT&CK technique under the EditEvent and EditSteplate pages
2527
- HTML Reports

Controllers/HomeController.cs

Lines changed: 199 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
using Manatee.Json.Serialization;
3030
using Manatee.Json.Schema;
3131
using System.Text.Json;
32+
using System.Text.RegularExpressions;
3233

3334
namespace Enter_The_Matrix.Controllers
3435
{
@@ -101,7 +102,7 @@ public async Task<IActionResult> DeleteAssessment(string assessmentId)
101102
Assessments assessment = _assessmentsService.GetByIdAsync(assessmentId).Result;
102103

103104
// Delete the threat tree associated with the assessment
104-
await _treeService.DeleteAsync(assessment.ThreatTreeId);
105+
if (assessment.ThreatTreeId != null && assessment.ThreatTreeId != "") { await _treeService.DeleteAsync(assessment.ThreatTreeId); }
105106

106107
// Clean out scenarios and events
107108
foreach (var scenarioId in assessment.Scenarios)
@@ -1188,6 +1189,7 @@ public async Task<IActionResult> Diagram(string scenarioId)
11881189
{
11891190
if (!User.Identity.IsAuthenticated) { return RedirectToAction("Login", "Security"); }
11901191

1192+
/* Deprecated GraphViz Code
11911193
List<string> document = new List<string>();
11921194
11931195
Scenarios scenario = await _scenariosService.GetByIdAsync(scenarioId);
@@ -1201,41 +1203,7 @@ public async Task<IActionResult> Diagram(string scenarioId)
12011203
12021204
// Fixing root node issue
12031205
var first = stepList.First();
1204-
if (
1205-
first.ThreatSource == "Outsider" ||
1206-
first.ThreatSource == "Insider" ||
1207-
first.ThreatSource == "Trusted Insider" ||
1208-
first.ThreatSource == "Privileged Insider"
1209-
)
1210-
{
1211-
nodeList.Add(new Node(null, "malicious-actor", first.ThreatSource, "", "root"));
1212-
}
1213-
else if (
1214-
first.ThreatSource == "Partner Organization" ||
1215-
first.ThreatSource == "Competitor Organization" ||
1216-
first.ThreatSource == "Supplier Organization" ||
1217-
first.ThreatSource == "Customer Organization"
1218-
)
1219-
{
1220-
nodeList.Add(new Node(null, "group", first.ThreatSource, "", "root"));
1221-
}
1222-
else if (
1223-
first.ThreatSource == "Ad Hoc Group" ||
1224-
first.ThreatSource == "Established Group"
1225-
)
1226-
{
1227-
nodeList.Add(new Node(null, "organization", first.ThreatSource, "", "root"));
1228-
}
1229-
else if (
1230-
first.ThreatSource == "Nation State"
1231-
)
1232-
{
1233-
nodeList.Add(new Node(null, "guard-personnel", first.ThreatSource, "", "root"));
1234-
}
1235-
else
1236-
{
1237-
nodeList.Add(new Node(null, "unknown-suspect", first.ThreatSource, "", "root"));
1238-
}
1206+
12391207
12401208
foreach (var step in stepList)
12411209
{
@@ -1341,7 +1309,133 @@ public async Task<IActionResult> Diagram(string scenarioId)
13411309
document.Add("\tfontsize=\"28\";");
13421310
document.Add("\tlabel=\"" + scenario.Name.Replace("\"", "\\\"") + "\";");
13431311
document.Add("}");
1312+
*/
1313+
1314+
// Hijacking flow for D3 //
1315+
1316+
Scenarios scenario = await _scenariosService.GetByIdAsync(scenarioId);
1317+
1318+
List<Steps> eventList = new List<Steps>();
1319+
foreach (string stepId in scenario.Steps) { eventList.Add(await _stepsService.GetByIdAsync(stepId)); }
1320+
1321+
string quoteEscaper = @"\" + "\"";
1322+
1323+
using (StreamWriter outputFile = new StreamWriter(Path.Combine("wwwroot/graphs/", "graph.json")))
1324+
{
1325+
outputFile.WriteLine("{");
1326+
outputFile.WriteLine("\"nodes\": [");
1327+
List<string> Ids = new List<string>();
1328+
List<(string parent, string id, string risk, int step)> links = new List<(string parent, string id, string risk, int step)>();
1329+
int counter = 0;
1330+
1331+
// Accounting for root node
1332+
Ids.Add("");
1333+
1334+
// Setting icon according to first event threat actor
1335+
var first = eventList.First();
1336+
string attackerIcon = "";
1337+
1338+
if (
1339+
first.ThreatSource == "Outsider" ||
1340+
first.ThreatSource == "Insider" ||
1341+
first.ThreatSource == "Trusted Insider" ||
1342+
first.ThreatSource == "Privileged Insider"
1343+
)
1344+
{
1345+
attackerIcon = "malicious-actor";
1346+
}
1347+
else if (
1348+
first.ThreatSource == "Partner Organization" ||
1349+
first.ThreatSource == "Competitor Organization" ||
1350+
first.ThreatSource == "Supplier Organization" ||
1351+
first.ThreatSource == "Customer Organization"
1352+
)
1353+
{
1354+
attackerIcon = "group";
1355+
}
1356+
else if (
1357+
first.ThreatSource == "Ad Hoc Group" ||
1358+
first.ThreatSource == "Established Group"
1359+
)
1360+
{
1361+
attackerIcon = "organization";
1362+
}
1363+
else if (
1364+
first.ThreatSource == "Nation State"
1365+
)
1366+
{
1367+
attackerIcon = "guard-personnel";
1368+
}
1369+
else
1370+
{
1371+
attackerIcon = "unknown-suspect";
1372+
}
1373+
1374+
outputFile.WriteLine("{");
1375+
outputFile.WriteLine($"\"x\": 0,");
1376+
outputFile.WriteLine($"\"y\": 0,");
1377+
outputFile.WriteLine($"\"title\": \"{first.ThreatSource}\",");
1378+
outputFile.WriteLine($"\"icon\": \"/icons/{attackerIcon}.png\"");
1379+
outputFile.WriteLine("},");
1380+
1381+
foreach (Steps ev in eventList)
1382+
{
1383+
1384+
// Inject Node Information
1385+
if (!Ids.Contains(ev.Id)) { Ids.Add(ev.Id); }
1386+
outputFile.WriteLine("{");
1387+
outputFile.WriteLine("\"x\": 0,");
1388+
outputFile.WriteLine("\"y\": 0,");
1389+
outputFile.WriteLine($"\"title\": \"{ev.GraphNode.EntityDescription.Replace(@"\", @"\\").Replace("\"", quoteEscaper) }\",");
1390+
outputFile.WriteLine($"\"icon\": \"/icons/{ev.GraphNode.EntityType}.png\"");
1391+
1392+
string end = "}";
1393+
if (counter++ < eventList.Count-1) { end = end + ","; }
1394+
outputFile.WriteLine(end);
1395+
1396+
// Collect link information
1397+
foreach (string parentId in ev.GraphNode.ParentId)
1398+
{
1399+
string parent = "";
1400+
if (parentId != null) { parent = parentId; }
1401+
links.Add((parent, ev.Id, ev.Risk.ToLower().Replace(" ", "-"), counter));
1402+
}
1403+
}
1404+
outputFile.WriteLine("],");
1405+
outputFile.WriteLine("\"links\": [");
1406+
counter = 0;
1407+
foreach (var link in links)
1408+
{
1409+
outputFile.WriteLine("{");
1410+
outputFile.WriteLine($"\"source\": {Ids.IndexOf(link.parent)},");
1411+
outputFile.WriteLine($"\"target\": {Ids.IndexOf(link.id)},");
1412+
outputFile.WriteLine($"\"risk\": \"{link.risk}\",");
1413+
outputFile.WriteLine($"\"label\": \"{link.step}.\"");
1414+
1415+
string end = "}";
1416+
if (counter++ < links.Count-1) { end = end + ","; }
1417+
outputFile.WriteLine(end);
1418+
}
1419+
outputFile.WriteLine("]");
1420+
outputFile.WriteLine("}");
1421+
1422+
}
1423+
1424+
string markdownTable = "| Event |\n| ----- |\n";
1425+
int eventCounter = 1;
1426+
foreach (var ev in eventList)
1427+
{
1428+
markdownTable += "| " + eventCounter++.ToString() + ". " + ev.Event + " |\n";
1429+
}
1430+
1431+
ViewBag.MarkdownTable = markdownTable;
1432+
1433+
scenario.Name = scenario.Name.Replace(" ", "_");
1434+
return View(scenario);
13441435

1436+
// End Hijacking, following code is deprecated
1437+
1438+
/*
13451439
// write the text to a DOT file
13461440
using (StreamWriter outputFile = new StreamWriter(Path.Combine("wwwroot/graphs/", "graph.dot")))
13471441
{
@@ -1373,6 +1467,7 @@ public async Task<IActionResult> Diagram(string scenarioId)
13731467
13741468
13751469
return View(stepList);
1470+
*/
13761471
}
13771472

13781473
#endregion
@@ -2579,6 +2674,58 @@ List<string> nodeShape
25792674

25802675
}
25812676

2677+
public async Task<IActionResult> EditTreeCategories(string[] categories, string[] colors, string threatTreeId, string assessmentId)
2678+
{
2679+
if (!User.Identity.IsAuthenticated) { return RedirectToAction("Login", "Security"); }
2680+
2681+
ThreatTree tree = await _treeService.GetByIdAsync(threatTreeId);
2682+
2683+
// Build the new list of categories we want to use
2684+
List<string[]> newCategories = new List<string[]>();
2685+
for (int i = 0; i < categories.Length; i++)
2686+
{
2687+
newCategories.Add(new string[]
2688+
{
2689+
categories[i], colors[i], tree.ColorList.GetValueOrDefault(colors[i])
2690+
});
2691+
}
2692+
2693+
// Check to see which of the old categories are no longer used
2694+
List<string> removeCategories = new List<string>();
2695+
foreach (string[] category in tree.Categories)
2696+
{
2697+
if (!categories.Contains(category[0]))
2698+
{
2699+
removeCategories.Add(category[0]);
2700+
}
2701+
}
2702+
2703+
// Reset each of the nodes which belongs to the removed categories
2704+
foreach (ThreatTreeNode node in tree.NodeList)
2705+
{
2706+
if (removeCategories.Contains(node.Classification[0]))
2707+
{
2708+
node.Classification = newCategories.First();
2709+
}
2710+
// If it is not to be reset, verify the colors are up to date
2711+
else
2712+
{
2713+
foreach (string[] category in newCategories)
2714+
{
2715+
if (node.Classification[0] == category[0]) { node.Classification = category; break; }
2716+
}
2717+
}
2718+
}
2719+
2720+
// Set the tree categories to our new list
2721+
tree.Categories = newCategories;
2722+
2723+
// Update the tree
2724+
await _treeService.UpdateAsync(tree.Id, tree);
2725+
2726+
return RedirectToAction("EditTree", "Home", new { threatTreeId = tree.Id, assessmentId = assessmentId });
2727+
}
2728+
25822729
public async Task<IActionResult> ExportTree(string treeId)
25832730
{
25842731
if (!User.Identity.IsAuthenticated) { return RedirectToAction("Login", "Security"); }
@@ -2612,10 +2759,11 @@ public async Task<IActionResult> ExportTree(string treeId)
26122759

26132760
// Build out the subgraphs
26142761
foreach (var category in tree.Categories)
2615-
{
2616-
// Handle Clustering
2617-
if (tree.IsClustered) { document.Add("\tsubgraph cluster_" + category[0].Replace(" ", "").ToLower() + " {"); }
2618-
else { document.Add("\tsubgraph " + category[0].Replace(" ", "").ToLower() + " {"); }
2762+
{
2763+
// Handle Clustering
2764+
string cleanCategory = Regex.Replace(category[0], "[^0-9a-zA-Z_]+", "_");
2765+
if (tree.IsClustered) { document.Add("\tsubgraph cluster_" + cleanCategory.ToLower() + " {"); }
2766+
else { document.Add("\tsubgraph " + cleanCategory.ToLower() + " {"); }
26192767

26202768

26212769
List<string> nodesInSubgraph = new List<string>();
@@ -2771,7 +2919,8 @@ public async Task<IActionResult> ExportTree(string treeId)
27712919
string legendEdge = "";
27722920
foreach (var category in tree.Categories)
27732921
{
2774-
document.Add("\tsubgraph " + category[0].Replace(" ","").ToLower() + " {");
2922+
string cleanCategory = Regex.Replace(category[0], "[^0-9a-zA-Z_]+", "_");
2923+
document.Add("\tsubgraph " + cleanCategory.ToLower() + " {");
27752924
document.Add("\t\t" + legendCounter + " [color=" + category[1] + ",style=\"rounded,filled\",shape=box,label=\"" + category[0] + "\"];");
27762925
document.Add("\t\t{rank=same; " + legendCounter + ";}");
27772926
document.Add("\t}");
@@ -2813,6 +2962,15 @@ public async Task<IActionResult> ExportTree(string treeId)
28132962
if (string.IsNullOrEmpty(error)) { Console.WriteLine(output); }
28142963
else { Console.WriteLine(error); }
28152964

2965+
string treePath = @"wwwroot/graphs/Tree.png";
2966+
string legendPath = @"wwwroot/graphs/Legend.png";
2967+
2968+
DateTime treeDate = System.IO.File.GetLastWriteTime(treePath);
2969+
DateTime legendDate = System.IO.File.GetLastWriteTime(legendPath);
2970+
2971+
ViewBag.TreeDate = treeDate;
2972+
ViewBag.LegendDate = legendDate;
2973+
28162974
return View();
28172975
}
28182976

Enter_The_Matrix.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
</ItemGroup>
3636

3737
<ItemGroup>
38-
<Folder Include="wwwroot\graphs\" />
3938
<Folder Include="wwwroot\templates\" />
4039
</ItemGroup>
4140

Startup.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,16 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
104104
app.UseHsts();
105105
}
106106
app.UseHttpsRedirection();
107-
app.UseStaticFiles();
107+
app.UseStaticFiles(new StaticFileOptions
108+
{
109+
OnPrepareResponse = ctx =>
110+
{
111+
if (!ctx.Context.User.Identity.IsAuthenticated && !( ctx.File.Name.EndsWith(".css") || ctx.File.Name.EndsWith(".scss") || ctx.File.Name.EndsWith(".ttf") ) )
112+
{
113+
ctx.Context.Response.Redirect("/");
114+
}
115+
}
116+
});
108117

109118
app.UseRouting();
110119

0 commit comments

Comments
 (0)