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 = @" + +
+ $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 = @" + +
+ $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 = @" + +
+ $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 = @" -
+
$ShareNameInterestingFilesInsideHTML
"@ @@ -4292,10 +4392,11 @@ input[type="checkbox"]:checked::before { - - + + - + + @@ -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 +
+
+
+
+
+
+
+
+ + +
+
Loading...
+ Export +
+ + + + + + + + + + + + + + + + + $ComputerTableRows + +
Computer
Name  
Computer Name
is the name of the computer.
Risk
Level  
Risk Level
relfects the exposure of credentials and sensitive data.
Share
Count  
Share Count
is the number of shares
hosted on the same computer.
Interesting
Files  
Interesting Files
are filenames that
may be sensitive.
+
+
+ @@ -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