Suds.psm1

function ConvertFrom-Base64 {
  <#
      .SYNOPSIS
      Decodes Base64 strings.

      .DESCRIPTION
      Decodes Base64 string objects into UTF-16 Little Endian objects.

      .INPUTS
      This function accepts piped objects.

      .OUTPUTS
      System.String.

      .EXAMPLE
      PS> "dABlAHMAdAAtAGMAbwBuAG4AZQBjAHQAaQBvAG4AIAA4AC4AOAAuADgALgA4AA==" | ConvertFrom-Base64
      test-connection 8.8.8.8

      .LINK
      https://github.com/cyberphor/Soap
  #>

  Param([Parameter(Mandatory, ValueFromPipeline)]$String)
  [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($String))
}

function ConvertTo-Base64 {
  <#
      .SYNOPSIS
      Encodes objects into Base64 strings.

      .DESCRIPTION
      Encodes UTF-16 Little Endian objects into Base64 string objects.

      .INPUTS
      This function accepts piped objects.

      .OUTPUTS
      System.String.

      .EXAMPLE
      PS> echo "test-connection 8.8.8.8" | ConvertTo-Base64
      dABlAHMAdAAtAGMAbwBuAG4AZQBjAHQAaQBvAG4AIAA4AC4AOAAuADgALgA4AA==
      
      PS> powershell -e dABlAHMAdAAtAGMAbwBuAG4AZQBjAHQAaQBvAG4AIAA4AC4AOAAuADgALgA4AA==
      Source Destination IPV4Address IPV6Address Bytes Time(ms)
      ------ ----------- ----------- ----------- ----- --------
      LAPTOP-H4T... 8.8.8.8 8.8.4.4 32 18
      LAPTOP-H4T... 8.8.8.8 8.8.4.4 32 22
      LAPTOP-H4T... 8.8.8.8 8.8.4.4 32 22
      LAPTOP-H4T... 8.8.8.8 8.8.4.4 32 17
  #>

  Param([Parameter(Mandatory, ValueFromPipeline)]$String)
  $Bytes = [System.Text.Encoding]::Unicode.GetBytes($String)
  [Convert]::ToBase64String($Bytes)
}

function ConvertTo-BinaryString {
  <#
      .SYNOPSIS
      Converts the provided IP address into binary.

      .DESCRIPTION
      Outputs a string of binary digits if the provided input is a valid IP address.

      .INPUTS
      This function accepts piped objects.

      .OUTPUTS
      System.String.

      .EXAMPLE
      PS> "8.8.8.8" | ConvertTo-BinaryString
      1000000010000000100000001000

      .LINK
      https://github.com/cyberphor/Soap
  #>

  Param([parameter(Mandatory,ValueFromPipeline)][IPAddress]$IpAddress)
  $Integer = $IpAddress.Address
  $ReverseIpAddress = [IPAddress][String]$Integer
  $BinaryString = [Convert]::toString($ReverseIpAddress.Address,2)
  return $BinaryString
}

function ConvertTo-IpAddress {
  <#
      .SYNOPSIS
      Converts the provided string into an IP address.

      .DESCRIPTION
      Outputs an IP address if the provided input is a string of binary digits.

      .INPUTS
      This function accepts piped objects.

      .OUTPUTS
      System.String.

      .EXAMPLE
      PS> ConvertTo-IpAddress -BinaryString 1000000010000000100000001000
      8.8.8.8

      .LINK
      https://github.com/cyberphor/Soap
  #>

  Param([parameter(Mandatory,ValueFromPipeline)][string]$BinaryString)
  $Integer = [System.Convert]::ToInt64($BinaryString,2).ToString()
  $IpAddress = ([System.Net.IPAddress]$Integer).IpAddressToString
  return $IpAddress
}

