From e962d8db1e107e714be55576f18cb759bed3b75a Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 7 Oct 2024 08:10:03 -0500 Subject: [PATCH] Update PowerHuntShares.psm1 Added parser for .fetchmailrc credentials. --- PowerHuntShares.psm1 | 121 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/PowerHuntShares.psm1 b/PowerHuntShares.psm1 index f21f022..5cb0c29 100644 --- a/PowerHuntShares.psm1 +++ b/PowerHuntShares.psm1 @@ -4,7 +4,7 @@ #-------------------------------------- # Author: Scott Sutherland, 2024 NetSPI # License: 3-clause BSD -# Version: v1.151 +# Version: v1.152 # 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 { @@ -1611,13 +1611,14 @@ function Invoke-HuntSMBShares $FileNamePatternsAll.Rows.Add("vas.conf","May include auth configs.","None.","Secret","") | Out-Null $FileNamePatternsAll.Rows.Add("grub.cfg","","None.","Secret","Get-PwGrubConfig") | Out-Null $FileNamePatternsAll.Rows.Add("grub.conf","","None.","Secret","Get-PwGrubConfig") | Out-Null - $FileNamePatternsAll.Rows.Add("*.fetchmailrc","","None.","Secret","") | Out-Null + $FileNamePatternsAll.Rows.Add("*.fetchmailrc","","None.","Secret","Get-PwFetchmailrc") | Out-Null $FileNamePatternsAll.Rows.Add("*.keytab","May store authentication tokens.","None.","Secret","") | Out-Null $FileNamePatternsAll.Rows.Add("*mysql_history*","","None.","Secret","") | Out-Null $FileNamePatternsAll.Rows.Add("*psql_history*","","None.","Secret","") | Out-Null $FileNamePatternsAll.Rows.Add("*.git-credentials*","","None.","Secret","Get-PwGitCredentials") | Out-Null $FileNamePatternsAll.Rows.Add("*azure.config.ini*","","None.","Secret","") | Out-Null $FileNamePatternsAll.Rows.Add("*azure.profile.json*","","None.","Secret","") | Out-Null + $FileNamePatternsAll.Rows.Add("*.azure","","None.","Secret","") | Out-Null $FileNamePatternsAll.Rows.Add("*dbeaver-data-sources.xml","","None.","Secret","") | Out-Null $FileNamePatternsAll.Rows.Add("*.s3cfg","","None.","Secret","") | Out-Null $FileNamePatternsAll.Rows.Add("*.netrc","","None.","Secret","Get-PwNetrc") | Out-Null @@ -26970,3 +26971,119 @@ function Get-PwGitCredentials { # Return parsed credentials return $credentialsList } + +# Author: Scott Sutherland, NetSPI (@_nullbind / nullbind) +# Intended input: .fetchmailrc files + +# Author: Scott Sutherland, NetSPI (@_nullbind / nullbind) +# Intended input: .fetchmailrc files + +function Get-PwFetchmailrc { + param ( + [string]$FilePath, + [string]$ComputerName = "NA", + [string]$ShareName = "NA", + [string]$UncFilePath = "NA", + [string]$FileName = "NA", + [string]$TargetURL = "NA" + ) + + if (-not (Test-Path -Path $FilePath)) { + Write-Host "File not found: $FilePath" + return + } + + # Read and clean the lines into a modifiable list + $lines = [System.Collections.Generic.List[string]](Get-Content -Path $FilePath | ForEach-Object { + $_.Trim() + } | Where-Object { $_ -notmatch '^#' }) # Remove comments + + # Consolidate multi-line configurations + for ($i = $lines.Count - 1; $i -gt 0; $i--) { + if ($lines[$i] -notmatch '^(defaults|poll|skip)\s+') { + $lines[$i - 1] += " " + $lines[$i] + $lines.RemoveAt($i) + } + } + + # Initialize variables + $defaults = @{} + $credentials = @() + + # Function to parse individual configuration lines + function Parse-FetchmailRCLine { + param ($line) + $cred = @{ + "Username" = @() + "Password" = @() + "TargetServer" = "" + "Section" = "" + "TargetPort" = "" + } + + # Extract users, passwords, server, protocol, and port + $userMatch = [regex]::Match($line, '\s+user(?:name)?\s+"([^"]+)"') + if ($userMatch.Success) { + $cred["Username"] = $userMatch.Groups[1].Value + } + + $passMatch = [regex]::Match($line, '\s+pass(?:word)?\s+"([^"]+)"') + if ($passMatch.Success) { + $cred["Password"] = $passMatch.Groups[1].Value + } + + $cred["TargetServer"] = if ($line -match '^(?:poll|skip)\s+(\S+)') { $matches[1] } else { $cred["TargetServer"] } + $cred["Section"] = if ($line -match '\s+proto(?:col)?\s+(\S+)') { $matches[1] } else { $cred["Section"] } + $cred["TargetPort"] = if ($line -match '\s+(?:port|service)\s+(\S+)') { $matches[1] } else { $cred["TargetPort"] } + + # Return credentials if found + return $cred + } + + # Parse each line for credentials + foreach ($line in $lines) { + # If 'defaults' line, save defaults + if ($line -match '^defaults') { + $defaults = Parse-FetchmailRCLine -line $line + continue + } + + # Parse line, merge with defaults if any + $parsedCred = Parse-FetchmailRCLine -line $line + foreach ($key in $defaults.Keys) { + if (-not $parsedCred[$key] -or ($parsedCred[$key] -is [array] -and $parsedCred[$key].Count -eq 0)) { + $parsedCred[$key] = $defaults[$key] + } + } + + # Add parsed credentials if valid + if ($parsedCred["TargetServer"] -and $parsedCred["Section"] -and $parsedCred["Username"] -and $parsedCred["Password"]) { + $credentials += [pscustomobject]@{ + ComputerName = $ComputerName + ShareName = $ShareName + UncFilePath = $UncFilePath + FileName = $FileName + Section = $parsedCred["Section"] + ObjectName = "NA" + TargetURL = $TargetURL + TargetServer = $parsedCred["TargetServer"] + TargetPort = $parsedCred["TargetPort"] + Database = "NA" + Domain = "NA" + Username = $parsedCred["Username"] + Password = $parsedCred["Password"] + PasswordEnc = "NA" + KeyFilePath = "NA" + } + } + } + + # Output credentials + if ($credentials.Count -eq 0) { + Write-Host "No credentials found in $FilePath" + } else { + $credentials | Format-Table -AutoSize + } + + return $credentials +}