diff --git a/Scripts/Analyze-HuntSMBShares.ps1 b/Scripts/Analyze-HuntSMBShares.ps1
index c4de0e0..6a42cc2 100644
--- a/Scripts/Analyze-HuntSMBShares.ps1
+++ b/Scripts/Analyze-HuntSMBShares.ps1
@@ -5,7 +5,7 @@
#--------------------------------------
# Author: Scott Sutherland, 2024 NetSPI
# License: 3-clause BSD
-# Version: v1.59
+# Version: v1.63
# References: This script includes custom code and code taken and modified from the open source projects PowerView, Invoke-Ping, and Invoke-Parrell.
function Analyze-HuntSMBShares
{
@@ -1580,10 +1580,110 @@ function Analyze-HuntSMBShares
$RiskLevelCountCritical = $ExcessiveSharePrivsFinal | where RiskLevel -eq 'Critical' | measure | select count -ExpandProperty count
# ----------------------------------------------------------------------
- # Create Computer Summary Information
+ # Create Computer Insight Summary Information
# ----------------------------------------------------------------------
- # TBD
+ # Reset global computer risk levels
+ $RiskLevelComputersCountCritical = 0
+ $RiskLevelComputersCountHigh = 0
+ $RiskLevelComputersCountMedium = 0
+ $RiskLevelComputersCountLow = 0
+
+ # Rest row data
+ $ComputerTableRows = ""
+ $ComputerTableRow = ""
+
+ # Get computer list
+ $ComputerPageComputerList = $ExcessiveSharePrivsFinal | select ComputerName -Unique
+
+ # Get computer count
+ $ComputersChartCount = $ComputerPageComputerList | measure | select count -ExpandProperty count # Unique folder group
+
+ # Process each computer & add data to final risk counts
+ $ComputerPageComputerList |
+ foreach {
+
+ # Set target share name
+ $TargetComputers = $_.ComputerName
+
+ # Grab the risk level for the highest risk acl for the share name
+ $ComputersTopACLRiskScore = $ExcessiveSharePrivsFinal | where ComputerName -eq $TargetComputers | select RiskScore | sort RiskScore -Descending | select -First 1 | select RiskScore -ExpandProperty RiskScore
+
+ # Check risk level - Highest wins
+ If($ComputersTopACLRiskScore -le 4 ) { $RiskLevelComputersResult = "Low"}
+ If($ComputersTopACLRiskScore -gt 4 -and $ComputersTopACLRiskScore -lt 11 ) { $RiskLevelComputersResult = "Medium"}
+ If($ComputersTopACLRiskScore -ge 11 -and $ComputersTopACLRiskScore -lt 20 ) { $RiskLevelComputersResult = "High"}
+ If($ComputersTopACLRiskScore -ge 20 ) { $RiskLevelComputersResult = "Critical"}
+
+ # Increment counts
+ if($RiskLevelComputersResult -eq "Low" ){$RiskLevelComputersCountLow = $RiskLevelComputersCountLow + 1}
+ if($RiskLevelComputersResult -eq "Medium" ){$RiskLevelComputersCountMedium = $RiskLevelComputersCountMedium + 1}
+ if($RiskLevelComputersResult -eq "High" ){$RiskLevelComputersCountHigh = $RiskLevelComputersCountHigh + 1}
+ if($RiskLevelComputersResult -eq "Critical"){$RiskLevelComputersCountCritical = $RiskLevelComputersCountCritical + 1}
+
+ # Get share count
+ $ComputerPageShares = $ExcessiveSharePrivsFinal | where ComputerName -eq $TargetComputers | select SharePath -Unique | ForEach-Object { $ASDF = $_.SharePath; "$ASDF " } | out-string
+ $ComputerPageShareCount = $ExcessiveSharePrivsFinal | where ComputerName -eq $TargetComputers | select SharePath -Unique | measure | select count -ExpandProperty count
+ $ComputerPageShareCountHTML = @"
+ $ComputerPageShareCount
+
+ $ComputerPageShares
+
+"@
+ # Check for interesting files
+ # For each file category generate count and list
+ $ComputerPageInterestingFilesInsideHTML = ""
+ $ComputerPageInterestingFilesOutsideHTML = ""
+ $FileNamePatternCategories | select Category -ExpandProperty Category |
+ foreach{
+
+ # Get category
+ $ComputerPageCategoryName = $_
+
+ # Get list of that sharename and category
+ $ComputerPageCategoryFilesBase = $InterestingFilesAllObjects | where ComputerName -eq $TargetComputers | where Category -eq "$ComputerPageCategoryName" | select FileName
+ $ComputerPageCategoryFiles = $InterestingFilesAllObjects | where ComputerName -eq $TargetComputers | where Category -eq "$ComputerPageCategoryName" | select FileName | ForEach-Object { $ASDF = $_.FileName; "$ASDF " } | out-string
+
+ # Get category count
+ $ComputerPageCategoryFilesCount = $ComputerPageCategoryFilesBase | measure | select count -expandproperty count
+
+ # Generate HTML with Category
+ if($ComputerPageCategoryFilesCount -ne 0){
+ $ComputerPageInterestingFilesHTMLPrep = @"
+ $ComputerPageCategoryFilesCount $ComputerPageCategoryName
+
+ $ComputerPageCategoryFiles
+
+"@
+ # Add to code block
+ $ComputerPageInterestingFilesInsideHTML = $ComputerPageInterestingFilesInsideHTML + $ComputerPageInterestingFilesHTMLPrep
+ }
+ }
+
+ # Get total for interesting files for target share name
+ $ComputerPageInterestingFilesCount = $InterestingFilesAllObjects | where ComputerName -eq $TargetComputers | measure | select count -expandproperty count
+
+ # Build final interesting file html for computers page
+ $ComputerPageInterestingFilesOutsideHTML = @"
+ $ComputerPageInterestingFilesCount Files
+
+ $ComputerPageInterestingFilesInsideHTML
+
+"@
+
+ # Create Row
+ $ComputerTableRow = @"
+
+ $TargetComputers
+ $ComputersTopACLRiskScore $RiskLevelComputersResult
+ $ComputerPageShareCountHTML
+ $ComputerPageInterestingFilesOutsideHTML
+
+"@
+
+ # Add row to rows
+ $ComputerTableRows = $ComputerTableRows + $ComputerTableRow
+ }
# ----------------------------------------------------------------------
# Create Share Name Summary Information
@@ -2652,7 +2752,7 @@ function Analyze-HuntSMBShares
# Build final interesting file html for share names page
$ShareNameInterestingFilesOutsideHTML = @"
$ShareNameInterestingFilesCount Files
-
+
$ShareNameInterestingFilesInsideHTML
"@
@@ -4292,10 +4392,11 @@ input[type="checkbox"]:checked::before {
Computer Summary
Share Summary
ACL Summary
-
Data Insights
-
Interesting Files
+
Data Insights
+
Computers
Share Names
-
Folder Groups
+
Folder Groups
+
Interesting Files
Affected Subnets
Share Owners
Group ACL Summary
@@ -4658,6 +4759,111 @@ $CardLastModifiedTimeLine
+
+
+
+
+
+
Computers
+
+
+$ComputerCount computers were found in the $TargetDomain Active Directory domain. Below is a list of the computers hosting shares configured with excessive privileges.
+
+
+
+
+
+ Live Computers Found
+
+
+
+
+ $ComputerPingableCount
+ ($ComputerWithExcessive host shares with excessive privileges)
+
+
+
+
+
+
+ Computer Count by Share Exposure
+
+
+
+
+
+ Computer Count by Risk Level
+
+
+
+
+
+
+
+
+
+
+ Computer Name
+
+ Risk Level
+
+ Share Count
+
+ Interesting Files
+
+
+
+
+
+ $ComputerTableRows
+
+
+
+
+
@@ -5121,7 +5327,7 @@ Below is a summary of the exposure associated with each of those groups.
Share Names
-This section contains a list of the most common SMB share names. In some cases, shares with the exact same name may be related to a single application or process. This information can help identify the root cause associated with the excessive privileges and expedite remediation.
+$AllSMBSharesCount shares were discovered across computers in the $TargetDomain Active Directory domain. $ShareNameChartCount shares were found configured with excessive privileges. Below is a summary of those shares grouped by name.
@@ -5828,6 +6034,93 @@ Invoke-HuntSMBShares -Threads 20 -RunSpaceTimeOut 10 -OutputDirectory c:\folder\
}
}
+
+// --------------------------
+// Computers Page - Computers Found
+// --------------------------
+
+// Initialize ApexCharts
+const ChartComputersDiscoOptions = {
+ series: [{
+ data: [$ComputerPingableCount,$Computers445OpenCount,$AllComputersWithSharesCount,$ComputerwithNonDefaultCount,$ComputerWithExcessive,$ComputerWithReadCount,$ComputerWithWriteCount]
+ }],
+ chart: {
+ type: 'bar',
+ height: 200
+ },
+ plotOptions: {
+ bar: {
+ borderRadius: 0,
+ borderRadiusApplication: 'end',
+ horizontal: true,
+ colors: {
+ backgroundBarColors: ['#e0e0e0'],
+ backgroundBarOpacity: 1,
+ ranges: [{
+ from: 0,
+ to: 1000,
+ color: '#f08c41'
+ }]
+ }
+ }
+ },
+ dataLabels: {
+ enabled: false
+ },
+ grid: {
+ show: false
+ },
+ xaxis: {
+ categories: ['Ping Response','445Open','Host Shares','Non Default','Excessive Privs','Readable','Writable']
+ }
+};
+
+const ChartComputersDisco = new ApexCharts(document.querySelector("#ChartComputersDisco"), ChartComputersDiscoOptions);
+ChartComputersDisco.render();
+
+// --------------------------
+// Computers Page - Computers Risk Levels
+// --------------------------
+
+// Initialize ApexCharts
+const ChartComputersRiskOptionsa = {
+ series: [{
+ data: [$RiskLevelComputersCountCritical, $RiskLevelComputersCountHigh, $RiskLevelComputersCountMedium, $RiskLevelComputersCountLow]
+ }],
+ chart: {
+ type: 'bar',
+ height: 200
+ },
+ plotOptions: {
+ bar: {
+ borderRadius: 0,
+ borderRadiusApplication: 'end',
+ horizontal: true,
+ colors: {
+ backgroundBarColors: ['#e0e0e0'],
+ backgroundBarOpacity: 1,
+ ranges: [{
+ from: 0,
+ to: 1000,
+ color: '#f08c41'
+ }]
+ }
+ }
+ },
+ dataLabels: {
+ enabled: false
+ },
+ grid: {
+ show: false
+ },
+ xaxis: {
+ categories: ['Critical','High','Medium','Low']
+ }
+};
+
+const ChartComputersRisk = new ApexCharts(document.querySelector("#ChartComputersRisk"), ChartComputersRiskOptionsa);
+ChartComputersRisk.render();
+
// --------------------------
// Folder Group Page: Chart - Interesting Files
// --------------------------
@@ -6474,6 +6767,10 @@ applyFiltersAndSort('foldergrouptable', 'filterInputTwo', 'filterCounterTwo', 'p
document.getElementById('filterInputIF').addEventListener("keyup", () => applyFiltersAndSort('InterestingFileTable', 'filterInputIF', 'filterCounterIF', 'paginationIF'));
applyFiltersAndSort('InterestingFileTable', 'filterInputIF', 'filterCounterIF', 'paginationIF');
+// Initialize computers table
+document.getElementById('computerfilterInput').addEventListener("keyup", () => applyFiltersAndSort('ComputersTable', 'computerfilterInput', 'computerfilterCounter', 'computerpagination'));
+applyFiltersAndSort('ComputersTable', 'computerfilterInput', 'computerfilterCounter', 'computerpagination');
+
// CSV export function
function extractAndDownloadCSV(tableId, columnIndex) {
// Regex to match \\server\share, \\server\share folder, and \\server\share\file.ext formats, allowing spaces
@@ -6509,11 +6806,14 @@ function extractAndDownloadCSV(tableId, columnIndex) {
let csvContent = 'data:text/csv;charset=utf-8,';
csvContent += cleanUncPaths.join('\n');
+ // Set output file name
+ let CombinedName = tableId + '_unc_paths.csv'
+
// Create a link to download the CSV file
const encodedUri = encodeURI(csvContent);
const link = document.createElement('a');
link.setAttribute('href', encodedUri);
- link.setAttribute('download', 'unc_paths.csv');
+ link.setAttribute('download', CombinedName);
document.body.appendChild(link);
// Force download the file