function Export-Gpo {
  Write-Output "`n[+] Available GPOs:"
  $AllGPOs = (Get-GPO -All).DisplayName
  $AllGPOs | ForEach-Object { Write-Output " - $_"}
  $GPOName = Read-Host -Prompt "`n[+] Which GPO do you want exported? `n - GPO Name"

  if ($AllGPOs -contains $GPOName) {
      $GPOBackupFolder = "$PWD\" + ($GPOName).Replace(" ", "-") + "GPO"
      $FindingGPOBackupFolder = Test-Path -Path $GPOBackupFolder
      if (-not $FindingGPOBackupFolder) {
          New-Item -Type Directory -Path $GPOBackupFolder | Out-Null
      }
      $GPOGuid = '{' + (Backup-GPO -Name $GPOName -Path $GPOBackupFolder).Id + '}'

      $OldDC = $env:COMPUTERNAME
      $OldNetBIOSName = $env:USERDOMAIN
      $OldDNSDomainName = $env:USERDNSDOMAIN

      $NewDC = 'DC1'
      $NewNetBIOSName = 'CONTOSO'
      $NewDNSDomainName = 'contoso.com'

      $Files = @(
          "$GPOBackupFolder\$GPOGuid\Backup.xml"
          "$GPOBackupFolder\$GPOGuid\bkupInfo.xml" 
          "$GPOBackupFolder\$GPOGuid\gpreport.xml"
      ) 

      $Files |
      ForEach-Object {
          Write-Output "`n[+] Scrubbing $_"
          (Get-Content -Path $_) `
          -replace $OldDC, $NewDC `
          -replace $OldDNSDomainName, $NewDNSDomainName `
          -replace $OldNetBIOSName, $NewNetBIOSName |
          Set-Content $_
          Write-Output " - Done."
      }
  }
}

function Get-DscResourcesRequired {
  Param([string[]]$Resources = @("AuditPolicyDsc","xBitLocker","NetworkingDsc"))
  $DownloadStartTime = Get-Date
  $OutputFile = "DscResources.zip"
  Install-Module -Name $Resources -Scope CurrentUser -Force
  if (Test-Path $OutputFile) { Remove-Item $OutputFile -Force }
  $env:PSModulePath -split ';' | 
  Where-Object { $_ -like "*$env:USERNAME*" } |
  Get-ChildItem | 
  Where-Object { $_.LastWriteTime -gt $DownloadStartTime } |
  Select-Object -ExpandProperty FullName |
  Compress-Archive -DestinationPath "DscResources.zip"
}

function Get-EventViewer {
  # create a COM object for Excel
  $Excel = New-Object -ComObject Excel.Application

  # create a workbook and then add two worksheets to it
  $Workbook = $Excel.Workbooks.Add()
  $Tab2 = $Workbook.Worksheets.Add()
  $Tab3 = $Workbook.Worksheets.Add()

  function Get-SuccessfulLogonEvents {
      # rename the first worksheet
      $Workbook.Worksheets.Item(1).Name = "SuccessfulLogon"

      # define column headers using the first row
      $Workbook.Worksheets.Item("SuccessfulLogon").Cells.Item(1,1) = "TimeCreated"
      $Workbook.Worksheets.Item("SuccessfulLogon").Cells.Item(1,2) = "RecordId"
      $Workbook.Worksheets.Item("SuccessfulLogon").Cells.Item(1,3) = "UserName"
      $Workbook.Worksheets.Item("SuccessfulLogon").Cells.Item(1,4) = "LogonType"
  
      # define where to begin adding data (by row and column)
      $rTimeCreated, $cTimeCreated = 2,1
      $rRecordId, $cRecordId = 2,2
      $rUserName, $cUserName = 2,3
      $rLogonType, $cLogonType = 2,4

      # define what Windows Event criteria must match
      $FilterHashTable = @{
          LogName = "Security"
          Id = 4624
          StartTime = (Get-Date).AddDays(-1)
      }

      # cycle through the Windows Events that match the criteria above
      Get-WinEvent -FilterHashtable $FilterHashTable |
      Read-WinEvent |
      Select-Object -Property TimeCreated,EventRecordId,TargetUserName,LogonType |
      Where-Object { 
          $_.TargetUserName -ne "SYSTEM" 
      } |
      ForEach-Object {
          [System.GC]::Collect()
          # fill-in the current row
          $Workbook.Worksheets.Item("SuccessfulLogon").Cells.Item($rTimeCreated, $cTimeCreated) = $_.TimeCreated
          $Workbook.Worksheets.Item("SuccessfulLogon").Cells.Item($rRecordId, $cRecordId) = $_.EventRecordId
          $Workbook.Worksheets.Item("SuccessfulLogon").Cells.Item($rUserName, $cUserName) = $_.TargetUserName
          $Workbook.Worksheets.Item("SuccessfulLogon").Cells.Item($rLogonType, $cLogonType) = $_.LogonType

          # move-on to the next row
          $rTimeCreated++
          $rRecordId++
          $rUserName++
          $rLogonType++
      }
  }

  function Get-ProcessCreationEvents {
      # rename the second worksheet
      $Workbook.Worksheets.Item(2).Name = "ProcessCreation"

      # define column headers using the first row
      $Workbook.Worksheets.Item("ProcessCreation").Cells.Item(1,1) = "TimeCreated"
      $Workbook.Worksheets.Item("ProcessCreation").Cells.Item(1,2) = "RecordId"
      $Workbook.Worksheets.Item("ProcessCreation").Cells.Item(1,3) = "UserName"
      $Workbook.Worksheets.Item("ProcessCreation").Cells.Item(1,4) = "ParentProcessName"
      $Workbook.Worksheets.Item("ProcessCreation").Cells.Item(1,5) = "NewProcessName"
      $Workbook.Worksheets.Item("ProcessCreation").Cells.Item(1,6) = "CommandLine"
  
      # define where to begin adding data (by row and column)
      $rTimeCreated, $cTimeCreated = 2,1
      $rRecordId, $cRecordId = 2,2
      $rUserName, $cUserName = 2,3
      $rParentProcessName, $cParentProcessName = 2,4
      $rNewProcessName, $cNewProcessName = 2,5
      $rCommandLine, $cCommandLine = 2,6

      # define what Windows Event criteria must match
      $FilterHashTable = @{
          LogName = "Security"
          Id = 4688
          StartTime = (Get-Date).AddDays(-1)

      }
      # cycle through the Windows Events that match the criteria above
      Get-WinEvent -FilterHashtable $FilterHashTable |
      Read-WinEvent |
      Select-Object -Property TimeCreated,EventRecordId,TargetUserName,ParentProcessName,NewProcessName,CommandLine |
      Where-Object { 
          ($_.TargetUserName -ne "-") -and `
          ($_.TargetUserName -notlike "*$") -and `
          ($_.TargetUserName -ne "LOCAL SERVICE")
      } |
      ForEach-Object {
          [System.GC]::Collect()
          # fill-in the current row
          $Workbook.Worksheets.Item("ProcessCreation").Cells.Item($rTimeCreated, $cTimeCreated) = $_.TimeCreated
          $Workbook.Worksheets.Item("ProcessCreation").Cells.Item($rRecordId, $cRecordId) = $_.EventRecordId
          $Workbook.Worksheets.Item("ProcessCreation").Cells.Item($rUserName, $cUserName) = $_.TargetUserName
          $Workbook.Worksheets.Item("ProcessCreation").Cells.Item($rParentProcessName, $cParentProcessName) = $_.ParentProcessName
          $Workbook.Worksheets.Item("ProcessCreation").Cells.Item($rNewProcessName, $cNewProcessName) = $_.NewProcessName
          $Workbook.Worksheets.Item("ProcessCreation").Cells.Item($rCommandLine, $cCommandLine) = $_.CommandLine

          # move-on to the next row
          $rTimeCreated++
          $rRecordId++
          $rUserName++
          $rParentProcessName++
          $rNewProcessName++
          $rCommandLine++
      }
  }

  function Get-PowerShellEvents {
      # rename the third worksheet
      $Workbook.Worksheets.Item(3).Name = "PowerShell"

      # define column headers using the first row
      $Workbook.Worksheets.Item("PowerShell").Cells.Item(1,1) = "TimeCreated"
      $Workbook.Worksheets.Item("PowerShell").Cells.Item(1,2) = "RecordId"
      $Workbook.Worksheets.Item("PowerShell").Cells.Item(1,3) = "Sid"
      $Workbook.Worksheets.Item("PowerShell").Cells.Item(1,4) = "ScriptBlockText"
  
      # define where to begin adding data (by row and column)
      $rTimeCreated, $cTimeCreated = 2,1
      $rRecordId, $cRecordId = 2,2
      $rSid, $cSid = 2,3
      $rScriptBlockText, $cScriptBlockText = 2,4

      # define what Windows Event criteria must match
      $FilterHashTable = @{
          LogName = "Microsoft-Windows-PowerShell/Operational"
          Id = 4104
          StartTime = (Get-Date).AddDays(-1)
      }

      # cycle through the Windows Events that match the criteria above
      Get-WinEvent -FilterHashtable $FilterHashTable |
      Read-WinEvent |
      Select-Object -Property TimeCreated,EventRecordId,@{N="Sid";E={$_.Security.UserId}},ScriptBlockText |
      Where-Object {
          ($_.Sid -ne "S-1-5-18") -and
          ($_.ScriptBlockText -ne "prompt")
      } |
      ForEach-Object {
          [System.GC]::Collect()
          # fill-in the current row
          $Workbook.Worksheets.Item("PowerShell").Cells.Item($rTimeCreated, $cTimeCreated) = $_.TimeCreated
          $Workbook.Worksheets.Item("PowerShell").Cells.Item($rRecordId, $cRecordId) = $_.EventRecordId
          $Workbook.Worksheets.Item("PowerShell").Cells.Item($rSid, $cSid) = $_.Sid
          $Workbook.Worksheets.Item("PowerShell").Cells.Item($rScriptBlockText, $cScriptBlockText) = $_.ScriptBlockText

          # move-on to the next row
          $rTimeCreated++
          $rRecordId++
          $rSid++
          $rScriptBlockText++
      }
  }

  $Path = $env:USERPROFILE + "\Desktop\Events-" + $(Get-Date -Format yyyy-MM-dd_hhmm) +".xlsx"
  $Workbook.SaveAs($Path,51)

  Get-SuccessfulLogonEvents
  $Workbook.Worksheets.Item("SuccessfulLogon").UsedRange.Columns.Autofit() | Out-Null

  Get-ProcessCreationEvents
  $Workbook.Worksheets.Item("ProcessCreation").UsedRange.Columns.Autofit() | Out-Null
  $Workbook.Save()

  Get-PowerShellEvents
  $Workbook.Worksheets.Item("PowerShell").UsedRange.Columns.Autofit() | Out-Null
  $Workbook.Save()

  $Excel.Quit()
  Invoke-Item -Path $Path
}

function Get-IpAddressRange {
  <#
      .SYNOPSIS
      Given a network ID in CIDR notation, returns an array of IPv4 address strings.

      .DESCRIPTION
      Given a network ID in CIDR notation, returns an array of IPv4 address strings.

      .PARAMETER Network
      Specifies the network ID in CIDR notation.

      .INPUTS
      None. You cannot pipe objects to Get-IpAddressRange.

      .OUTPUTS
      System.Array. Get-IpAddressRange returns an array of IPv4 address strings.

      .EXAMPLE
      Get-IpAddressRange -Network 192.168.2.0/30
      192.168.2.1
      192.168.2.2

      .EXAMPLE
      Get-IpAddressRange -Network 192.168.2.0/30, 192.168.3.0/30
      192.168.2.1
      192.168.2.2
      192.168.3.1
      192.168.3.2

      .EXAMPLE
      Get-IpAddressRange -Network 192.168.2.1/32
      192.168.2.1

      .LINK
      https://github.com/cyberphor/soap

      .NOTES
      https://community.spiceworks.com/topic/649706-question-on-splitting-a-string-in-powershell
      https://devblogs.microsoft.com/scripting/use-powershell-to-easily-convert-decimal-to-binary-and-back/
      https://stackoverflow.com/questions/28460208/what-is-the-idiomatic-way-to-slice-an-array-relative-to-both-of-its-ends
      https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/converting-binary-data-to-ip-address-and-vice-versa
  #>

  Param([Parameter(Mandatory)][string[]]$Network)
  $IpAddressRange = @()
  $Network |
  foreach {
      if ($_.Contains('/')) {
          $NetworkId = $_.Split('/')[0]
          $SubnetMask = $_.Split('/')[1]
          if ([ipaddress]$NetworkId -and ($SubnetMask -eq 32)) {
              $IpAddressRange += $NetworkId          
          } elseif ([ipaddress]$NetworkId -and ($SubnetMask -le 32)) {
              $Wildcard = 32 - $SubnetMask
              $NetworkIdBinary = ConvertTo-BinaryString $NetworkId
              $NetworkIdIpAddressBinary = $NetworkIdBinary.SubString(0,$SubnetMask) + ('0' * $Wildcard)
              $BroadcastIpAddressBinary = $NetworkIdBinary.SubString(0,$SubnetMask) + ('1' * $Wildcard)
              $NetworkIdIpAddress = ConvertTo-IpAddress $NetworkIdIpAddressBinary
              $BroadcastIpAddress = ConvertTo-IpAddress $BroadcastIpAddressBinary
              $NetworkIdInt32 = [convert]::ToInt32($NetworkIdIpAddressBinary,2)
              $BroadcastIdInt32 = [convert]::ToInt32($BroadcastIpAddressBinary,2)
              $NetworkIdInt32..$BroadcastIdInt32 | 
              foreach {
                  $BinaryString = [convert]::ToString($_,2)
                  $Address = ConvertTo-IpAddress $BinaryString
                  $IpAddressRange += $Address
              }            
          }
      }
  }
  return $IpAddressRange
}

function Get-ProcessCreationReport {
  <#
      .SYNOPSIS
      Searches the Windows "Security" Event log for commands defined in a blacklist and sends an email when a match is found.
      
      .DESCRIPTION
      This script will automatically create a file called "SentItems.log" to keep track of what logs have already been emailed (using the Record Id field/value).
      
      .INPUTS
      None. You cannot pipe objects to this script.
      
      .OUTPUTS
      An email.
      
      .EXAMPLE
      Get-ProcessCreationReport.ps1 -BlacklistFile ".\command-blacklist.txt" -EmailServer "smtp.gmail.com" -EmailServerPort 587 -EmailAddressSource "DrSpockTheChandelier@gmail.com" -EmailPassword "iHaveABadFeelingAboutThis2022!" -EmailAddressDestination "DrSpockTheChandelier@gmail.com"
  
      .NOTES
      If you are going to use Gmail, this is what you need to use (as of 17 MAR 22):
      - EmailServer = smtp.gmail.com
      - EmailServerPort = 587
      - EmailAddressSource = YourEmailAddress@gmail.com
      - EmailAddressDestination = AnyEmailAddress@AnyDomain.com
      - EmailPassword = iHaveABadFeelingAboutThis2022!
  
      Also, consider reading this:
      - https://myaccount.google.com/lesssecureapps
  #>

  Param(
      [Parameter(Mandatory)][string]$BlacklistFile,
      [Parameter(Mandatory)][string]$EmailServer,
      [Parameter(Mandatory)][int]$EmailServerPort,
      [Parameter(Mandatory)][string]$EmailAddressSource,
      [Parameter(Mandatory)][string]$EmailPassword,
      [Parameter(Mandatory)][string]$EmailAddressDestination,
      [string]$SentItemsLog = ".\SentItems.log"           
  )
  $UserId = [Security.Principal.WindowsIdentity]::GetCurrent()
  $AdminId = [Security.Principal.WindowsBuiltInRole]::Administrator
  $CurrentUser = New-Object Security.Principal.WindowsPrincipal($UserId)
  $RunningAsAdmin = $CurrentUser.IsInRole($AdminId)
  if (-not $RunningAsAdmin) { 
      Write-Error "This script requires administrator privileges."
      break
  }
  # get the command blacklist
  # - commands in your blacklist must include the full-path
  # - ex: C:\Windows\System32\whoami.exe
  $Blacklist = Get-Content -Path $BlacklistFile
  if (Test-Path $SentItemsLog) {
      # check if the script log exists
      # - save its contents to a variable
      $SentItems = Get-Content -Path $SentItemsLog
  } else {
      # otherwise, create a script log
      # - this is important so you are not sending the same record multiple times
      New-Item -ItemType File -Path $SentItemsLog | Out-Null
  }
  # define the search criteria
  $FilterHashTable = @{
      LogName = "Security"
      Id = 4688
      StartTime = $(Get-Date).AddDays(-1)    
  }
  # cycle through events matching the criteria above
  # - return the first event that contains a command on the blacklist
  $Event = Get-WinEvent -FilterHashtable $FilterHashTable |
      Where-Object { 
          ($Blacklist -contains $_.Properties[5].Value) -and 
          ($SentItems -notcontains $_.RecordId)    
      } | 
      Select-Object * -First 1
  # if there is an event meeting the criteria defined, send an email
  if ($Event) {
      # assign important fields to separate variables for readability
      $EventId = $Event.Id
      $Source = $Event.ProviderName
      $MachineName = $Event.MachineName
      $Message = $Event.Message
      # define values required to send an email via PowerShell
      $EmailClient = New-Object Net.Mail.SmtpClient($EmailServer, $EmailServerPort)
      $Subject = "Alert from $MachineName"
      $Body = "
          EventID: $EventId `r
          Source: $Source `r `
          MachineName: $MachineName `r
          Message: $Message `r
      "

      $EmailClient.EnableSsl = $true
      $EmailClient.Credentials = New-Object System.Net.NetworkCredential($EmailAddressSource, $EmailPassword)
      $EmailClient.Send($EmailAddressSource, $EmailAddressDestination, $Subject, $Body)
      Add-Content -Value $Event.RecordId -Path $SentItemsLog
  }
}

function Get-SerialNumberAndCurrentUser {
  Param([string[]]$ComputerName)
  Invoke-Command -ComputerName $ComputerName -ScriptBlock {
      Get-WmiObject -Class Win32_Bios | Select-Object -ExpandProperty SerialNumber
      Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty UserName
  }
}

function Get-Stig {
  <#
      .SYNOPSIS
      Returns STIG rules as PowerShell objects.
              
      .DESCRIPTION
      Returns Security Technical Implementation Guide (STIG) rules as PowerShell objects after reading an Extensible Configuration Checklist Description Format (XCCDF) document.

      .INPUTS
      None. You cannot pipe objects to Get-Stig.

      .OUTPUTS
      PSCustomObject.

      .EXAMPLE
      Get-Stig -Path 'U_MS_Windows_10_STIG_V2R3_Manual-xccdf.xml'

      .LINK
      https://gist.github.com/entelechyIT
  #>

  Param([Parameter(Mandatory)]$Path)
  if (Test-Path $Path) {
      [xml]$XCCDFdocument = Get-Content -Path $Path
      if ($XCCDFdocument.Benchmark.xmlns -like 'http://checklists.nist.gov/xccdf/*') {
          $Stig = @()
          $XCCDFdocument.Benchmark.Group.Rule |
          ForEach-Object {
              $Rule = New-Object -TypeName PSObject -Property ([ordered]@{
                  RuleID    = $PSItem. id
                  RuleTitle = $PSItem.title 
                  Severity = $PSItem.severity
                  VulnerabilityDetails = $($($($PSItem.description) -split '</VulnDiscussion>')[0] -replace '<VulnDiscussion>', '')
                  Check = $PSItem.check.'check-content'
                  Fix = $PSItem.fixtext.'#text'
                  ControlIdentifier = $PSItem.ident.'#text'
                  Control = $null 
              })
              $Stig += $Rule
          }
          return $Stig
      } 
      Write-Error 'The file provided is not a XCCDF document.'
  }
}

function Import-AdUsersFromCsv {
  $Password = ConvertTo-SecureString -String '1qaz2wsx!QAZ@WSX' -AsPlainText -Force
  Import-Csv -Path .\users.csv |
  ForEach-Object {
      $Name = $_.LastName + ', ' + $_.FirstName
      $SamAccountName = ($_.FirstName + '.' + $_.LastName).ToLower()
      $UserPrincipalName = $SamAccountName + '@' + (Get-AdDomain).Forest
      $Description = $_.Description
      $ExpirationDate = Get-Date -Date 'October 31 2022'
      New-AdUser `
          -Name $Name `
          -DisplayName $Name `
          -GivenName $_.FirstName `
          -Surname $_.LastName `
          -SamAccountName $SamAccountName `
          -UserPrincipalName $UserPrincipalName `
          -Description $Description `
          -ChangePasswordAtLogon $true `
          -AccountExpirationDate $ExpirationDate `
          -Enabled $true `
          -Path "OU=Users,$(Get-ADDomain).DistinguishedName" `
          -AccountPassword $Password
  }
}

function Install-RSAT {
  Get-WindowsCapability -Name RSAT* -Online | 
  Add-WindowsCapability -Online
}

function Install-Sysmon {
  Invoke-WebRequest -Uri "https://download.sysinternals.com/files/Sysmon.zip" -OutFile "Sysmon.zip"
  Expand-Archive -Path "Sysmon.zip" -DestinationPath "C:\Program Files\Sysmon" 
  Remove-Item -Path "Sysmon.zip" -Recurse
  Invoke-WebRequest -Uri "https://raw.githubusercontent.com/SwiftOnSecurity/sysmon-config/master/sysmonconfig-export.xml" -OutFile "C:\Program Files\Sysmon\config.xml"
  Invoke-Expression "C:\'Program Files'\Sysmon\Sysmon64.exe -accepteula -i C:\'Program Files'\Sysmon\config.xml"
}

function Invoke-SecurityBaseline {
  # V-220726: Data Execution Prevention (DEP) must be configured to at least OptOut.
  bcdedit /set "{current}" nx OptOut

  # V-220748
  auditpol /set /subcategory:"Credential Validation" /failure:enable

  # V-220749
  auditpol /set /subcategory:"Credential Validation" /success:enable

  # V-220750
  auditpol /set /subcategory:"Security Group Management" /success:enable

  # V-220751
  auditpol /set /subcategory:"User Account Management" /failure:enable

  # V-220752
  auditpol /set /subcategory:"User Account Management" /success:enable

  # V-220753
  auditpol /set /subcategory:"Plug and Play Events" /success:enable

  # V-220754
  auditpol /set /subcategory:"Process Creation" /success:enable

  # V-220755
  auditpol /set /subcategory:"Account Lockout" /failure:enable

  # V-220756
  auditpol /set /subcategory:"Group Membership" /success:enable

  # V-220757
  auditpol /set /subcategory:"Logoff" /success:enable

  # V-220758
  auditpol /set /subcategory:"Logon" /failure:enable

  # V-220759
  auditpol /set /subcategory:"Logon" /success:enable

  # V-220760
  auditpol /set /subcategory:"Special Logon" /success:enable

  # V-220761
  auditpol /set /subcategory:"File Share" /failure:enable

  # V-220762
  auditpol /set /subcategory:"File Share" /success:enable

  # V-220763
  auditpol /set /subcategory:"Other Object Access Events" /success:enable

  # V-220764
  auditpol /set /subcategory:"Other Object Access Events" /failure:enable

  # V-220765
  auditpol /set /subcategory:"Removable Storage" /failure:enable

  # V-220766
  auditpol /set /subcategory:"Removable Storage" /success:enable

  # V-220767
  auditpol /set /subcategory:"Audit Policy Change" /success:enable

  # V-220768
  auditpol /set /subcategory:"Authentication Policy Change" /success:enable

  # V-220769
  auditpol /set /subcategory:"Authorization Policy Change" /success:enable

  # V-220770
  auditpol /set /subcategory:"Sensitive Privilege Use" /failure:enable

  # V-220771
  auditpol /set /subcategory:"Sensitive Privilege Use" /success:enable

  # V-220772
  auditpol /set /subcategory:"IPSec Driver" /failure:enable

  # V-220773
  auditpol /set /subcategory:"Other System Events" /success:enable

  # V-220774
  auditpol /set /subcategory:"Other System Events" /failure:enable

  # V-220775
  auditpol /set /subcategory:"Security State Change" /success:enable

  # V-220776
  auditpol /set /subcategory:"Security System Extension" /success:enable

  # V-220777
  auditpol /set /subcategory:"System Integrity" /failure:enable

  # V-220778
  auditpol /set /subcategory:"System Integrity" /success:enable

  # V-220779: the Application event log size must be configured to 32768 KB or greater
  wevtutil sl "Application" /ms:32768000

  # V-220780: the Security event log size must be configured to 1024000 KB or greater
  wevtutil sl "Security" /ms:1024000000

  # V-220781: the System event log size must be configured to 32768 KB or greater
  wevtutil sl "System" /ms:32768000

  # V-220785
  auditpol /set /subcategory:"Other Policy Change Events" /success:enable

  # V-220786
  auditpol /set /subcategory:"Other Policy Change Events" /failure:enable

  # V-220787
  auditpol /set /subcategory:"Other Logon/Logoff Events" /success:enable

  # V-220787
  auditpol /set /subcategory:"Other Logon/Logoff Events" /failure:enable

  # V-220789
  auditpol /set /subcategory:"Detailed File Share" /success:enable

  # V-220790
  auditpol /set /subcategory:"MPSSVC Rule-Level Policy Change" /success:enable

  # V-220791
  auditpol /set /subcategory:"MPSSVC Rule-Level Policy Change" /failure:enable

  # V-220809: Command line data must be included in process creation events.
  $Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit"
  $Name = "ProcessCreationIncludeCmdLine_Enabled"
  $PropertyType = "DWORD"
  $Value = 1
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220823: Solicited Remote Assistance must not be allowed.
  $Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\"
  $Name = "fAllowToGetHelp"
  $PropertyType = "DWORD"
  $Value = 0 
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220827: Autoplay must be turned off for non-volume devices.
  $Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer\"
  $Name = "NoAutoplayfornonVolume"
  $PropertyType = "DWORD"
  $Value = 1
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220828: The default autorun behavior must be configured to prevent autorun commands.
  $Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\"
  $Name = "NoAutorun"
  $PropertyType = "DWORD"
  $Value = 1
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220829: Autoplay must be disabled for all drives.
  $Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\"
  $Name = "NoDriveTypeAutoRun"
  $PropertyType = "DWORD"
  $Value = 255
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220857: The Windows Installer Always install with elevated privileges must be disabled.
  $Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Installer\"
  $Name = "AlwaysInstallElevated"
  $PropertyType = "DWORD"
  $Value = 0
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220860: PowerShell script block logging must be enabled on Windows 10.
  $Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging\"
  $Name = "EnableScriptBlockLogging"
  $PropertyType = "DWORD"
  $Value = 1
  New-Item -Path $Path -Force
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220862: The Windows Remote Management (WinRM) client must not use Basic authentication.
  $Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WinRM\Client\"
  $Name = "AllowBasic"
  $PropertyType = "DWORD"
  $Value = 0
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220865: The Windows Remote Management (WinRM) service must not use Basic authentication.
  $Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service\"
  $Name = "AllowBasic"
  $PropertyType = "DWORD"
  $Value = 0
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220913: Audit policy using subcategories must be enabled
  $Path = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa"
  $Name = "SCENoApplyLegacyAuditPolicy"
  $PropertyType = "DWORD"
  $Value = 1
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220930: Anonymous enumeration of shares must be restricted.
  $Path = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\"
  $Name = "RestrictAnonymous"
  $PropertyType = "DWORD"
  $Value = 1
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220938: The LanMan authentication level must be set to send NTLMv2 response only, and to refuse LM and NTLM.
  $Path = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\"
  $Name = "LmCompatibilityLevel"
  $PropertyType = "DWORD"
  $Value = 5
  New-Item -Path $Path
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # V-220978: the Manage auditing and security log user right must only be assigned to the Administrators group.
  $SecurityTemplate = @"
      [Unicode]
      Unicode=yes
      [Registry Values]
      [Privilege Rights]
      SeSecurityPrivilege = *S-1-5-32-544
      [Version]
      signature=`"`$CHICAGO`$`"
      Revision=1
"@

  $FileName = "V-220978.inf"
  if (Test-Path $FileName) {
      Remove-Item $FileName
      New-Item -ItemType File -Name $FileName | Out-Null
  }
  Add-Content -Value $SecurityTemplate -Path $FileName 
  secedit /configure /db secedit.sdb /cfg $FileName
  Remove-Item "secedit.sdb"
  Remove-Item $FileName

  # V-250318: PowerShell Transcription must be enabled on Windows 10.
  $Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription"
  $Name = "EnableTranscripting"
  $PropertyType = "DWORD"
  $Value = 1
  New-Item -Path $Path -Force
  New-ItemProperty -Path $Path -Name $Name -PropertyType $PropertyType -Value $Value -Force

  # Reboot
  shutdown /r /t 15 /c "Rebooting in 15 seconds."
}

function New-AdDomainAdmin {
  Param(
      [Parameter(Mandatory)][string]$FirstName,
      [Parameter(Mandatory)][string]$LastName,
      [securestring]$Password = $(ConvertTo-SecureString -String '1qaz2wsx!QAZ@WSX' -AsPlainText -Force)
  )
  $Name = "$LastName, $FirstName (DA)"
  $SamAccountName = ("$FirstName.$LastName.da").ToLower()
  $AccountExpirationDate = (Get-Date).AddYears(1)
  New-ADUser `
      -GivenName $FirstName `
      -Surname $LastName `
      -Name $Name `
      -DisplayName $Name `
      -SamAccountName $SamAccountName `
      -AccountPassword $Password `
      -AccountExpirationDate $AccountExpirationDate `
      -ChangePasswordAtLogon $true `
      -Enabled $true
  Add-ADGroupMember -Identity "Domain Admins" -Members $SamAccountName
}

function New-AdForest {
  Param(
      [Parameter(Mandatory)][string]$DomainName,
      [securestring]$SafeModeAdministratorPassword = $(ConvertTo-SecureString -AsPlainText -Force "1qaz2wsx!QAZ@WSX")
  )
  Install-WindowsFeature DNS, AD-Domain-Services -IncludeManagementTools
  $Parameters = @{
      InstallDns                    = $True
      DomainName                    = $DomainName
      SafeModeAdministratorPassword = $SafeModeAdministratorPassword
      NoRebootOnCompletion          = $True
      Force                         = $True
  }
  Install-ADDSForest @Parameters
}


function New-GpoWallpaper {
  Param(
      [Parameter(Mandatory)]$InputFile,
      [Parameter(Mandatory)]$Server
  )
  # create a SMB share on the server
  $Session = New-PSSession -ComputerName $Server
  Invoke-Command -Session $Session -ScriptBlock {
      New-Item -ItemType Directory -Path "C:\Wallpaper"
      New-SmbShare -Name "Wallpaper" -Path "C:\Wallpaper" -FullAccess "Administrators" -ReadAccess "Everyone"
  }
  # copy the wallpaper to the SMB share
  Copy-Item -ToSession $Session -Path $InputFile -Destination "C:\Wallpaper\Wallpaper.jpg"
  # create the GPO
  $WallpaperPath = "\\$Server\Wallpaper\Wallpaper.jpg"
  $Key = "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System"
  New-GPO -Name "Wallpaper" -Comment "Sets the wallpaper." -ErrorAction Stop
  Set-GPRegistryValue -Name "Wallpaper" -Key $Key -ValueName "Wallpaper" -Value $WallpaperPath -Type "String"
  Set-GPRegistryValue -Name "Wallpaper" -Key $Key -ValueName "WallpaperStyle" -Value "0" -Type "String"
  New-GPLink -Name "Wallpaper" -Target  $(Get-ADDomain -Current LocalComputer).DistinguishedName   
}

function Remove-StaleDnsRecord {
  <#
      .LINK
      https://adamtheautomator.com/powershell-dns/
  #>

  $Domain = (Get-AdDomain).Forest
  $30DaysAgo = (Get-Date).AddDays(-30)
  Get-DnsServerResourceRecord -Zone $Domain -RRType A | 
  Where-Object { $_.TimeStamp -le $30DaysAgo } | 
  Remove-DnsServerResourceRecord -ZoneName $Domain -Force
}


function Start-AdBackup {
  Param(
      [Parameter(Mandatory)][string]$ComputerName,
      [string]$Share = "Backups",
      [string]$Prefix = "AdBackup"
  )
  $BackupFeature = (Install-WindowsFeature -Name Windows-Server-Backup).InstallState
  $BackupServerIsOnline = Test-Connection -ComputerName $ComputerName -Count 2 -Quiet
  if ($BackupFeature -eq "Installed") {
      if ($BackupServerIsOnline) {
          $Date = Get-Date -Format "yyyy-MM-dd"
          $Target = "\\$ComputerName\$Share\$Prefix-$Date"
          $LogDirectory = "C:\BackupLogs"
        $LogFile = "$LogDirectory\$Prefix-$Date"
          if (Test-Path $Target) { Remove-Item -Path $Target -Recurse -Force }
          New-Item -ItemType Directory -Path $Target -Force | Out-Null
          if (Test-Path $LogDirectory) { New-Item -ItemType Directory -Path $LogDirectory -Force | Out-Null }
          $Expression = "wbadmin START BACKUP -systemState -vssFull -backupTarget:$Target -noVerify -quiet"
          Invoke-Expression $Expression | Out-File -FilePath $LogFile
      } else {
          Write-Output "[x] The computer specified is not online."
      }
  } else {
      Write-Output "[x] The Windows-Server-Backup feature is not installed. Use the command below to install it."
      Write-Output " Install-WindowsFeature -Name Windows-Server-Backup"
  }
}

function Uninstall-Sysmon {
  Invoke-Expression "C:\'Program Files'\Sysmon\Sysmon64.exe -u"
  Remove-Item -Path "C:\Program Files\Sysmon" -Recurse -Force
}