diff --git a/PowerHuntShares.psm1 b/PowerHuntShares.psm1
index 1580c3e..f0ec931 100644
--- a/PowerHuntShares.psm1
+++ b/PowerHuntShares.psm1
@@ -4,7 +4,7 @@
#--------------------------------------
# Author: Scott Sutherland, 2024 NetSPI
# License: 3-clause BSD
-# Version: v1.61
+# Version: v1.70
# References: This script includes custom code and code taken and modified from the open source projects PowerView, Invoke-Ping, and Invoke-Parrell.
function Invoke-HuntSMBShares
{
@@ -258,7 +258,7 @@ function Invoke-HuntSMBShares
# Create sub directories
mkdir $OutputDirectoryBase | Out-Null
$SubDir = "Results"
- mkdir "$OutputDirectoryBase\$SubDir" | Out-Null
+ mkdir "$OutputDirectoryBase\Results" | Out-Null
$OutputDirectory = "$OutputDirectoryBase\Results"
Write-Output " [*][$Time] Output Directory: $OutputDirectoryBase"
}else{
@@ -1546,8 +1546,18 @@ function Invoke-HuntSMBShares
$ShareName = $_.name
Write-Output " [*][$Time] - $ShareCount $ShareName"
}
- Write-Output " [*] -----------------------------------------------"
- Write-Output " [*][$Time] - Generating HTML Report"
+
+ # Estimate report generation time
+ If($AllSMBSharesCount -le 500 ) {$ReportGenTimeEstimate = "1 minute or less" }
+ If($AllSMBSharesCount -ge 1000 ) {$ReportGenTimeEstimate = "3 minute or less" }
+ If($AllSMBSharesCount -ge 2000 ) {$ReportGenTimeEstimate = "5 minute or less" }
+ If($AllSMBSharesCount -ge 10000 ) {$ReportGenTimeEstimate = "10 minute or less"}
+ If($AllSMBSharesCount -ge 15000 ) {$ReportGenTimeEstimate = "15 minute or less"}
+ If($AllSMBSharesCount -ge 30000 ) {$ReportGenTimeEstimate = "20 minute or less"}
+
+ Write-Output " [*] -----------------------------------------------"
+ Write-Output " [*][$Time] - Generating HTML Report"
+ Write-Output " [*][$Time] - Estimated generation time: $ReportGenTimeEstimate"
# ----------------------------------------------------------------------
# Display final summary - NEW HTML REPORT
@@ -1627,13 +1637,16 @@ function Invoke-HuntSMBShares
$ShareDescriptionSample = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | where ShareDescription -NE "" | select ShareDescription -first 1 -expandproperty ShareDescription | foreach {"Sample Description $_ "}
# First created
- $ShareFirstCreated = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select creationdate | foreach{[datetime]$_.creationdate } | Sort-Object | select -First 1 | foreach {$_.tostring("MM/dd/yyyy HH:mm:ss")}
+ # $ShareFirstCreated = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select creationdate | foreach{[datetime]$_.creationdate } | Sort-Object | select -First 1 | foreach {$_.tostring("MM/dd/yyyy HH:mm:ss")}
+ $ShareFirstCreated = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select creationdate | foreach{[datetime]$_.creationdate } | Sort-Object | select -First 1 | foreach {$_.tostring("MM/dd/yyyy")}
# Last created
- $ShareLastCreated = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select creationdate | foreach{[datetime]$_.creationdate } | Sort-Object -Descending | select -First 1 | foreach {$_.tostring("MM/dd/yyyy HH:mm:ss")}
+ # $ShareLastCreated = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select creationdate | foreach{[datetime]$_.creationdate } | Sort-Object -Descending | select -First 1 | foreach {$_.tostring("MM/dd/yyyy HH:mm:ss")}
+ $ShareLastCreated = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select creationdate | foreach{[datetime]$_.creationdate } | Sort-Object -Descending | select -First 1 | foreach {$_.tostring("MM/dd/yyyy")}
# Last modified
- $ShareLastModified = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select LastModifiedDate | foreach{[datetime]$_.LastModifiedDate } | Sort-Object -Descending | select -First 1 | foreach {$_.tostring("MM/dd/yyyy HH:mm:ss")}
+ # $ShareLastModified = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select LastModifiedDate | foreach{[datetime]$_.LastModifiedDate } | Sort-Object -Descending | select -First 1 | foreach {$_.tostring("MM/dd/yyyy HH:mm:ss")}
+ $ShareLastModified = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select LastModifiedDate | foreach{[datetime]$_.LastModifiedDate } | Sort-Object -Descending | select -First 1 | foreach {$_.tostring("MM/dd/yyyy")}
# Share owner list
$ShareOwnerList = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | Sort-Object | select ShareOwner -Unique -ExpandProperty ShareOwner
@@ -1671,139 +1684,460 @@ function Invoke-HuntSMBShares
}
}
- # ----
- # Calculate similarity here - START
-
- # Calculate share to file group ratio: 0 to 1
- # min value = 0
- # max value = number of shares
- # 10 shares / 1 group = 10.0 - highest score when only one group - correct
- # 10 shares / 5 groups = 2.0 - high score when fewer groups - correct
- # 10 shares / 10 groups = 1.0 - lower score when more groups - correct
- # 5 shares / 10 groups = 0.5 - lower score when more groups - correct
- $SimularityCalcShareFg1 = [math]::Round($ShareCount/$ShareFolderGroupCount,4) # Original value
- $SimularityCalcShareFg = $SimularityCalcShareFg1 / $ShareCount
-
- # Calculate share to owner ration: 0 to 1 (to show difference between avg foldergroup ad overall)
- $SimularityCalcShareOwner1 = [math]::Round($ShareCount/$ShareOwnerListCount,4)
- $SimularityCalcShareOwner = $SimularityCalcShareOwner1 / $ShareCount
+ #region SimilarityScore
+ # ----------------------------------------------------------------------
+ # Calculate Similarity Score - START
+ # ----------------------------------------------------------------------
- # Calculate file group to owner avg ratio avg per share name: 0 to 1
- $FGtoOwnersRatios = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select ShareName,FileListGroup -Unique | Group-Object FileListGroup | sort count -Descending | select count, name |
- foreach{
- $FgName = $_.name
- $FgCount = $_.count
+ ## ---
+ ## Folder Group Coverage Weighted Calculations
+ ## ---------------------------------------------
- # Get count of owners associated with file group
- $FgNameOwnercount = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | where FileListGroup -EQ "$FgName" | select ShareOwner -Unique | measure | select count -expandproperty count
-
- # Calculate file group to owners ratio
- [math]::Round($FgCount/$FgNameOwnercount,4)
- }
- $FGtoOwnersRatiosSum = 0
- $FGtoOwnersRatiosCount = $FGtoOwnersRatios | measure-object | select count -expandproperty count
- $FGtoOwnersRatios |
- Foreach{
- $FGtoOwnersRatiosSum += $_
- }
- $SimularityCalcFGOwnerAvg = [math]::Round($FGtoOwnersRatiosSum/$FGtoOwnersRatiosCount,4)
+ ##
+ ## Determine if the percentage of shares that exists in each folder group for the target share name meet the defined thresholds.
- # Caluatlate if at least 1 file group is 30%/50% or greater: 0 or 1
- # take total count ($ShareFolderGroupCount)
- # divide the number of instances by individual
- # foreach loop until yes.
- $fiftyorgreater = 0
- $SimularityCalc50P = 0
- $SimularityCalOver30 = 0
- $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select ShareName,FileListGroup -Unique | Group-Object FileListGroup | sort count -Descending | select count, name |
+ # Start all values at 0
+ $SimularityFolderGroupCoverageScore = 0
+ $SimularityFolderGroupCoverage10 = 0
+ $SimularityFolderGroupCoverage20 = 0
+ $SimularityFolderGroupCoverage30 = 0
+ $SimularityFolderGroupCoverage40 = 0
+ $SimularityFolderGroupCoverage50 = 0
+ $SimularityFolderGroupCoverage60 = 0
+ $SimularityFolderGroupCoverage70 = 0
+ $SimularityFolderGroupCoverage80 = 0
+ $SimularityFolderGroupCoverage90 = 0
+ $SimularityFolderGroupCoverage100 = 0
+
+ # Get the share count for each folder group
+ $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select SharePath,FileListGroup -Unique | Group-Object FileListGroup | sort count -Descending | select count, name |
foreach{
- # Get % the file group represents for the share
- $fgpercentage = [math]::Round($_.count/$ShareFolderGroupCount,4)
+ # Determine if thesholds where met and update tracker variables
- # If it's 25% or great flip the bit
- if($fgpercentage -ge .25){
- $SimularityCalOver30 = 1
+ # Get % of shares the folder group includes
+ $PercentOfSharesInFg = [math]::Round($_.count/$ShareCount,4)
+
+ # Check if 10% of shares share the same file group
+ if($PercentOfSharesInFg -ge .10){
+ $SimularityFolderGroupCoverage10 = 1
}
- # If it's 50% or great flip the bit
- if($fgpercentage -ge .5){
- $SimularityCalc50P = 1
+ # Check if 20% of shares share the same file group
+ if($PercentOfSharesInFg -ge .20){
+ $SimularityFolderGroupCoverage20 = 1
+ }
+
+ # Check if 30% of shares share the same file group
+ if($PercentOfSharesInFg -ge .30){
+ $SimularityFolderGroupCoverage30 = 1
+ }
+
+ # Check if 40% of shares share the same file group
+ if($PercentOfSharesInFg -ge .40){
+ $SimularityFolderGroupCoverage40 = 1
+ }
+
+ # Check if 51% of shares share the same file group
+ if($PercentOfSharesInFg -ge .51){
+ $SimularityFolderGroupCoverage50 = 1
+ }
+
+ # Check if 60% of shares share the same file group
+ if($PercentOfSharesInFg -ge .60){
+ $SimularityFolderGroupCoverage60 = 1
+ }
+ # Check if 70% of shares share the same file group
+ if($PercentOfSharesInFg -ge .70){
+ $SimularityFolderGroupCoverage70 = 1
+ }
+
+ # Check if 80% of shares share the same file group
+ if($PercentOfSharesInFg -ge .80){
+ $SimularityFolderGroupCoverage80 = 1
+ }
+
+ # Check if 90% of shares share the same file group
+ if($PercentOfSharesInFg -ge .90){
+ $SimularityFolderGroupCoverage90 = 1
+ }
+
+ # Check if 100% of shares share the same file group
+ if($PercentOfSharesInFg -ge 1){
+ $SimularityFolderGroupCoverage100 = 1
}
}
- # Caluatlate if at least one file name exists across 80 or more of the shares
- # Configurations
- $SameFileNamePercentageThreshold = .8
- $SameFileNameCountThreshold = 1
- $SameFileNameMeetsThresholds = 0
- # Get list of files from share name folder groups
- $FullFileListSim = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select FileListGroup,FileList -Unique | Select FileList | foreach {$_.FileList -split "`r`n"} | Where-Object { $_.Trim() -ne "" }
- # Count how many of each file name exist for the share name
- $FullFileListSimCounts = $FullFileListSim | Group-Object | sort count -Descending | select count,name
- # Foreach file determine if any of them exist in 80% or more of the folder groups
- $FullFileListSimCounts80 = $FullFileListSimCounts |
- Foreach{
- $SameFileNameCount = $_.count
- $SameFileName = $_.name
- $SameFileNameCoverage = [math]::Round($SameFileNameCount/$ShareFolderGroupCount,4)
- if($SameFileNameCoverage -ge $SameFileNamePercentageThreshold){
- $object = New-Object psobject
- $object | add-member noteproperty count $SameFileNameCount
- $object | add-member noteproperty name $SameFileName
- $object
- }
- }
- # Count how many files are over 80%
- If($FullFileListSimCounts80){
- $FullFileListSimCounts80Count = $FullFileListSimCounts80 | Measure | select count -expandproperty count
- if($FullFileListSimCounts80Count -ge $SameFileNameCountThreshold){
- $SameFileNameMeetsThresholds = 1
- }
- }
+ # Set the weighted values
+ $SimularityFolderGroupCoverage10w = $SimularityFolderGroupCoverage10 * 10
+ $SimularityFolderGroupCoverage20w = $SimularityFolderGroupCoverage20 * 10
+ $SimularityFolderGroupCoverage30w = $SimularityFolderGroupCoverage30 * 10
+ $SimularityFolderGroupCoverage40w = $SimularityFolderGroupCoverage40 * 10
+ $SimularityFolderGroupCoverage50w = $SimularityFolderGroupCoverage50 * 10
+ $SimularityFolderGroupCoverage60w = $SimularityFolderGroupCoverage60 * 10
+ $SimularityFolderGroupCoverage70w = $SimularityFolderGroupCoverage70 * 10
+ $SimularityFolderGroupCoverage80w = $SimularityFolderGroupCoverage80 * 10
+ $SimularityFolderGroupCoverage90w = $SimularityFolderGroupCoverage90 * 10
+ $SimularityFolderGroupCoverage100w = $SimularityFolderGroupCoverage100 * 10
- # Calculate share to creation date ratio (just informational, not used in similarity score - for now)
- $ShareCreateCount = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select creationdate -Unique | Measure-Object | Select-Object count -ExpandProperty count
- $SimularityCalcCreateDate1 = [math]::Round($ShareCount/$ShareCreateCount,4)
- $SimularityCalcCreateDate = $SimularityCalcCreateDate1 / $ShareCount
+ # Set max value
+ $SimularityFolderGroupCoverageMax = 100
- # Calculate share to modification date ratio (just informational, not used in similarity score - for now)
- $ShareModifiedCount = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select LastModifiedDate -Unique | Measure-Object | Select-Object count -ExpandProperty count
- $SimularityCalcLastModDate1 = [math]::Round($ShareCount/$ShareModifiedCount,4)
- $SimularityCalcLastModDate = $SimularityCalcLastModDate1 / $ShareCount
+ # Set actual value
+ $SimularityFolderGroupCoverageValue = $SimularityFolderGroupCoverage10w +
+ $SimularityFolderGroupCoverage20w +
+ $SimularityFolderGroupCoverage30w +
+ $SimularityFolderGroupCoverage40w +
+ $SimularityFolderGroupCoverage50w +
+ $SimularityFolderGroupCoverage60w +
+ $SimularityFolderGroupCoverage70w +
+ $SimularityFolderGroupCoverage80w +
+ $SimularityFolderGroupCoverage90w +
+ $SimularityFolderGroupCoverage100w
+
+
+ # Calculate Weighted Score
+ $SimularityFolderGroupCoverageScore = $SimularityFolderGroupCoverageValue / $SimularityFolderGroupCoverageMax
+ $SimularityFolderGroupCoverageScoreP1 = [math]::round(($SimularityFolderGroupCoverageScore.tostring("P") -replace('%','')))
+ $SimularityFolderGroupCoverageScoreP = "$SimularityFolderGroupCoverageScoreP1%"
- # Calculate combined similarity score
- # WeightFn802 = 5
- # WeightFileGroup = 4
- # Weightfg50 = 3
- # Weightfg30 = 2
- # WeightFgOwnerAvg = 2
- # WeightCreate = 1
- # WeightLastMod = 1
- # condense into 0-1, low (0-.50), medium(.51-.80), high similary (.81-1)
- $SameFileNameMeetsThresholdsFinal = $SameFileNameMeetsThresholds * 5 # A file exists in 80% of the file groups associated with the sharename
- $SimularityCalcShareFgFinal = $SimularityCalcShareFg * 4 # File group ratio 11
- $SimularityCalc50PFinal = $SimularityCalc50P * 3 # A file group exists that represent 50% or more of the fg population for the sharename
- $SimularityCalOver30Final = $SimularityCalOver30 * 2 # A file group exists that represent 30% or more of the fg population for the sharename
- $SimularityCalcFGOwnerAvgFinal = $SimularityCalcFGOwnerAvg * 2 # Owner to share file group ratio average
- $SimularityCalcCreateDateFinal = $SimularityCalcCreateDate * 1 # Share to creation date ratio
- $SimularityCalcLastModDateFinal = $SimularityCalcLastModDate * 1 # Share to modification date ratio
+ ## ---
+ ## File Name Coverage Weighted Calculations
+ ## ---------------------------------------------
- # Max is 5 + 4 + 3 + 2 + 2 + 1 + 1 = 17; Min is 0
- $SimilarityTotal = $SimularityCalcShareFgFinal + $SameFileNameMeetsThresholdsFinal + $SimularityCalc50PFinal + $SimularityCalOver30Final + $SimularityCalcFGOwnerAvgFinal +$SimularityCalcCreateDateFinal + $SimularityCalcLastModDateFinal
- $SimilarityScore = $SimilarityTotal / 18
- $SimilarityScoreP1 = [math]::round(($SimilarityScore.tostring("P") -replace('%','')))
- $SimilarityScoreP = "$SimilarityScoreP1%"
- If($SimilarityScore -gt .80){ $SimLevel = "High"}
- If($SimilarityScore -lt .80){ $SimLevel = "Medium"}
- If($SimilarityScore -lt .50){ $SimLevel = "Low"}
+ ##
+ ## Determine if at least one file is the same across the target percentages of file groups for the target share.
- # Calculate similarity here - END
- # ----
- ######
- # ----
+ # Start all values at 0
+ $SimularityFileCoverageScore = 0
+ $SimularityFileCoverage10 = 0
+ $SimularityFileCoverage20 = 0
+ $SimularityFileCoverage30 = 0
+ $SimularityFileCoverage40 = 0
+ $SimularityFileCoverage50 = 0
+ $SimularityFileCoverage60 = 0
+ $SimularityFileCoverage70 = 0
+ $SimularityFileCoverage80 = 0
+ $SimularityFileCoverage90 = 0
+ $SimularityFileCoverage100 = 0
+ $SimularityFileCommonList = ""
+ $SimularityFileCommonList = New-Object System.Data.DataTable
+ $SimularityFileCommonList.Columns.Add("Count") | Out-Null
+ $SimularityFileCommonList.Columns.Add("FileName") | Out-Null
+ $SimularityFileCommonList.Columns.Add("Coverage") | Out-Null
+
+ # Get a list of file names from each folder group for the target share name
+ $FullFileListSim = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select FileListGroup,FileList -Unique | Select FileList | foreach {$_.FileList -split "`r`n"} | Where-Object {$_ -ne ''}
+
+ # Count how many file groups (file name instances) each file is seen in
+ $FullFileListSimCounts = $FullFileListSim | Group-Object | sort count -Descending | select count,name | Where-Object {$_.name -ne ''}
+
+ # Determine if at least one file meets each coverage threshold
+ $FullFileListSimCounts |
+ Foreach{
+ [string]$SameFileNameCount = $_.count
+ [string]$SameFileName = $_.name
+ $SameFileNameCoverage = [math]::Round($SameFileNameCount/$ShareFolderGroupCount,4)
+
+ # Check for 10% coverage
+ if($SameFileNameCoverage -ge .10){
+ $SimularityFileCoverage10 = 1
+ $SimularityFileCommonList.rows.Add($SameFileNameCount,"$SameFileName",$SameFileNameCoverage) | Out-Null
+ }
+
+ # Check for 20% coverage
+ if($SameFileNameCoverage -ge .20){
+ $SimularityFileCoverage20 = 1
+ $SimularityFileCommonList.rows.Add($SameFileNameCount,"$SameFileName",$SameFileNameCoverage) | Out-Null
+ }
+
+ # Check for 30% coverage
+ if($SameFileNameCoverage -ge .30){
+ $SimularityFileCoverage30 = 1
+ $SimularityFileCommonList.rows.Add($SameFileNameCount,"$SameFileName",$SameFileNameCoverage) | Out-Null
+ }
+
+ # Check for 40% coverage
+ if($SameFileNameCoverage -ge .40){
+ $SimularityFileCoverage40 = 1
+ $SimularityFileCommonList.rows.Add($SameFileNameCount,"$SameFileName",$SameFileNameCoverage) | Out-Null
+ }
+
+ # Check for 50% coverage
+ if($SameFileNameCoverage -ge .51){
+ $SimularityFileCoverage50 = 1
+ $SimularityFileCommonList.rows.Add($SameFileNameCount,"$SameFileName",$SameFileNameCoverage) | Out-Null
+ }
+
+ # Check for 60% coverage
+ if($SameFileNameCoverage -ge .60){
+ $SimularityFileCoverage60 = 1
+ $SimularityFileCommonList.rows.Add($SameFileNameCount,"$SameFileName",$SameFileNameCoverage) | Out-Null
+ }
+
+ # Check for 70% coverage
+ if($SameFileNameCoverage -ge .70){
+ $SimularityFileCoverage70 = 1
+ $SimularityFileCommonList.rows.Add($SameFileNameCount,"$SameFileName",$SameFileNameCoverage) | Out-Null
+ }
+
+ # Check for 80% coverage
+ if($SameFileNameCoverage -ge .80){
+ $SimularityFileCoverage80 = 1
+ $SimularityFileCommonList.rows.Add($SameFileNameCount,"$SameFileName",$SameFileNameCoverage) | Out-Null
+ }
+
+ # Check for 90% coverage
+ if($SameFileNameCoverage -ge .90){
+ $SimularityFileCoverage90 = 1
+ $SimularityFileCommonList.rows.Add($SameFileNameCount,"$SameFileName",$SameFileNameCoverage) | Out-Null
+ }
+
+ # Check for 100% coverage
+ if($SameFileNameCoverage -ge 1){
+ $SimularityFileCoverage100 = 1
+ $SimularityFileCommonList.rows.Add($SameFileNameCount,"$SameFileName",$SameFileNameCoverage) | Out-Null
+ }
+ }
+
+ # Set common file constraints
+ If($ShareCount -eq 1){
+ $CommonFileConstraint = 0
+ }else{
+ $CommonFileConstraint = 1
+ }
+
+ # Select a list of all files found in 10% or more of folder groups
+ $SimularityFileCommonListTop = $SimularityFileCommonList | where Coverage -ge .10 | where Count -gt $CommonFileConstraint |
+ foreach {
+ $SimularityFileTopName = $_.FileName
+ $SimularityFileTopCount = $_.Count
+ $SimularityFileTopCountCalc = [math]::Round($SimularityFileTopCount/$ShareFolderGroupCount,4)
+ $SimularityFileTopCPercentage = ($SimularityFileTopCountCalc * 100).ToString() + '%'
+ "
$SimularityFileTopName $SimularityFileTopCPercentage ($SimularityFileTopCount) "
+ } | select -Unique
+
+ # This supports situations when the a file exist in more than one group, but the total folder group count is less than 10
+ # we need this should we only show common files with more than one instance
+ If($ShareFolderGroupCount -lt 10 -and $ShareFolderGroupCount -gt 1 -and $ShareCount -gt 2) {
+ $SimularityFileCommonListTop = $SimularityFileCommonList | where Count -gt 1 |
+ foreach {
+ $SimularityFileTopName = $_.FileName
+ $SimularityFileTopCount = $_.Count
+ $SimularityFileTopCountCalc = [math]::Round($SimularityFileTopCount/$ShareFolderGroupCount,4)
+ $SimularityFileTopCPercentage = ($SimularityFileTopCountCalc * 100).ToString() + '%'
+ "$SimularityFileTopName $SimularityFileTopCPercentage ($SimularityFileTopCount) "
+ } | select -Unique
+ }
+
+ # This supports situations when the a file exist in more than one group, but there is only one group
+ If($ShareFolderGroupCount -eq 1 -and $ShareCount -gt 1) {
+ $SimularityFileCommonListTop = $SimularityFileCommonList |
+ foreach {
+ $SimularityFileTopName = $_.FileName
+ $SimularityFileTopCount = $_.Count
+ $SimularityFileTopCountCalc = [math]::Round($SimularityFileTopCount/$ShareFolderGroupCount,4)
+ $SimularityFileTopCPercentage = ($SimularityFileTopCountCalc * 100).ToString() + '%'
+ "$SimularityFileTopName $SimularityFileTopCPercentage ($SimularityFileTopCount) "
+ } | select -Unique
+ }
+
+ # Set count for display
+ $SimularityFileCommonListTopNum = $SimularityFileCommonListTop | measure | select count -ExpandProperty count
+
+ # Format list for display
+ $SimularityFileCommonListTxt = $SimularityFileCommonListTop -join "`n"
+
+ # Set the weighted values
+ $SimularityFileCoverage10w = $SimularityFileCoverage10 * 10
+ $SimularityFileCoverage20w = $SimularityFileCoverage20 * 10
+ $SimularityFileCoverage30w = $SimularityFileCoverage30 * 10
+ $SimularityFileCoverage40w = $SimularityFileCoverage40 * 10
+ $SimularityFileCoverage50w = $SimularityFileCoverage50 * 10
+ $SimularityFileCoverage60w = $SimularityFileCoverage60 * 10
+ $SimularityFileCoverage70w = $SimularityFileCoverage70 * 10
+ $SimularityFileCoverage80w = $SimularityFileCoverage80 * 10
+ $SimularityFileCoverage90w = $SimularityFileCoverage90 * 10
+ $SimularityFileCoverage100w = $SimularityFileCoverage100 * 10
+
+ # Set max value
+ $SimularityFileCoverageMax = 100
+
+ # Set actual value
+ $SimularityFileCoverageValue = $SimularityFileCoverage10w +
+ $SimularityFileCoverage20w +
+ $SimularityFileCoverage30w +
+ $SimularityFileCoverage40w +
+ $SimularityFileCoverage50w +
+ $SimularityFileCoverage60w +
+ $SimularityFileCoverage70w +
+ $SimularityFileCoverage80w +
+ $SimularityFileCoverage90w +
+ $SimularityFileCoverage100w
+
+
+ # Calculate Weighted Score
+ $SimularityFileCoverageScore = $SimularityFileCoverageValue / $SimularityFileCoverageMax
+ $SimularityFileCoverageScoreP1 = [math]::round(($SimularityFileCoverageScore.tostring("P") -replace('%','')))
+ $SimularityFileCoverageScoreP = "$SimularityFileCoverageScoreP1%"
+
+ ## ---
+ ## Share Properties Weight Group Calculations
+ ## ---------------------------------------------
+
+ ##
+ ## Calculate share name ratio
+ ## Output Value: 0 or 1
+ $SimularitySharePropShareName = 1
+
+ ##
+ ## Calculate creation date ratio
+ ## Output Value: 0 to 1
+ $ShareCreateCount = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select creationdate -Unique | Measure-Object | Select-Object count -ExpandProperty count
+ $SimularityCalcCreateDate1 = [math]::Round($ShareCount/$ShareCreateCount,4)
+ $SimularitySharePropCreateDateRatio = $SimularityCalcCreateDate1 / $ShareCount
+ $SimularitySharePropCreateDateRatioT = $SimularitySharePropCreateDateRatio.ToString("F2")
+
+ ##
+ ## Calculate modification date ratio
+ ## Output Value: 0 to 1
+ $ShareModifiedCount = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select LastModifiedDate -Unique | Measure-Object | Select-Object count -ExpandProperty count
+ $SimularityCalcLastModDate1 = [math]::Round($ShareCount/$ShareModifiedCount,4)
+ $SimularitySharePropModDateRatio = $SimularityCalcLastModDate1 / $ShareCount
+ $SimularitySharePropModDateRatioT = $SimularitySharePropModDateRatio.ToString("F2")
+
+ ##
+ ## Calculate file group owner ratio average
+ ## Output Value: 0 to 1
+
+ # Reset avg score
+ $SimularitySharePropFGOwnerAvg = 0
+
+ # Foreach folder group calculate owner to folder group ratio
+ $FGtoOwnerFGList = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | select FileListGroup -Unique |
+ foreach{
+
+ # Get folder group name
+ $FGtoOwnerFG = $_.FileListGroup
+
+ # Get number of shares in folder group
+ $FGtoOwnerShareCount = $ExcessiveSharePrivs | where ShareName -EQ "$ShareName" | where FileListGroup -eq "$FGtoOwnerFG" | select SharePath -Unique | Measure-Object | select count -ExpandProperty count
+
+ # Get number of owners associated with folder group
+ $FGtoOwnerOwnerCount = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | where FileListGroup -EQ "$FGtoOwnerFG" | select ShareOwner -Unique | Measure-Object | select count -ExpandProperty count
+
+ # Calculate fg to owner ratio
+ $FGtoOwnerThings = $FGtoOwnerOwnerCount/$FGtoOwnerShareCount
+ $FGtoOwnerThings
+ }
+
+ # Calculate file group owner ratio average
+ $FGtoOwnerMax = $FGtoOwnerFGList | measure | select count -ExpandProperty count
+ $FGtoOwnerValue = ($FGtoOwnerFGList | measure -sum).sum
+ $SimularitySharePropFGOwnerAvg = [math]::Round($FGtoOwnerValue/$FGtoOwnerMax,4)
+ $SimularitySharePropFGOwnerAvgT = $SimularitySharePropFGOwnerAvg.ToString("F2")
+
+ ##
+ ## Calculate group score
+
+ # Set the weighted values
+ $SimularitySharePropShareNameW = $SimularitySharePropShareName * 90
+ $SimularitySharePropFGOwnerAvgW = $SimularitySharePropFGOwnerAvg * 5
+ $SimularitySharePropCreateDateRatioW = $SimularitySharePropCreateDateRatio * 3
+ $SimularitySharePropModDateRatioW = $SimularitySharePropModDateRatio * 2
+
+ # Set max value
+ $SimularitySharePropCoverageMax = 100
+
+ # Set actual value
+ $SimularitySharePropCoverageValue = $SimularitySharePropShareNameW +
+ $SimularitySharePropFGOwnerAvgW +
+ $SimularitySharePropCreateDateRatioW +
+ $SimularitySharePropModDateRatioW
+ # Calculate Weighted Score
+ $SimularitySharePropCoverageScore = $SimularitySharePropCoverageValue / $SimularitySharePropCoverageMax
+ $SimularitySharePropCoverageScoreP1 = [math]::round(($SimularitySharePropCoverageScore.tostring("P") -replace('%','')))
+ $SimularitySharePropCoverageScoreP = "$SimularityFolderGroupCoverageScoreP1%"
+
+ ## ---
+ ## General metrics (Not included in similarity score, but displayed in details)
+ ## ---------------------------------------------
+
+ # Calculate the share to owner ratio
+ # Output value: 0 to 1
+ $SimularityCalcShareOwner1 = [math]::Round($ShareCount/$ShareOwnerListCount,4)
+ $SimularityCalcShareOwner = $SimularityCalcShareOwner1 / $ShareCount
+ $SimularityCalcShareOwner = $SimularityCalcShareOwner.ToString("F2")
+
+ ## Calculate share to folder group ratio
+ # Output value: 0 to 1
+ $SimularityCalcShareFg1 = [math]::Round($ShareCount/$ShareFolderGroupCount,4) # Original value
+ $SimularityCalcShareFg = $SimularityCalcShareFg1 / $ShareCount
+ $SimularityCalcShareFg = $SimularityCalcShareFg.ToString("F2")
+
+ ##
+ ## Calculate if all descriptions for the target share name are the same.
+ ## Output Value: 0 or 1
+ $ShareDescriptionCount = $ExcessiveSharePrivs | where sharename -EQ "$ShareName" | where ShareDescription -NE ""| select ShareDescription -Unique | measure | select count -ExpandProperty count
+ If($ShareDescriptionCount -ge 1){
+ $SimularityCalcShareDesc = 1
+ }else{
+ $SimularityCalcShareDesc = 0
+ }
+
+ ## ---
+ ## Final Similarity Weighting V1
+ ## ---------------------------------------------
+
+ # Normalize Weight Groups
+ # - File Name Coverage Weight Group
+ # - File Group Coverage Wieght Group
+ # - Share Properties Weight Group
+
+ # Set final max
+ $FinalSimilarityMax = 3.85
+
+ # Set weighted values
+ if($SimularityFileCoverageScore -eq 0){
+
+ # 0 matches patch; means all folders are empty, so we want to weight up.
+ $SimularityFileCoverageScoreW = 2.25
+ }else{
+
+ # Default setting
+ $SimularityFileCoverageScoreW = $SimularityFileCoverageScore * 2.25
+ }
+ $SimularityFolderGroupCoverageScoreW = $SimularityFolderGroupCoverageScore * 1
+ $SimularitySharePropCoverageScoreW = $SimularitySharePropCoverageScore * .6
+
+ # Set final value
+ $FinalSimilarityValue = $SimularityFolderGroupCoverageScoreW +
+ $SimularityFileCoverageScoreW +
+ $SimularitySharePropCoverageScoreW
+
+ # Set cumulative score
+ $FinalSimilarityScore = $FinalSimilarityValue / $FinalSimilarityMax
+ $FinalSimilarityScoreP1 = [math]::round(($FinalSimilarityScore.tostring("P") -replace('%','')))
+ $FinalSimilarityScoreP = "$FinalSimilarityScoreP1%"
+
+ #
+ # Set similarity classification
+ #
+ If($FinalSimilarityScore -ge .80){ $SimLevel = "$FinalSimilarityScoreP High"}
+ If($FinalSimilarityScore -ge .95){ $SimLevel = "$FinalSimilarityScoreP Very High"}
+ If($FinalSimilarityScore -lt .80){ $SimLevel = "$FinalSimilarityScoreP Medium"}
+ If($FinalSimilarityScore -lt .50){ $SimLevel = "$FinalSimilarityScoreP Low"}
+
+ # ----------------------------------------------------------------------
+ # Calculate Similarity Score - END
+ # ----------------------------------------------------------------------
+ #endregion SimilarityScore
+
+ #region PeakDateRange
+ # ----------------------------------------------------------------------
# Calculate peak event date range - START
+ # ----------------------------------------------------------------------
# Assumptions: a) if only two unique dates exist, then both will be included in the observation window.
# Count total number of events
@@ -1872,22 +2206,493 @@ function Invoke-HuntSMBShares
$ObservationWindowsEndDate = $ObservationWindowsEndDate + $ObservationWindow
}
+ # ----------------------------------------------------------------------
# Calculate peak event date range - END
- # ----
+ # ----------------------------------------------------------------------
+ #endregion PeakDateRange
+
+ #region Access Checks
+ # ----------------------------------------------------------------------
+ # Check share access types for name - START
+ # ----------------------------------------------------------------------
+
+ # Check if high risk
+ try{
+ $ShareRowHighRisk = $ExcessiveSharePrivs | Where ShareName -like "$ShareName" |
+ Foreach {
+ if(($_.ShareName -like 'c$') -or ($_.ShareName -like 'admin$') -or ($_.ShareName -like "*wwwroot*") -or ($_.ShareName -like "*inetpub*") -or ($_.ShareName -like 'c') -or ($_.ShareName -like 'c_share'))
+ {
+ $_ # out to file
+ }
+ }
+
+ $ShareRowCountHRAcls = $ShareRowHighRisk | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountHRShares = $ShareRowHighRisk | Select-Object SharePath -Unique | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountHRComputers = $ShareRowHighRisk | Select-Object ComputerName -Unique | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountHRSharesCacl = $ShareRowCountHRShares / $ShareCount
+ $ShareRowCountHRSharesCaclP1 = [math]::round(($ShareRowCountHRSharesCacl.tostring("P") -replace('%','')))
+ $ShareRowCountHRSharesCaclP = "$ShareRowCountHRSharesCaclP1% ($ShareRowCountHRShares)"
+
+ if($ShareRowCountHRAcls -gt 0){
+ $ShareRowHasHighRisk = "Yes"
+ }else{
+ $ShareRowHasHighRisk = "No"
+ }
+ }catch{
+ $ShareRowCountHRAcls = "Unknown"
+ $ShareRowCountHRShares = "Unknown"
+ $ShareRowCountHRComputers = "Unknown"
+ $ShareRowHasHighRisk = "Unknown"
+ }
+
+ # Check if write
+ try{
+ $ShareRowWrite = $ExcessiveSharePrivs | Where ShareName -like "$ShareName" |
+ Foreach {
+ if(($_.FileSystemRights -like "*GenericAll*") -or ($_.FileSystemRights -like "*Write*"))
+ {
+ $_ # out to file
+ }
+ }
+
+ $ShareRowCountWriteAcls = $ShareRowWrite | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountWriteShares = $ShareRowWrite | Select-Object SharePath -Unique | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountWriteComputers = $ShareRowWrite | Select-Object ComputerName -Unique | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountwriteSharesCacl = $ShareRowCountwriteShares / $ShareCount
+ $ShareRowCountwriteSharesCaclP1 = [math]::round(($ShareRowCountwriteSharesCacl.tostring("P") -replace('%','')))
+ $ShareRowCountwriteSharesCaclP = "$ShareRowCountwriteSharesCaclP1% ($ShareRowCountwriteShares)"
+
+ if($ShareRowCountWriteAcls -gt 0){
+ $ShareRowHasWrite = "Yes"
+ }else{
+ $ShareRowHasWrite = "No"
+ }
+ }catch{
+ $ShareRowCountWriteAcls = "Unknown"
+ $ShareRowCountWriteShares = "Unknown"
+ $ShareRowCountWriteComputers = "Unknown"
+ $ShareRowHasWrite = "Unknown"
+ }
+
+ # Check if read
+ try{
+ $ShareRowRead = $ExcessiveSharePrivs | Where ShareName -like "$ShareName" |
+ Foreach {
+ if(($_.FileSystemRights -like "*read*"))
+ {
+ $_ # out to file
+ }
+ }
+
+ $ShareRowCountReadAcls = $ShareRowRead | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountReadShares = $ShareRowRead | Select-Object SharePath -Unique | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountReadComputers = $ShareRowRead | Select-Object ComputerName -Unique | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountReadSharesCacl = $ShareRowCountReadShares / $ShareCount
+ $ShareRowCountReadSharesCaclP1 = [math]::round(($ShareRowCountReadSharesCacl.tostring("P") -replace('%','')))
+ $ShareRowCountReadSharesCaclP = "$ShareRowCountReadSharesCaclP1% ($ShareRowCountReadShares)"
+
+ if($ShareRowCountReadAcls -gt 0){
+ $ShareRowHasRead = "Yes"
+ }else{
+ $ShareRowHasRead = "No"
+ }
+ }catch{
+ $ShareRowCountReadAcls = "Unknown"
+ $ShareRowCountReadShares = "Unknown"
+ $ShareRowCountReadComputers = "Unknown"
+ $ShareRowHasRead = "Unknown"
+ }
+
+ # Check if non defaults
+ try{
+ $ShareRowNonDefault = $ExcessiveSharePrivs | Where ShareName -like "$ShareName" |
+ Foreach {
+ if(($_.ShareName -like 'admin$') -or ($_.ShareName -like 'c$') -or ($_.ShareName -like 'd$') -or ($_.ShareName -like 'e$') -or ($_.ShareName -like 'f$'))
+ {
+ $_ # out to file
+ }
+ }
+
+ $ShareRowCountNonDefaultAcls = $ShareRowNonDefault | Measure-Object | select count -ExpandProperty count
+ if($ShareRowCountNonDefaultAcls -gt 0){
+ $ShareRowHasDefault = "Yes"
+ }else{
+ $ShareRowHasDefault = "No"
+ }
+ }catch{
+ $ShareRowHasDefault = "Unknown"
+ }
+
+ # Check if empty
+ try{
+ $ShareRowEmpty = $ExcessiveSharePrivs | Where ShareName -like "$ShareName" | where FileCount -eq 0
+
+ $ShareRowCountEmptyAcls = $ShareRowEmpty | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountEmptyShares = $ShareRowEmpty | Select-Object SharePath -Unique | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountEmptyComputers = $ShareRowEmpty | Select-Object ComputerName -Unique | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountEmptySharesCacl = $ShareRowCountEmptyShares / $ShareCount
+ $ShareRowCountEmptySharesCaclP1 = [math]::round(($ShareRowCountEmptySharesCacl.tostring("P") -replace('%','')))
+ $ShareRowCountEmptySharesCaclP = "$ShareRowCountEmptySharesCaclP1% ($ShareRowCountEmptyShares)"
+
+ if($ShareRowCountEmptyAcls -gt 0){
+ $ShareRowHasEmpty = "Yes"
+ }else{
+ $ShareRowHasEmpty = "No"
+ }
+ }catch{
+ $ShareRowCountEmptyAcls = "Unknown"
+ $ShareRowCountEmptyShares = "Unknown"
+ $ShareRowCountEmptyComputers = "Unknown"
+ $ShareRowHasEmpty = "Unknown"
+ }
+
+ # Check if stale
+ try{
+ $oneYearAgo = (Get-Date).AddYears(-1)
+ $ShareRowStale = $ExcessiveSharePrivs | Where ShareName -like "$ShareName" | where { $_.LastModifiedDate-ge $oneYearAgo }
+
+ $ShareRowCountStaleAcls = $ShareRowStale | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountStaleShares = $ShareRowStale | Select-Object SharePath -Unique | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountStaleComputers = $ShareRowStale | Select-Object ComputerName -Unique | Measure-Object | select count -ExpandProperty count
+ $ShareRowCountStaleSharesCacl = $ShareRowCountStaleShares / $ShareCount
+ $ShareRowCountStaleSharesCaclP1 = [math]::round(($ShareRowCountStaleSharesCacl.tostring("P") -replace('%','')))
+ $ShareRowCountStaleSharesCaclP = "$ShareRowCountStaleSharesCaclP1% ($ShareRowCountStaleShares)"
+
+ if($ShareRowCountStaleAcls -gt 0){
+ $ShareRowHasStale = "Yes"
+ }else{
+ $ShareRowHasStale = "No"
+ }
+ }catch{
+ $ShareRowCountStaleAcls = "Unknown"
+ $ShareRowCountStaleShares = "Unknown"
+ $ShareRowCountStaleComputers = "Unknown"
+ $ShareRowHasStale = "Unknown"
+ }
+
+ # Set default
+ $ShareRowCountInteresting = "No"
+
+ # Define common image and other formats to filter out later
+ $ImageFormats = @("*.jpg", "*.jpeg", "*.png", "*.gif", "*.bmp", "*.ico", "*.svg", "*.webp", "*.mif", "*.heic", "*.msi")
+
+ # Check if interesting files - Secrets
+ # Files that may contain passwords, key, or other authentication tokens
+ $ShareRowInterestingFileListSecrets = ""
+ $ShareRowInterestingFileListSecretsCount = 0
+ $ShareRowCountInterestingSecrets = "No"
+ $FileNamePatternsSecrets = @(
+ "*.bacpac*",
+ "*.bat*",
+ "*.config*",
+ "*.dtsx*",
+ "*.json*",
+ "*.ps1*",
+ "*.psm1*",
+ "*.UDL*",
+ "*config.php*",
+ "*Credentials*",
+ "*Creds*",
+ "*keys*",
+ "*pass*",
+ "*private*",
+ "*secret*",
+ "*secure*",
+ "*security*",
+ "*web.conf*",
+ "*htaccess*",
+ "*htpasswd*",
+ "*inetpub*",
+ "applicationhost.config*",
+ "auth*",
+ "config.xml*",
+ "context.xml*",
+ "db2cli.ini*",
+ "ftpd.*",
+ "ftpusers*",
+ "httpd.conf*",
+ "hudson.security.HudsonPrivateSecurityRealm.*",
+ "jboss-cli.xml*",
+ "jboss-logmanager.properties*",
+ "jenkins.model.JenkinsLocationConfiguration.*",
+ "machine.config*",
+ "my.*",
+ "mysql.user*",
+ "nginx.conf*",
+ "pg_hba.conf*",
+ "php.ini*",
+ "putty.reg*",
+ "postgresql.conf*",
+ "SAM",
+ "SAM-*",
+ "SAM_*",
+ "server.xml*",
+ "shadow*",
+ "standalone.xml*",
+ "tnsnames.ora*",
+ "tomcat-users.xml*",
+ "sitemanager.xml*",
+ "users.*",
+ "*.vmx*",
+ "*.vmdk*",
+ "*.nvram*",
+ "*.vmsd*",
+ "*.vmsn*",
+ "*.vmss*",
+ "*.vmem*",
+ "*.vhd*",
+ "*.vhdx*",
+ "*.avhd*",
+ "*.avhdx*",
+ "*.vsv*",
+ "*.vbox*",
+ "*.vbox-prev*",
+ "*.vdi*",
+ "*.hdd*",
+ "*.sav*",
+ "*.pvm*",
+ "*.pvs*",
+ "*.qcow*",
+ "*.qcow2*",
+ "*.img*",
+ "*vcenter*",
+ "*vault*",
+ "*DefaultAppPool*",
+ "*WinSCP.ini*",
+ "*.kdbx",
+ "wp-config.php*"
+ )
+
+ # Check for matches
+ $ShareRowInterestingFileListSecrets = foreach ($pattern in $FileNamePatternsSecrets) {
+
+ # Parse the filenames for all shares with the target name into a list
+ # $FullFileListSim - define in above sections
+
+ # Check if interesting file pattern matches the file name
+ $FullFileListSim |
+ foreach {
+
+ # Reset exclude flag
+ $ExcludeThisFile = 0
+
+ # File to check
+ $CheckThisfileOut = $_
+
+ # Check if it should be excluded
+ $ImageFormats |
+ foreach{
+ if($CheckThisfileOut -like "$_"){
+ $ExcludeThisFile = 1
+ }
+ }
+
+ # Check if it matches pattern
+ if ($_ -like $pattern -and $ExcludeThisFile -eq 0) {
+
+ # v1 add file name to link
+ "$_ "
+
+ # v2 add hyperlinked sharepath that expands betwen the: file name %(count)
+ }
+ }
+ }
+
+ $ShareRowInterestingFileListSecrets = $ShareRowInterestingFileListSecrets | select -Unique | sort
+
+ # Count number of interesting files and update status
+ $ShareRowInterestingFileListSecretsCount = $ShareRowInterestingFileListSecrets | select -Unique | sort | Measure | select count -ExpandProperty count
+ if( $ShareRowInterestingFileListSecretsCount -gt 0){
+ $ShareRowCountInterestingSecrets = "Yes"
+ $ShareRowCountInteresting = "Yes"
+ }
+
+ # Check if interesting files - Sensitive Data
+ $ShareRowInterestingFileListData = ""
+ $ShareRowInterestingFileListDataCount = 0
+ $ShareRowCountInterestingData = "No"
+ $FileNamePatternsData = @(
+ "*credit*",
+ "*card*",
+ "*pci*",
+ "*social*",
+ "*ssn*",
+ "*database*",
+ "human*",
+ "finance*",
+ "Health*",
+ "Billing*",
+ "patient*",
+ "*.bak*",
+ "*backup*",
+ "*.sql",
+ "*.mdb",
+ "*.mdf",
+ "*.idf",
+ "*.sqlite",
+ "*ftp",
+ "*Program Files*",
+ "HR*"
+ )
+
+ # Check for matches
+ $ShareRowInterestingFileListData = foreach ($pattern in $FileNamePatternsData) {
+
+ # Parse the filenames for all shares with the target name into a list
+ # $FullFileListSim - define in above sections
+
+ # Check if interesting file pattern matches the file name
+ $FullFileListSim |
+ foreach {
+
+ # Reset exclude flag
+ $ExcludeThisFile = 0
+
+ # File to check
+ $CheckThisfileOut = $_
+
+ # Check if it should be excluded
+ $ImageFormats |
+ foreach{
+ if($CheckThisfileOut -like "$_"){
+ $ExcludeThisFile = 1
+ }
+ }
+
+ if ($_ -like $pattern-and $ExcludeThisFile -eq 0) {
+ # v1 add file name to link
+ "$_ "
+
+ # v2 add hyperlinked sharepath that expands betwen the: file name %(count)
+ }
+ }
+ }
+
+ $ShareRowInterestingFileListData = $ShareRowInterestingFileListData | select -Unique | sort
+
+ # Count number of interesting files and update status
+ $ShareRowInterestingFileListDataCount = $ShareRowInterestingFileListData | select -Unique | sort | Measure | select count -ExpandProperty count
+ if( $ShareRowInterestingFileListDataCount -gt 0){
+ $ShareRowCountInterestingData = "Yes"
+ $ShareRowCountInteresting = "Yes"
+ }
+
+ $ShareRowInterestingFileTotalCount = $ShareRowInterestingFileListDataCount + $ShareRowInterestingFileListSecretsCount
+
+ # Build coverage icon set
+ $CoverageIcons = ""
+ if($ShareRowHasHighRisk -eq "Yes"){ $CoverageIcons = $CoverageIcons + " " }
+ if($ShareRowHasWrite -eq "Yes"){ $CoverageIcons = $CoverageIcons + " " }
+ if($ShareRowHasRead -eq "Yes"){ $CoverageIcons = $CoverageIcons + " " }
+ if($ShareRowCountInteresting -eq "Yes"){ $CoverageIcons = $CoverageIcons + " " }
+ if($ShareRowHasEmpty -eq "Yes"){ $CoverageIcons = $CoverageIcons + " " }
+ if($ShareRowHasStale -eq "Yes"){ $CoverageIcons = $CoverageIcons + " " }
+
+ # ----------------------------------------------------------------------
+ # Check share access types for name - END
+ # ----------------------------------------------------------------------
+ #endregion Check Access
+
+
+ # ----------------------------------------------------------------------
+ # Calculate Risk Level
+ # ----------------------------------------------------------------------
+
+ # Define Weights
+
+ $RiskWeightRCE = 11
+
+ $RiskWeightData = 8
+ $RiskWeightDataVolume = 1
+
+ $RiskWeightSecrets = 2
+ $RiskWeightSecretsVolume = 1
+
+ $RiskWeightWrite = 4
+ $RiskWeightRead = 3
+
+ $RiskWeightLargeVolume = 1
+ $RiskWeightOfficeDocs = 1 # TBD; Placeholder
+
+ $RiskWeightEmpty = -1
+ $RiskWeightStale = -1
+
+ # Calculate Actual Value
+ $ShareNameRiskValue = 0
+ if($ShareRowHasHighRisk -eq "Yes"){ $ShareNameRiskValue = $ShareNameRiskValue + $RiskWeightRCE } # Potential RCE
+ if($ShareRowCountInterestingData -eq "Yes"){ $ShareNameRiskValue = $ShareNameRiskValue + $RiskWeightData } # Potential Sensitive Data
+ if($ShareRowInterestingFileListDataCount -gt 10 ){ $ShareNameRiskValue = $ShareNameRiskValue + $RiskWeightDataVolume } # Potential Sensitive Data Volume
+ if($ShareRowCountInterestingSecrets -eq "Yes"){ $ShareNameRiskValue = $ShareNameRiskValue + $RiskWeightSecrets } # Potential Password Access
+ if($ShareRowInterestingFileListDataCount -gt 10 ){ $ShareNameRiskValue = $ShareNameRiskValue + $RiskWeightSecretsVolume } # Potential Sensitive Data Volume
+ if($ShareRowHasWrite -eq "Yes"){ $ShareNameRiskValue = $ShareNameRiskValue + $RiskWeightWrite } # Write Access
+ if($ShareRowHasRead -eq "Yes"){ $ShareNameRiskValue = $ShareNameRiskValue + $RiskWeightRead } # Read Access
+ if($SimularityFileCommonListTopNum -gt 100 ){ $ShareNameRiskValue = $ShareNameRiskValue + $RiskWeightLargeVolume } # Large number of common files
+ if($ShareRowHasEmpty -eq "Yes"){ $ShareNameRiskValue = $ShareNameRiskValue + $RiskWeightEmpty } # Empty Folders
+ if($ShareRowHasStale -eq "Yes"){ $ShareNameRiskValue = $ShareNameRiskValue + $RiskWeightStale } # Stake Folders
+
+ # Set Risk Score
+ If($ShareNameRiskValue -ge 11 ) { $RiskLevel = "$ShareNameRiskValue High"}
+ If($ShareNameRiskValue -ge 20 ) { $RiskLevel = "$ShareNameRiskValue Very High"}
+ If($ShareNameRiskValue -lt 11 ) { $RiskLevel = "$ShareNameRiskValue Medium"}
+ If($ShareNameRiskValue -lt 4 ) { $RiskLevel = "$ShareNameRiskValue Low"}
+
+ <#
+ # Determine Max Value
+ $ShareNameRiskMax = $RiskWeightRCE +
+ $RiskWeightData +
+ $RiskWeightDataVolume +
+ $RiskWeightSecrets +
+ $RiskWeightSecretsVolume +
+ $RiskWeightWrite +
+ $RiskWeightRead +
+ $RiskWeightLargeVolume
+
+ # Calculate Score
+ $ShareNameRiskScore = $ShareNameRiskValue / $ShareNameRiskMax
+ $ShareNameRiskScoreP1 = [math]::round(($ShareNameRiskScore.tostring("P") -replace('%','')))
+ $ShareNameRiskScoreP = "$ShareNameRiskScoreP1%"
+
+ # Set Risk Score
+ If($ShareNameRiskScore -ge .80){ $RiskLevel = "$ShareNameRiskScoreP High"}
+ If($ShareNameRiskScore -ge .95){ $RiskLevel = "$ShareNameRiskScoreP Very High"}
+ If($ShareNameRiskScore -lt .80){ $RiskLevel = "$ShareNameRiskScoreP Medium"}
+ If($ShareNameRiskScore -lt .50){ $RiskLevel = "$ShareNameRiskScoreP Low"}
+ #>
+
+ # ----------------------------------------------------------------------
+ # Build Share Name Summary Page Rows
+ # ----------------------------------------------------------------------
+ # Build Rows
$ThisRow = @"
-
-
+
+
$ShareCount
-
-
- $ShareName
+
+
+
+ $ShareName
+ $CoverageIcons
+
- $ShareDescriptionSample
-
-
Timeline Context
+ $ShareDescriptionSample
+
Affected Assets
+
+
+ Computers: $ComputerBar
+
+
+ Shares: $ShareBar
+
+
+ ACLs: $AclBar
+
+
+
+
+
Timeline Context
First Created:
@@ -1898,90 +2703,216 @@ function Invoke-HuntSMBShares
$ShareLastCreated
- Last Modified:
+ Last Mod:
$ShareLastModified
-
+
+
-
-
-
Peak Window Details
+
Owners ($ShareOwnerListCount)
+
+ $ShareOwnerListHTML
+
+
+
+
+
+ $RiskLevel
+
+
+
+
Risk Summary
- Total Share Instances:
- $ShareEventCountTotal
+ HE: $ShareRowCountHRSharesCaclP
- Peak Window Instances:
- $ObservationWindowRangecountWinner
+ Write: $ShareRowCountwriteSharesCaclP
- Peak Window Start:
- $ObvWinnerFirst
+ Read: $ShareRowCountReadSharesCaclP
- Peak Window End:
- $ObvWinnerLast
+ Stale: $ShareRowCountStaleSharesCaclP
-
-
-
-
+
+ Empty: $ShareRowCountEmptySharesCaclP
+
+
+ Default: $ShareRowHasDefault
+
+
+ Sensitive: $ShareRowInterestingFileListDataCount
+
+
+ Secrets: $ShareRowInterestingFileListSecretsCount
+
+
+
+
+
- $SimLevel ($SimilarityScoreP)
+ $SimLevel
-
-
Probability Distributions
+
- FolderGroup: $SimularityCalcShareFg
+ Final Weighted Score: : $FinalSimilarityScoreP
+
+
+ File Name Coverage: $SimularityFileCoverageScoreP
+
+
+ Folder Group Coverage: $SimularityFolderGroupCoverageScoreP
+
+
+ Share Property Coverage: $SimularitySharePropCoverageScoreP
+
+
+
---
+
+
File Name Metrics
+
+
+ 1 File FG Coverage 10%: $SimularityFileCoverage10
- OwnerFG: $SimularityCalcFGOwnerAvg
+ 1 File FG Coverage 20%: $SimularityFileCoverage20
- Owner: $SimularityCalcShareOwner
-
-
- 80% FN: $SameFileNameMeetsThresholds
-
-
- 25% FG: $SimularityCalOver30
+ 1 File FG Coverage 30%: $SimularityFileCoverage30
- 50% FG: $SimularityCalc50P
-
-
- Created: $SimularityCalcCreateDate
+ 1 File FG Coverage 40%: $SimularityFileCoverage40
- LastMod: $SimularityCalcLastModDate
+ 1 File FG Coverage 51%: $SimularityFileCoverage50
+
+
+ 1 File FG Coverage 60%: $SimularityFileCoverage60
+
+
+ 1 File FG Coverage 70%: $SimularityFileCoverage70
+
+
+ 1 File FG Coverage 80%: $SimularityFileCoverage80
+
+
+ 1 File FG Coverage 90%: $SimularityFileCoverage90
+
+
+ 1 File FG Coverage 100%: $SimularityFileCoverage100
+
+
+
+
Folder Group Metrics
+
+
+ 1 FG Covers 10% of shares: $SimularityFolderGroupCoverage10
+
+
+ 1 FG Covers 20% of shares: $SimularityFolderGroupCoverage20
+
+
+ 1 FG Covers 30% of shares: $SimularityFolderGroupCoverage30
+
+
+ 1 FG Covers 40% of shares: $SimularityFolderGroupCoverage40
+
+
+ 1 FG Covers 51% of shares: $SimularityFolderGroupCoverage50
+
+
+ 1 FG Covers 60% of shares: $SimularityFolderGroupCoverage60
+
+
+ 1 FG Covers 70% of shares: $SimularityFolderGroupCoverage70
+
+
+ 1 FG Covers 80% of shares: $SimularityFolderGroupCoverage80
+
+
+ 1 FG Covers 90% of shares: $SimularityFolderGroupCoverage90
+
+
+ 1 FG Covers 100% of shares: $SimularityFolderGroupCoverage100
-
+
+
+
Share Property Metrics
+
+
+ Same Share Name: 1
+
+
+ File Group/Owner Ratio Average: $SimularitySharePropFGOwnerAvgT
+
+
+ Creation Date/Share Ratio: $SimularitySharePropCreateDateRatioT
+
+
+ Last Modification Date/Share Ratio: $SimularitySharePropModDateRatioT
+
+
+
Additional Metrics
+
+
+ Share Owner Ratio: $SimularityCalcShareOwner
+
+
+ File Group/Name Ratio: $SimularityCalcShareFg
+
+
+ All Descriptions Match: $SimularityCalcShareDesc
+
+
- $ShareFolderGroupCount
+ $ShareFolderGroupCount
-
-
- $ShareOwnerListCount
-
-
- $ShareOwnerListHTML
-
-
-
+
- $ComputerBar
- $ShareBar
- $AclBar
-
+ $SimularityFileCommonListTopNum Files
+
+
+
+ $SimularityFileCommonListTop
+
+
+
+
+
+
+ $ShareRowInterestingFileTotalCount Files
+
+
+
+
$ShareRowInterestingFileListSecretsCount Secrets Files
+
+
+
+ $ShareRowInterestingFileListSecrets
+
+
+
+
+
$ShareRowInterestingFileListDataCount Data Files
+
+
+
+ $ShareRowInterestingFileListData
+
+
+
+
+
+
"@
$ThisRow
@@ -2280,6 +3211,10 @@ $NewHtmlReport = @"
border-top:1px solid #eceeef
}
+ .NamesTh {
+ cursor: pointer;
+ }
+
.subtable{
all: unset;
margin: 0;
@@ -2836,6 +3771,7 @@ $NewHtmlReport = @"
line-height:1.15;
-webkit-text-size-adjust:100%;
-ms-text-size-adjust:100%;
+ z-index: 1;
}
.sidenav a {
@@ -3095,6 +4031,92 @@ $NewHtmlReport = @"
--border-bottom-left-radius: 10px;
}
+.tooltip {
+ position: relative;
+ display: inline-block;
+ cursor: pointer;
+}
+
+.tooltip .tooltiptext {
+ visibility: hidden;
+ width: 130px;
+ font-size: 12;
+ font-weight: normal;
+ background-color: #555;
+ color: #fff;
+ text-align: center;
+ vertical-align: middle;
+ border-radius: 5px;
+ padding: 6px 0;
+ margin: 10px;
+ left: -50px;
+ position: absolute;
+ z-index: 1;
+ bottom: 125%; /* Position the tooltip above the text */
+ opacity: 0;
+ transition: opacity 0.3s
+}
+
+.tooltip:hover .tooltiptext {
+ visibility: visible;
+ opacity: 1;
+}
+
+input[type="checkbox"] {
+ width: 20px;
+ height: 20px;
+ font-size: 16px;
+ border-radius: 3px;
+ text-align: center;
+ vertical-align: middle;
+ -webkit-appearance: none;
+ border: 1px solid #BDBDBD;
+ background-color: white;
+}
+
+input[type="checkbox"]:checked {
+ background-color: #07142A; /* Change this to your desired color */
+ --border-color: #07142A;
+ border: 1px solid #07142A;
+}
+
+input[type="checkbox"]:checked::before {
+ content: '✔';
+ color: orange;
+ display: block;
+ text-align: center;
+ line-height: 20px;
+ font-size: 16px;
+}
+
+.searchbar {
+ box-shadow: 0 2px 4px 0 #DEDFE1;
+ margin-left:10px;
+ background-color: #ccc;
+ border-radius: 2px;
+ width:95%;
+ height: 40px;
+ outline:1px solid #BDBDBD;
+}
+
+.circle {
+ width: 14px;
+ height: 14px;
+ background-color: #f2f4f7;
+ color: #07142A;
+ border: 1.5px solid #07142A; /* 1px border */
+ border-radius: 60%; /* Makes the div a circle */
+ --display: flex;
+ display: inline-block;
+ align-items: center;
+ justify-content: center;
+ font-size: 10px; /* Adjust font size to fit the circle */
+ text-align: center;
+ vertical-align: middle;
+ opacity:.25;
+ font-weight: bold;
+ z-index: 2;
+}
@@ -3568,7 +4590,7 @@ Below is a summary of the domain computers that were targeted, connectivity to t
100.00%
$ComputerCount
- CSV | HTML
+ CSV | HTML
PING RESPONSE
@@ -4005,22 +5027,72 @@ Below is a summary of the exposure associated with each of those groups.
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.
-
-
-