https://github.com/Purp1eW0lf/Blue-Team-Notes
You didn't think I'd go and leave the blue team out, right?
https://github.com/Purp1eW0lf/Blue-Team-Notes
blueteam cybersecurity dfir infosec powershell
Last synced: about 1 year ago
JSON representation
You didn't think I'd go and leave the blue team out, right?
- Host: GitHub
- URL: https://github.com/Purp1eW0lf/Blue-Team-Notes
- Owner: Purp1eW0lf
- Created: 2021-04-18T17:58:06.000Z (about 5 years ago)
- Default Branch: main
- Last Pushed: 2023-09-19T16:04:20.000Z (over 2 years ago)
- Last Synced: 2025-04-08T09:11:15.433Z (about 1 year ago)
- Topics: blueteam, cybersecurity, dfir, infosec, powershell
- Homepage:
- Size: 1.17 MB
- Stars: 1,648
- Watchers: 57
- Forks: 242
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- awesome-soc - Blue Team Notes
- awesome-soc-analyst - Blue Team Notes | Purp1eW0lf
README
# Blue Team Notes
A collection of one-liners, small scripts, and some useful tips for blue team work.
I've included screenshots where possible so you know what you're getting.
## Contact me
If you see a mistake, or have an easier way to run a command then you're welcome to hit me up on [Twitter](https://twitter.com/Purp1eW0lf) or commit an issue here.
If you want to contribute I'd be grateful for the command and a screenshot. I'll of course add you as a contributor
If you want to find me elsehwere, for reasons(?), searching 'Dray Agha' on the internets should find whatever it is you're looking for.
## Did the Notes help?
I hope the Blue Team Notes help you catch an adversary, thwart an attack, or even just helps you learn.
If you've benefited from the Blue Team Notes, would you kindly consider making a donation to one or two charities.
Donate as much or little money as you like, of course. I have some UK charities you could donate to: [Great Ormond Street - Children's hospital](https://www.gosh.org/_donate/?amount=3&frequency=single&campaign=1284), [Cancer Research](https://donate.cancerresearchuk.org/support-us/your-donation?type=single&amount=3), and [Feeding Britain - food charity](https://feedingbritain.org/donate/)
## Table of Contents
- [Shell Style](#shell-style)
- [Windows](#Windows)
* [OS Queries](#os-queries)
* [Account Queries](#account-queries)
* [Service Queries](#service-queries)
* [Network Queries](#network-queries)
* [Remoting Queries](#remoting-queries)
* [Firewall Queries](#firewall-queries)
* [SMB Queries](#smb-queries)
* [Process Queries](#process-queries)
* [Recurring Task Queries](#recurring-task-queries)
* [File Queries](#file-queries)
* [Registry Queries](#registry-queries)
* [Driver Queries](#driver-queries)
* [DLL Queries](#dll-queries)
* [AV Queries](#AV-Queries)
* [Log Queries](#log-queries)
* [Powershell Tips](#powershell-tips)
- [Linux](#linux)
* [Bash History](#bash-history)
* [Grep and Ack](#grep-and-ack)
* [Processes and Networks](#processes-and-networks)
* [Files](#files)
* [Bash Tips](#bash-tips)
- [macOS](#macOS)
* [Reading .plist files](#Reading-.plist-files)
* [Quarantine Events](#Quarantine-Events)
* [Install History](Install-History)
* [Most Recently Used (MRU)](#Most-Recently-Used-(MRU))
* [Audit Logs](#Audit-Logs)
* [Command line history](#Command-line-history)
* [WHOMST is in the Admin group](#WHOMST-is-in-the-Admin-group)
* [Persistence locations](#Persistence-locations)
* [Transparency, Consent, and Control (TCC)](#Transparency,-Consent,-and-Control-(TCC))
* [Built-In Security Mechanisms](#Built-In-Security-Mechanisms)
- [Malware](#Malware)
* [Rapid Malware Analysis](#rapid-malware-Analysis)
* [Unquarantine Malware](#Unquarantine-Malware)
* [Process Monitor](#process-monitor)
* [Hash Check Malware](#hash-check-malware)
* [Decoding Powershell](#decoding-powershell)
- [SOC](#SOC)
* [Sigma Converter](#sigma-converter)
* [SOC Prime](#soc-prime)
- [Honeypots](#honeypots)
* [Basic Honeypots](#basic-honeypots)
- [Network Traffic](#network-traffic)
* [Capture Traffic](#capture-traffic)
* [TShark](#tshark)
* [Extracting Stuff](#extracting-stuff)
* [PCAP Analysis IRL](#pcap-analysis-irl)
- [Digital Forensics](#Digital-Forensics)
* [Volatility](#volatility)
* [Quick Forensics](#quick-forensics)
* [Chainsaw](#chainsaw)
* [Browser History](#browser-history)
* [Which logs to pull in an incident](#Which-logs-to-pull-in-an-incident)
* [USBs](#USBs)
* [Reg Ripper](#reg-ripper)
* [Winget](#winget)
---
As you scroll along, it's easy to lose orientation. Wherever you are in the Blue Team Notes, if you look to the top-left of the readme you'll see a little icon. This is a small table of contents, and it will help you figure out where you are, where you've been, and where you're going

As you go through sections, you may notice the arrowhead that says 'section contents'. I have nestled the sub-headings in these, to make life a bit easier.

---
# Shell Style
section contents
+ [Give shell timestamp](#give-shell-timestamp)
- [CMD](#cmd)
- [Pwsh](#pwsh)
- [Bash](#bash)
### Give shell timestamp
For screenshots during IR, I like to have the date, time, and sometimes the timezone in my shell
#### CMD
```bat
setx prompt $D$S$T$H$H$H$S$B$S$P$_--$g
:: all the H's are to backspace the stupid microsecond timestamp
:: $_ and --$g seperate the date/time and path from the actual shell
:: We make the use of the prompt command: https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/prompt
:: setx is in fact the command line command to write variables to the registery
:: We are writing the prompt's new timestamp value in the cmd line into the reg so it stays, otherwise it would not stay in the cmdline when we closed it.
```

#### Pwsh
```powershell
###create a powershell profile, if it doesnt exist already
New-Item $Profile -ItemType file –Force
##open it in notepad to edit
function prompt{ "[$(Get-Date)]" +" | PS "+ "$(Get-Location) > "}
##risky move, need to tighten this up. Change your execution policy or it won't
#run the profile ps1
#run as powershell admin
Set-ExecutionPolicy RemoteSigned
```

#### Bash
```bash
##open .bashrc
sudo nano .bashrc
#https://www.howtogeek.com/307701/how-to-customize-and-colorize-your-bash-prompt/
##date, time, colour, and parent+child directory only, and -> promptt
PS1='\[\033[00;35m\][`date +"%d-%b-%y %T %Z"]` ${PWD#"${PWD%/*/*}/"}\n\[\033[01;36m\]-> \[\033[00;37m\]'
##begin purple #year,month,day,time,timezone #show last 2 dir #next line, cyan,->prompt #back to normal white text
#restart the bash source
source ~/.bashrc
```

---
# Windows
section contents
+ [OS Queries](#os-queries)
+ [Account Queries](#account-queries)
+ [Service Queries](#service-queries)
+ [Network Queries](#network-queries)
+ [Remoting Queries](#remoting-queries)
+ [Firewall Queries](#firewall-queries)
+ [SMB Queries](#smb-queries)
+ [Process Queries](#process-queries)
+ [Recurring Task Queries](#recurring-task-queries)
+ [File Queries](#file-queries)
+ [Registry Queries](#registry-queries)
+ [Driver Queries](#driver-queries)
+ [DLL Queries](#dll-queries)
+ [Log Queries](#log-queries)
+ [Powershell Tips](#powershell-tips)
I've generally used these Powershell queries with [Velociraptor](https://www.velocidex.com), which can query thousands of endpoints at once.
## OS Queries
section contents
+ [Get Fully Qualified Domain Name](#get-fully-qualified-domain-name)
+ [Get OS and Pwsh info](#get-os-and-pwsh-info)
- [Hardware Info](#hardware-info)
+ [Time info](#time-info)
- [Human Readable](#human-readable)
- [Machine comparable](#machine-comparable)
- [Compare UTC time from Local time](#compare-utc-time-from-local-time)
+ [Update Info](#update-info)
- [Get Patches](#get-patches)
- [Manually check if patch has taken](#manually-check-if-patch-has-taken)
* [Microsoft Support Page](#microsoft-support-page)
* [On Host](#on-host)
* [Discrepencies](#discrepencies)
## Get Fully Qualified Domain Name
```powershell
([System.Net.Dns]::GetHostByName(($env:computerName))).Hostname
# Get just domain name
(Get-WmiObject -Class win32_computersystem).domain
```

### Get OS and Pwsh info
This will print out the hostname, the OS build info, and the powershell version
```powershell
$Bit = (get-wmiobject Win32_OperatingSystem).OSArchitecture ;
$V = $host | select-object -property "Version" ;
$Build = (Get-WmiObject -class Win32_OperatingSystem).Caption ;
write-host "$env:computername is a $Bit $Build with Pwsh $V
```

#### Hardware Info
If you want, you can get Hardware, BIOS, and Disk Space info of a machine
```powershell
#Get BIOS Info
gcim -ClassName Win32_BIOS | fl Manufacturer, Name, SerialNumber, Version;
#Get processor info
gcim -ClassName Win32_Processor | fl caption, Name, SocketDesignation;
#Computer Model
gcim -ClassName Win32_ComputerSystem | fl Manufacturer, Systemfamily, Model, SystemType
#Disk space in Gigs, as who wants bytes?
gcim -ClassName Win32_LogicalDisk |
Select -Property DeviceID, DriveType, @{L='FreeSpaceGB';E={"{0:N2}" -f ($_.FreeSpace /1GB)}}, @{L="Capacity";E={"{0:N2}" -f ($_.Size/1GB)}} | fl
## Let's calculate an individual directory, C:\Sysmon, and compare with disk memory stats
$size = (gci c:\sysmon | measure Length -s).sum / 1Gb;
write-host " Sysmon Directory in Gigs: $size";
$free = gcim -ClassName Win32_LogicalDisk | select @{L='FreeSpaceGB';E={"{0:N2}" -f ($_.FreeSpace /1GB)}};
echo "$free";
$cap = gcim -ClassName Win32_LogicalDisk | select @{L="Capacity";E={"{0:N2}" -f ($_.Size/1GB)}}
echo "$cap"
```

### Time info
#### Human Readable
Get a time that's human readable
```powershell
Get-Date -UFormat "%a %Y-%b-%d %T UTC:%Z"
```

#### Machine comparable
This one is great for doing comparisons between two strings of time
```powershell
[Xml.XmlConvert]::ToString((Get-Date).ToUniversalTime(), [System.Xml.XmlDateTimeSerializationMode]::Utc)
```

#### Compare UTC time from Local time
```powershell
$Local = get-date;$UTC = (get-date).ToUniversalTime();
write-host "LocalTime is: $Local";write-host "UTC is: $UTC"
```

### Update Info
#### Get Patches
Will show all patch IDs and their installation date
```powershell
get-hotfix|
select-object HotFixID,InstalledOn|
Sort-Object -Descending -property InstalledOn|
format-table -autosize
```

Find why an update failed
```powershell
$Failures = gwmi -Class Win32_ReliabilityRecords;
$Failures | ? message -match 'failure' | Select -ExpandProperty message
```
#### Manually check if patch has taken
This happened to me during the March 2021 situation with Microsoft Exchange's ProxyLogon. The sysadmin swore blind they had patched the server, but neither `systeminfo` of `get-hotfix` was returning with the correct KB patch.
The manual workaround isn't too much ballache
##### Microsoft Support Page
First identify the ID number of the patch you want. And then find the dedicated Microsoft support page for it.
For demonstration purposes, let's take `KB5001078` and it's [corresponding support page](https://support.microsoft.com/en-us/topic/kb5001078-servicing-stack-update-for-windows-10-version-1607-february-12-2021-3e19bfd1-7711-48a8-978b-ce3620ec6362). You'll be fine just googling the patch ID number.

Then click into the dropdown relevant to your machine.

Here you can see the files that are included in a particular update. The task now is to pick a handful of the patch-files and compare your host machine. See if these files exist too, and if they do do they have similar / same dates on the host as they do in the Microsoft patch list?
##### On Host
Let us now assume you don't know the path to this file on your host machine. You will have to recursively search for the file location. It's a fair bet that the file will be in `C:\Windows\` (but not always), so lets' recursively look for `EventsInstaller.dll`
```powershell
$file = 'EventsInstaller.dll'; $directory = 'C:\windows' ;
gci -Path $directory -Filter $file -Recurse -force|
sort-object -descending -property LastWriteTimeUtc | fl *
```
We'll get a lot of information here, but we're really concerned with is the section around the various *times*. As we sort by the `LastWriteTimeUtc`, the top result should in theory be the latest file of that name...but this is not always true.

##### Discrepencies
I've noticed that sometimes there is a couple days discrepency between dates.

For example in our screenshot, on the left Microsoft's support page supposes the `EventsInstaller.dll` was written on the 13th January 2021. And yet our host on the right side of the screenshot comes up as the 14th January 2021. This is fine though, you've got that file don't sweat it.
---
## Account Queries
section contents
+ [Users recently created in Active Directory](#users-recently-created-in-active-directory)
+ [Hone in on suspicious user](#hone-in-on-suspicious-user)
+ [Retrieve local user accounts that are enabled](#retrieve-local-user-accounts-that-are-enabled)
+ [Find all users currently logged in](#find-all-users-currently-logged-in)
- [Find all users logged in across entire AD](#Find-all-users-logged-in-across-entire-AD)
+ [Evict User](#Evict-User)
- [Force user logout](#Force-user-logout)
- [Force user new password](#force-user-new-password)
- [Disable AD Account](#Disable-ad-account)
- [Evict from Group](#evict-from-group)
+ [Computer / Machine Accounts](#computer---machine-accounts)
- [Show machine accounts that are apart of interesting groups.](#show-machine-accounts-that-are-apart-of-interesting-groups)
- [Reset password for a machine account.](#reset-password-for-a-machine-account)
+ [Query Group Policy](#query-group-policy)
+ [All Users PowerShell History](#All-Users-PowerShell-History)
### Users recently created in Active Directory
*Run on a Domain Controller*.
Change the AddDays field to more or less days if you want. Right now set to seven days.
The 'when Created' field is great for noticing some inconsistencies. For example, how often are users created at 2am?
```powershell
import-module ActiveDirectory;
$When = ((Get-Date).AddDays(-7)).Date;
Get-ADUser -Filter {whenCreated -ge $When} -Properties whenCreated |
sort whenCreated -descending
```

### Hone in on suspicious user
You can use the `SamAccountName` above to filter
```powershell
import-module ActiveDirectory;
Get-ADUser -Identity HamBurglar -Properties *
```

### Retrieve local user accounts that are enabled
```powershell
Get-LocalUser | ? Enabled -eq "True"
```

### Find all users currently logged in
```powershell
qwinsta
#or
quser
```
#### Find all users logged in across entire AD
If you want to find every single user logged in on your Active Directory, with the machine they are also signed in to.
I can reccomend YossiSassi's [Get-UserSession.ps1](https://github.com/YossiSassi/Get-UserSession/blob/master/Get-UserSession.ps1) and [Get-RemotePSSession.ps1](https://github.com/YossiSassi/Get-RemotePSSession/blob/master/Get-RemotePSSession.ps1).
This will generate a LOT of data in a real-world AD though.

### Evict User
#### Force user logout
You may need to evict a user from a session - perhaps you can see an adversary has been able to steal a user's creds and is leveraging their account to traverse your environment
```powershell
#show the users' session
qwinsta
#target their session id
logoff 2 /v
```

#### Force user new password
From the above instance, we may want to force a user to have a new password - one the adversary does not have
##### for Active Directory
```powershell
$user = "lizzie" ; $newPass = "HoDHSyxkzP-cuzjm6S6VF-7rvqKyR";
#Change password twice.
#First can be junk password, second time can be real new password
Set-ADAccountPassword -Identity $user -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "6;wB3yj9cI8X" -Force) -verbose
Set-ADAccountPassword -Identity $user -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "$newPass" -Force) -verbose
```

##### For local non-domain joined machines
```powershell
#for local users
net user #username #newpass
net user frank "lFjcVR7fW2-HoDHSyxkzP"
```

#### Disable AD Account
```powershell
#needs the SAMAccountName
$user = "lizzie";
Disable-ADAccount -Identity "$user" #-whatif can be appended
#check its disabled
(Get-ADUser -Identity $user).enabled
#renable when you're ready
Enable-ADAccount -Identity "$user" -verbose
```


#### Disable local Account
```powershell
# list accounts with Get-LocalUser
Disable-LocalUser -name "bad_account$"
```

#### Evict from Group
Good if you need to quickly eject an account from a specific group, like administrators or remote management.
```powershell
$user = "erochester"
remove-adgroupmember -identity Administrators -members $User -verbose -confirm:$false
```

### Computer / Machine Accounts
Adversaries like to use Machine accounts (accounts that have a $) as these often are overpowered AND fly under the defenders' radar
#### Show machine accounts that are apart of interesting groups.
There may be misconfigurations that an adversary could take advantadge.
```powershell
Get-ADComputer -Filter * -Properties MemberOf | ? {$_.MemberOf}
```

#### Reset password for a machine account.
Good for depriving adversary of pass they may have got.
Also good for re-establishing trust if machine is kicked out of domain trust for reasons(?)
```powershell
Reset-ComputerMachinePassword
```
### All Users PowerShell History
During an IR, you will want to access other users PowerShell history. However, the get-history command only will retrieve the current shell's history, which isn't very useful.
Instead, [PowerShell in Windows 10 saves the last 4096 commands in a particular file](https://social.technet.microsoft.com/Forums/en-US/7c3cd614-f793-4b99-b826-3dff917ebe88/powershell-commands-history-windows-10-1809-psreadline?forum=win10itprogeneral#:~:text=By%20default%2C%20the%20PowerShell%20in,separately%20for%20PowerShell%20and%20ISE.). On an endpoint, we can run a quick loop that will print the full path of the history file - showing which users history it is showing - and then show the contents of that users' PwSh commands
```powershell
$Users = (Gci C:\Users\*\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt).FullName
$Pasts = @($Users);
foreach ($Past in $Pasts) {
write-host "`n----User Pwsh History Path $Past---`n" -ForegroundColor Magenta;
get-content $Past
}
```

And check this one too
```powershell
c:\windows\system32\config\systemprofile\appdata\roaming\microsoft\windows\powershell\psreadline\consolehost_history.txt
```
---
## Service Queries
section contents
+ [Show Services](#Show-Services)
+ [Hone in on specific Service](#hone-in-on-specific-service)
+ [Kill a service](#kill-a-service)
+ [Hunting potential sneaky services](#Hunting-potential-sneaky-services)
### Show Services
Let's get all the services and sort by what's running
```powershell
get-service|Select Name,DisplayName,Status|
sort status -descending | ft -Property * -AutoSize|
Out-String -Width 4096
```

Now show the underlying executable supporting that service
```powershell
Get-WmiObject win32_service |? State -match "running" |
select Name, DisplayName, PathName, User | sort Name |
ft -wrap -autosize
```

### Hone in on specific Service
If a specific service catches your eye, you can get all the info for it. Because the single and double qoutes are important to getting this right, I find it easier to just put the DisplayName of the service I want as a variable, as I tend to fuck up the displayname filter bit
```powershell
$Name = "eventlog";
gwmi -Class Win32_Service -Filter "Name = '$Name' " | fl *
#or this, but you get less information compared to the one about tbh
get-service -name "eventlog" | fl *
```

### Kill a service
```powershell
Get-Service -DisplayName "meme_service" | Stop-Service -Force -Confirm:$false -verbose
```
### Hunting potential sneaky services
I saw a red team tweet regarding [sneaky service install](https://twitter.com/Alh4zr3d/status/1580925761996828672?s=20&t=3IV0LmMvw-ThCCj_kgpjwg). To identify this, you can deploy the following:
```powershell
Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\services\*" |
ft PSChildName, ImagePath -autosize | out-string -width 800
# Grep out results from System32 to reduce noise, though keep in mind adversaries can just put stuff in there too
Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\services\*" |
where ImagePath -notlike "*System32*" |
ft PSChildName, ImagePath -autosize | out-string -width 800
```

---
## Network Queries
section contents
+ [Show TCP connections and underlying process](#Show-TCP-connections-and-underlying-process)
+ [Find internet established connections, and sort by time established](#find-internet-established-connections--and-sort-by-time-established)
+ [Sort remote IP connections, and then unique them](#sort-remote-ip-connections--and-then-unique-them)
- [Hone in on a suspicious IP](#hone-in-on-a-suspicious-ip)
+ [Show UDP connections](#show-udp-connections)
+ [Kill a connection](#kill-a-connection)
+ [Check Hosts file](#check-Hosts-file)
- [Check Host file Time](#Check-Host-file-time)
+ [DNS Cache](#dns-cache)
- [Investigate DNS](#investigate-dns)
+ [IPv6](#ipv6)
- [Disable Priority Treatment of IPv6](#Disable-Priority-Treatment-of-IPv6)
+ [BITS Queries](#bits-queries)
### Show TCP connections and underlying process
This one is so important, I have it [listed twice](#Processes-and-TCP-Connections) in the blue team notes
I have a neat one-liner for you. This will show you the local IP and port, the remote IP andport, the process name, and the underlying executable of the process!
You could just use `netstat -b`, which gives you SOME of this data
But instead, try this bad boy on for size:
```powershell
Get-NetTCPConnection |
select LocalAddress,localport,remoteaddress,remoteport,state,@{name="process";Expression={(get-process -id $_.OwningProcess).ProcessName}}, @{Name="cmdline";Expression={(Get-WmiObject Win32_Process -filter "ProcessId = $($_.OwningProcess)").commandline}} |
sort Remoteaddress -Descending | ft -wrap -autosize
#### you can search/filter by the commandline process, but it will come out janky.
##### in the final field we're searching by `anydesk`
Get-NetTCPConnection |
select LocalAddress,localport,remoteaddress,remoteport,state,@{name="process";Expression={(get-process -id $_.OwningProcess).ProcessName}}, @{Name="cmdline";Expression={(Get-WmiObject Win32_Process -filter "ProcessId = $($_.OwningProcess)").commandline}}
| Select-String -Pattern 'anydesk'
```

######## **Bound to catch bad guys or your moneyback guaranteed!!!!**
### Find internet established connections, and sort by time established
You can always sort by whatever value you want really. CreationTime is just an example
```powershell
Get-NetTCPConnection -AppliedSetting Internet |
select-object -property remoteaddress, remoteport, creationtime |
Sort-Object -Property creationtime |
format-table -autosize
```

### Sort remote IP connections, and then unique them
This really makes strange IPs stand out
```powershell
(Get-NetTCPConnection).remoteaddress | Sort-Object -Unique
```

#### Hone in on a suspicious IP
If you see suspicious IP address in any of the above, then I would hone in on it
```powershell
Get-NetTCPConnection |
? {($_.RemoteAddress -eq "1.2.3.4")} |
select-object -property state, creationtime, localport,remoteport | ft -autosize
## can do this as well
Get-NetTCPConnection -remoteaddress 0.0.0.0 |
select state, creationtime, localport,remoteport | ft -autosize
```

### Show UDP connections
You can generally filter pwsh UDP the way we did the above TCP
```powershell
Get-NetUDPEndpoint | select local*,creationtime, remote* | ft -autosize
```

### Kill a connection
There's probably a better way to do this. But essentially, get the tcp connection that has the specific remote IPv4/6 you want to kill. It will collect the OwningProcess. From here, get-process then filters for those owningprocess ID numbers. And then it will stop said process. Bit clunky
``` powershell
stop-process -verbose -force -Confirm:$false (Get-Process -Id (Get-NetTCPConnection -RemoteAddress "1.2.3.4" ).OwningProcess)
```
### Check Hosts file
Some malware may attempt DNS hijacking, and alter your Hosts file
```powershell
gc -tail 4 "C:\Windows\System32\Drivers\etc\hosts"
#the above gets the most important bit of the hosts file. If you want more, try this:
gc "C:\Windows\System32\Drivers\etc\hosts"
```
#### Check Host file Time
Don't trust timestamps....however, may be interesting to see if altered recently
```powershell
gci "C:\Windows\System32\Drivers\etc\hosts" | fl *Time*
```

### DNS Cache
Collect the DNS cache on an endpoint. Good for catching any sneaky communication or sometimes even DNS C2
```powershell
Get-DnsClientCache | out-string -width 1000
```

#### Investigate DNS
The above command will likely return a lot of results you don't really need about the communication between 'trusted' endpoints and servers. We can filter these 'trusted' hostnames out with regex, until we're left with less common results.
On the second line of the below code, change up and insert the regex that will filter out your machines. For example, if your machines are generally called WrkSt1001.corp.local, or ServStFAX.corp.local, you can regex out that first poriton so it will exclude any and all machines that share this - so `workst|servst` would do the job. You don't need to wildcard here.
Be careful though. If you are too generic and liberal, you may end up filtering out malicious and important results. It's bettter to be a bit specific, and drill down further to amake sure you aren't filtering out important info. So for example, I wouldn't suggest filtering out short combos of letters or numbers `ae|ou|34|`
```powershell
Get-DnsClientCache |
? Entry -NotMatch "workst|servst|memes|kerb|ws|ocsp" |
out-string -width 1000
```
If there's an IP you're sus of, you can always take it to [WHOIS](https://who.is/) or [VirusTotal](https://www.virustotal.com/gui/home/search), as well see for other instances it appears in your network and what's up to whilst it's interacting there.
### IPv6
Since Windows Vitsa, the Windows OS prioritises IPv6 over IPv4. This lends itself to man-in-the-middle attacks, you can find some more info on exploitation [here](https://www.youtube.com/watch?v=zzbIuslB58c)
Get IPv6 addresses and networks
```powershell
Get-NetIPAddress -AddressFamily IPv6 | ft Interfacealias, IPv6Address
```

#### Disable Priority Treatment of IPv6
You probably don't want to switch IPv6 straight off. And if you DO want to, then it's probably better at a DHCP level. But what we can do is change how the OS will prioritise the IPv6 over IPv4.
```powershell
#check if machine prioritises IPv6
ping $env:COMPUTERNAME -n 4 # if this returns an IPv6, the machine prioritises this over IPv4
#Reg changes to de-prioritise IPv6
New-ItemProperty “HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\” -Name “DisabledComponents” -Value 0x20 -PropertyType “DWord”
#If this reg already exists and has values, change the value
Set-ItemProperty “HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\” -Name “DisabledComponents” -Value 0x20
#you need to restart the computer for this to take affect
#Restart-Computer
```

### BITS Queries
```powershell
Get-BitsTransfer|
fl DisplayName,JobState,TransferType,FileList, OwnerAccount,BytesTransferred,CreationTime,TransferCompletionTime
## filter out common bits jobs in your enviro, ones below are just an example, you will need to add your own context
Get-BitsTransfer|
| ? displayname -notmatch "WU|Office|Dell_Asimov|configjson" |
fl DisplayName,JobState,TransferType,FileList, OwnerAccount,BytesTransferred,CreationTime,TransferCompletionTime
## Hunt down BITS transfers that are UPLOADING, which may be sign of data exfil
Get-BitsTransfer|
? TransferType -match "Upload" |
fl DisplayName,JobState,TransferType,FileList, OwnerAccount,BytesTransferred,CreationTime,TransferCompletionTime
```

## Remoting Queries
section contents
+ [Powershell Remoting](#powershell-remoting)
- [Remoting Permissions](#remoting-permissions)
- [Query WinRM Sessions Deeper](#Query-WinRM-sessions-Deeper)
- [Check Constrained Language](#check-constrained-language)
+ [RDP Settings](#rdp-settings)
+ [Query RDP Logs](#query-rdp-logs)
+ [Current RDP Sessions](#current-rdp-sessions)
+ [Check Certificates](#check-certificates)
- [Certificate Dates](#certificate-dates)
### Powershell Remoting
Get Powershell sessions created
```powershell
Get-PSSession
```
#### Query WinRM Sessions Deeper
You can query the above even deeper.
```powershell
get-wsmaninstance -resourceuri shell -enumerate |
select Name, State, Owner, ClientIP, ProcessID, MemoryUsed,
@{Name = "ShellRunTime"; Expression = {[System.Xml.XmlConvert]::ToTimeSpan($_.ShellRunTime)}},
@{Name = "ShellInactivity"; Expression = {[System.Xml.XmlConvert]::ToTimeSpan($_.ShellInactivity)}}
```

The ClientIP field will show the original IP address that WinRM'd to the remote machine.
The times under the Shell fields at the bottom have been converted into HH:MM:SS, so in the above example, the remote PowerShell session has been running for 0 hours, 4 minutes, and 26 seconds.
#### Remoting Permissions
```powershell
Get-PSSessionConfiguration |
fl Name, PSVersion, Permission
```

### Check Constrained Language
To be honest, constrained language mode in Powershell can be trivally easy to mitigate for an adversary. And it's difficult to implement persistently. But anyway. You can use this quick variable to confirm if a machine has a constrained language mode for pwsh.
```powershell
$ExecutionContext.SessionState.LanguageMode
```

### RDP settings
You can check if RDP capability is permissioned on an endpoint
```powershell
if ((Get-ItemProperty "hklm:\System\CurrentControlSet\Control\Terminal Server").fDenyTSConnections -eq 0){write-host "RDP Enabled" } else { echo "RDP Disabled" }
```
If you want to block RDP
```powershell
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -name "fDenyTSConnections" -value 1
#Firewall it out too
Disable-NetFirewallRule -DisplayGroup "Remote Desktop"
```
### Query RDP Logs
Knowing who is RDPing in your enviroment, and from where, is important. Unfortunately, RDP logs are balllache. [Threat hunting blogs like this one](https://research.nccgroup.com/2021/10/21/detecting-and-protecting-when-remote-desktop-protocol-rdp-is-open-to-the-internet/) can help you narrow down what you are looking for when it comes to RDP
Let's call on one of the RDP logs, and filter for event ID 1149, which means a RDP connection has been made. Then let's filter out any IPv4 addresses that begin with 10.200, as this is the internal IP schema. Perhaps I want to hunt down public IP addresses, as this would suggest the RDP is exposed to the internet on the machine and an adversary has connected with correct credentials!!!
[Two logs of interest](https://www.security-hive.com/post/rdp-forensics-logging-detection-and-forensics)
* Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational
* Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx
```powershell
# if you acquire a log, change this to get-winevent -path ./RDP_log_you_acquired.evtx
get-winevent -path "./Microsoft-Windows-TerminalServices-RemoteConnectionManager%4Operational.evtx" |
? id -match 1149 |
sort Time* -descending |
fl time*, message
get-winevent -path ./ "Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx" |
? id -match 21 |
sort Time* -descending |
fl time*, message
```

### Current RDP Sessions
You can query the RDP sessions that a [system is currently running](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/qwinsta)
```cmd
qwinsta
:: get some stats
qwinsta /counter
```

You can read here about [how to evict](#Evict-Users) a malicious user from a session and change the creds rapidly to deny them future access
### Check Certificates
```powershell
gci "cert:\" -recurse | fl FriendlyName, Subject, Not*
```

#### Certificate Dates
You will be dissapointed how many certificates are expired but still in use. Use the `-ExpiringInDays` flag
```powershell
gci "cert:\*" -recurse -ExpiringInDays 0 | fl FriendlyName, Subject, Not*
```
## Firewall Queries
section contents
+ [Retreieve Firewall profile names](#retreieve-firewall-profile-names)
- [Retrieve rules of specific profile](#retrieve-rules-of-specific-profile)
+ [Filter all firewall rules](#filter-all-firewall-rules)
+ [Code Red](#code-red)
- [Isolate Endpoint](#isolate-endpoint)
### Retrieve Firewall profile names
```powershell
(Get-NetFirewallProfile).name
```

#### Retrieve rules of specific profile
Not likely to be too useful getting all of this information raw, so add plenty of filters
```powershell
Get-NetFirewallProfile -Name Public | Get-NetFirewallRule
##filtering it to only show rules that are actually enabled
Get-NetFirewallProfile -Name Public | Get-NetFirewallRule | ? Enabled -eq "true"
```

### Filter all firewall rules
```powershell
#show firewall rules that are enabled
Get-NetFirewallRule | ? Enabled -eq "true"
#will show rules that are not enabled
Get-NetFirewallRule | ? Enabled -notmatch "true"
##show firewall rules that pertain to inbound
Get-NetFirewallRule | ? direction -eq "inbound"
#or outbound
Get-NetFirewallRule | ? direction -eq "outbound"
##stack these filters
Get-NetFirewallRule | where {($_.Enabled -eq "true" -and $_.Direction -eq "inbound")}
#or just use the built in flags lol
Get-NetFirewallRule -Enabled True -Direction Inbound
```
### Code Red
#### Isolate Endpoint
Disconnect network adaptor, firewall the fuck out of an endpoint, and display warning box
This is a code-red command. Used to isolate a machine in an emergency.
In the penultimate and final line, you can change the text and title that will pop up for the user
```powershell
New-NetFirewallRule -DisplayName "Block all outbound traffic" -Direction Outbound -Action Block | out-null;
New-NetFirewallRule -DisplayName "Block all inbound traffic" -Direction Inbound -Action Block | out-null;
$adapter = Get-NetAdapter|foreach { $_.Name } ; Disable-NetAdapter -Name "$adapter" -Confirm:$false;
Add-Type -AssemblyName PresentationCore,PresentationFramework;
[System.Windows.MessageBox]::Show('Your Computer has been Disconnected from the Internet for Security Issues. Please do not try to re-connect to the internet. Contact Security Helpdesk Desk ',' CompanyNameHere Security Alert',[System.Windows.MessageBoxButton]::OK,[System.Windows.MessageBoxImage]::Information)
```

---
---
## SMB Queries
section contents
+ [List Shares](#list-shares)
+ [List client-to-server SMB Connections](#list-client-to-server-smb-connections)
+ [Remove an SMB Share](#remove-an-smb-share)
### List Shares
```powershell
Get-SMBShare
```

### List client-to-server SMB Connections
Dialect just means verison. SMB3, SMB2 etc
``` powershell
Get-SmbConnection
#just show SMB Versions being used. Great for enumeration flaws in enviro - i.e, smb1 being used somewhere
Get-SmbConnection |
select Dialect, Servername, Sharename | sort Dialect
```


### Remove an SMB Share
```powershell
Remove-SmbShare -Name MaliciousShare -Confirm:$false -verbose
```
---
## Process Queries
section contents
+ [Processes and TCP Connections](#processes-and-tcp-connections)
+ [Show all processes and their associated user](#show-all-processes-and-their-associated-user)
+ [Get specific info about the full path binary that a process is running](#get-specific-info-about-the-full-path-binary-that-a-process-is-running)
+ [Is a specific process a running on a machine or not](#is-a-specific-process-a-running-on-a-machine-or-not)
+ [Get process hash](#get-process-hash)
+ [Show all DLLs loaded with a process](#show-all-dlls-loaded-with-a-process)
+ [Identify process CPU usage](#identify-process-cpu-usage)
- [Sort by least CPU-intensive processes](#sort-by-least-cpu-intensive-processes)
+ [Stop a Process](#stop-a-process)
+ [Process Tree](#process-tree)
### Processes and TCP Connections
I have a neat one-liner for you. This will show you the local IP and port, the remote IP andport, the process name, and the underlying executable of the process!
You could just use `netstat -b`, which gives you SOME of this data

But instead, try this bad boy on for size:
```powershell
Get-NetTCPConnection |
select LocalAddress,localport,remoteaddress,remoteport,state,@{name="process";Expression={(get-process -id $_.OwningProcess).ProcessName}}, @{Name="cmdline";Expression={(Get-WmiObject Win32_Process -filter "ProcessId = $($_.OwningProcess)").commandline}} |
sort Remoteaddress -Descending | ft -wrap -autosize
```

### Show all processes and their associated user
```powershell
get-process * -Includeusername
```

Try this one if you're hunting down suspicious processes from users
```powershell
gwmi win32_process |
Select Name,@{n='Owner';e={$_.GetOwner().User}},CommandLine |
sort Name -unique -descending | Sort Owner | ft -wrap -autosize
```

### Get specific info about the full path binary that a process is running
```powershell
gwmi win32_process |
Select Name,ProcessID,@{n='Owner';e={$_.GetOwner().User}},CommandLine |
sort name | ft -wrap -autosize | out-string
```

### Get specific info a process is running
```powershell
get-process -name "nc" | ft Name, Id, Path,StartTime,Includeusername -autosize
```

### Is a specific process a running on a machine or not
```powershell
$process = "memes";
if (ps | where-object ProcessName -Match "$process") {Write-Host "$process successfully installed on " -NoNewline ; hostname} else {write-host "$process absent from " -NoNewline ; hostname}
```
Example of process that is absent

Example of process that is present

### Get process hash
Great to make malicious process stand out. If you want a different Algorithm, just change it after `-Algorithm` to something like `sha256`
```powershell
foreach ($proc in Get-Process | select path -Unique){try
{ Get-FileHash $proc.path -Algorithm sha256 -ErrorAction stop |
ft hash, path -autosize -HideTableHeaders | out-string -width 800 }catch{}}
```

### Show all DLLs loaded with a process
```powershell
get-process -name "memestask" -module
```

Alternatively, pipe `|fl` and it will give a granularity to the DLLs

### Identify process CPU usage
```powershell
(Get-Process -name "googleupdate").CPU | fl
```

I get mixed results with this command but it's supposed to give the percent of CPU usage. I need to work on this, but I'm putting it in here so the world may bare wittness to my smooth brain.
```powershell
$ProcessName = "symon" ;
$ProcessName = (Get-Process -Id $ProcessPID).Name;
$CpuCores = (Get-WMIObject Win32_ComputerSystem).NumberOfLogicalProcessors;
$Samples = (Get-Counter "\Process($Processname*)\% Processor Time").CounterSamples;
$Samples | Select `InstanceName,@{Name="CPU %";Expression={[Decimal]::Round(($_.CookedValue / $CpuCores), 2)}}
```

### Sort by least CPU-intensive processes
Right now will show the lower cpu-using proccesses...useful as malicious process probably won't be as big a CPU as Chrome, for example. But change first line to `Sort CPU -descending` if you want to see the chungus processes first
```powershell
gps | Sort CPU |
Select -Property ProcessName, CPU, ID, StartTime |
ft -autosize -wrap | out-string -width 800
```

### Stop a Process
```powershell
Get-Process -Name "memeprocess" | Stop-Process -Force -Confirm:$false -verbose
```
### Process Tree
You can download the [PsList exe from Sysinternals](https://docs.microsoft.com/en-us/sysinternals/downloads/pslist)
Fire it off with the `-t` flag to create a parent-child tree of the processes

---
## Recurring Task Queries
section contents
+ [Get scheduled tasks](#get-scheduled-tasks)
- [Get a specific schtask](#get-a-specific-schtask)
- [To find the commands a task is running](#to-find-the-commands-a-task-is-running)
- [To stop the task](#to-stop-the-task)
- [All schtask locations](#all-schtask-locations)
- [Sneaky Schtasks via the Registry](#Sneaky-Schtasks-via-the-Registry)
+ [Show what programs run at startup](#show-what-programs-run-at-startup)
- [Programs at login](#programs-at-login)
- [Programs at PowerShell](#programs-at-powershell)
+ [Stolen Links](#stolen-links)
+ [Scheduled Jobs](#scheduled-jobs)
- [Find out what scheduled jobs are on the machine](#find-out-what-scheduled-jobs-are-on-the-machine)
- [Get detail behind scheduled jobs](#get-detail-behind-scheduled-jobs)
- [Kill job](#kill-job)
+ [Hunt WMI Persistence](#hunt-wmi-persistence)
- [Removing it](#removing-it)
- [A note on CIM](#a-note-on-cim)
+ [Run Keys](#Run-Keys)
- [What are Run Keys](#what-are-run-keys)
- [Finding Run Evil](#Finding-Run-Evil)
- [Removing Run Evil](#removing-run-evil)
- [Other Malicious Run Locations](#other-malicious-run-locations)
- [Evidence of Run Key Execution](#Evidence-of-Run-Key-Execution)
+ [Screensaver Persistence](#Screensaver-Persistence)
+ [Query Group Policy](#Query-Group-Policy)
- [Query GPO Scripts](#query-gpo-scripts)
+ [Autoruns](#autoruns)
### Get scheduled tasks
Identify the user behind a command too. Great at catching out malicious schtasks that perhaps are imitating names, or a process name
```powershell
schtasks /query /FO CSV /v | convertfrom-csv |
where { $_.TaskName -ne "TaskName" } |
select "TaskName","Run As User", Author, "Task to Run"|
fl | out-string
```

#### Get a specific schtask
```powershell
Get-ScheduledTask -Taskname "wifi*" | fl *
```

#### To find the commands a task is running
Great one liner to find exactly WHAT a regular task is doing
```powershell
$task = Get-ScheduledTask | where TaskName -EQ "meme task";
$task.Actions
```

And a command to get granularity behind the schtask requires you to give the taskpath. Tasks with more than one taskpath will throw an error here
```powershell
$task = "CacheTask";
get-scheduledtask -taskpath (Get-ScheduledTask -Taskname "$task").taskpath | Export-ScheduledTask
#this isn't the way the microsoft docs advise.
##But I prefer this, as it means I don't need to go and get the taskpath when I already know the taskname
```

#### To stop the task
```powershell
Get-ScheduledTask "memetask" | Stop-ScheduledTask -Force -Confirm:$false -verbose
```
#### All schtask locations
There's some major overlap here, but it pays to be thorough.
```
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\Tree
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\Tasks
C:\Windows\System32\Tasks
C:\Windows\Tasks
C:\windows\SysWOW64\Tasks\
```
You can compare the above for tasks missing from the C:\Windows directories, but present in the Registry.
```powershell
# From my man Anthony Smith - https://www.linkedin.com/in/anthony-c-smith/
$Reg=(Get-ItemProperty -path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\tree\*").PsChildName
$XMLs = (ls C:\windows\System32\Tasks\).Name
Compare-Object $Reg $XMLs
```

#### Sneaky Schtasks via the Registry
Threat actors have been known to manipulate scheduled tasks in such a way that Task Scheduler no longer has visibility of the recuring task.
However, querying the Registry locations `HKLM\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\Tree` and `HKLM\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\Tasks`, can reveal a slice of these sneaky tasks.
Shout out to my man [@themalwareguy](https://twitter.com/themalwareguy) for the $fixedstring line that regexes in/out good/bad characters.
```Powershell
# the schtask for our example
# schtasks /create /tn "Find_Me" /tr calc.exe /sc minute /mo 100 /k
# Loop and parse \Taskcache\Tasks Registry location for scheduled tasks
## Parses Actions to show the underlying binary / commands for the schtask
## Could replace Actions with Trigggers on line 10, after ExpandedProperty
(Get-ItemProperty "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\Tasks\*").PSChildName |
Foreach-Object {
write-host "----Schtask ID is $_---" -ForegroundColor Magenta ;
$hexstring = Get-ItemProperty "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\Tasks\$_" | Select -ExpandProperty Actions;
$fixedstring = [System.Text.Encoding]::Unicode.GetString($hexstring) -replace '[^a-zA-Z0-9\\._\-\:\%\/\$ ]', ' '; # Obtaining the Unicode string reduces the chances of getting invalid characters, and the regex will assist in stripping each string of junk
write-host $fixedstring
}
```

If you don't need to loop to search, because you know what you're gunning for then you can just deploy this
```powershell
$hexstring = (Get-ItemProperty "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\Tasks\{ID}" |
Select -ExpandProperty Actions) -join ',' ; $hexstring.Split(" ")
## can then go to cyberchef, and convert From Decimal with the comma (,) delimineter
```

Once you've deployed the above loop, and zoned in on a binary / one-liner that seems sus, you can query it in the other Registry location
```PowerShell
# Then for the ID of interest under \Taskcache\Tree subkey
# Example: $ID = "{8E350038-3475-413A-A1AE-20711DD11C95}" ;
$ID = "{XYZ}" ;
get-itemproperty -path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\Tree\*" |
? Id -Match "$ID" | fl *Name,Id,PsPath
```

And then eradicating these Registry schtask entries is straight forward via Regedit's GUI, that way you have no permission problems. Delete both:
* HKLM\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\Tasks\\{$ID}
* HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Schedule\Taskcache\Tree\\$Name

### Show what programs run at startup
```powershell
Get-CimInstance Win32_StartupCommand | Select-Object Name, command, Location, User | Format-List
```

Some direct path locations too can be checked
```powershell
HKLM\software\classes\exefile\shell\open\command
c:\Users\*\appdata\roaming\microsoft\windows\start menu\programs\startup
```
Querying that last one in more detail, you have some interesting options
```powershell
#Just list out the files in each user's startup folder
(gci "c:\Users\*\appdata\roaming\microsoft\windows\start menu\programs\startup\*").fullname
#Extract from the path User, Exe, and print machine name
(gci "c:\Users\*\appdata\roaming\microsoft\windows\start menu\programs\startup\*").fullname |
foreach-object {$data = $_.split("\\");write-output "$($data[2]), $($data[10]), $(hostname)"}
#Check the first couple lines of files' contents
(gci "c:\Users\*\appdata\roaming\microsoft\windows\start menu\programs\startup\*").fullname |
foreach-object {write-host `n$_`n; gc $_ -encoding byte| fhx |select -first 5}
```

#### Programs at login
Adversaries can link persistence mechanisms to be activated to a users' login via the registry `HKEY_CURRENT_USER\Environment -UserInitMprLogonScript`
```powershell
#Create HKU drive
mount -PSProvider Registry -Name HKU -Root HKEY_USERS
#list all user's enviros
(gp "HKU:\*\Environment").UserInitMprLogonScript
#Collect SID of target user with related logon task
gp "HKU:\*\Environment" | FL PSParentPath,UserInitMprLogonScript
# insert SID and convert it into username
gwmi win32_useraccount |
select Name, SID |
? SID -match "" #insert SID between quotes
```

You can remove this regsistry entry
```powershell
#confirm via `whatif` flag that this is the right key
remove-itemproperty "HKU:\SID-\Environment\" -name "UserInitMprLogonScript" -whatif
#delete it
remove-itemproperty "HKU:\SID-\Environment\" -name "UserInitMprLogonScript" -verbose
```

#### Programs at Powershell
Adversaries can link their persistence mechanisms to a PowerShell profile, executing their malice every time you start PowerShell
```powershell
#confirm the profile you are querying
echo $Profile
#show PowerShell profile contents
type $Profile
```

To fix this one, I'd just edit the profile and remove the persistence (so `notepad $Profile` will be just fine)
You can get a bit more clever with this if you want
```powershell
(gci C:\Users\*\Documents\WindowsPowerShell\*profile.ps1, C:\Windows\System32\WindowsPowerShell\v1.0\*profile.ps1).FullName|
Foreach-Object {
write-host "----$_---" -ForegroundColor Magenta ;
gc $_ # | select-string -notmatch function ## if you want to grep out stuff you don't wanna see, uncomment
}
```

### Stolen Links
Adversaries can insert their malice into shortcuts. They can do it in clever ways, so that the application will still run but at the same time their malice will also execute when you click on the application
For demo purposes, below we have Microsoft Edge that has been hijacked to execute calc on execution.

We can specifically query all Microsoft Edge's shortcuts to find this
```powershell
Get-CimInstance Win32_ShortcutFile |
? FileName -match 'edge' |
fl FileName,Name,Target, LastModified
```

This doesn't scale however, as you will not know the specific shortcut that the adversary has manipulated. So instead, sort by the `LastModified` date
```powershell
Get-CimInstance Win32_ShortcutFile |
sort LastModified -desc |
fl FileName,Name,Target, LastModified
```

#### Hunt LNKs at scale
This above will output a LOT, however. You may want to only show results for anything LastModified after a certain date. Lets ask to only see things modified in the year 2022 onwards
```powershell
Get-CimInstance Win32_ShortcutFile |
where-object {$_.lastmodified -gt [datetime]::parse("01/01/2022")} |
sort LastModified -desc | fl FileName,Name,Target, LastModified
```

### Scheduled Jobs
Surprisingly, not many people know about [Scheduled Jobs](https://devblogs.microsoft.com/scripting/introduction-to-powershell-scheduled-jobs/). They're not anything too strange or different, they're just scheduled tasks that are specificially powershell.
[I've written about a real life encounter I had during an incident](https://labs.jumpsec.com/powershell-jobs/), where the adversary had leveraged a PowerShell scheduled job to execute their malice at an oppertune time
#### Find out what scheduled jobs are on the machine
```powershell
Get-ScheduledJob
# pipe to | fl * for greater granularity
```

#### Get detail behind scheduled jobs
```powershell
Get-ScheduledJob | Get-JobTrigger |
Ft -Property @{Label="ScheduledJob";Expression={$_.JobDefinition.Name}},ID,Enabled, At, frequency, DaysOfWeek
#pipe to fl or ft, whatever you like the look of more in the screenshot
```

#### Kill job
The following all work.
```powershell
Disable-ScheduledJob -Name evil_sched
Unregister-ScheduledJob -Name eviler_sched
Remove-Job -id 3
#then double check it's gone with Get-ScheduledJob
#if persists, tack on to unregister or remove-job
-Force -Confirm:$false -verbose
```
### Hunt WMI Persistence
WMIC can do some pretty evil things [1](https://www.fireeye.com/content/dam/fireeye-www/global/en/current-threats/pdfs/wp-windows-management-instrumentation.pdf) & [2](https://in.security/an-intro-into-abusing-and-identifying-wmi-event-subscriptions-for-persistence/). One sneaky, pro-gamer move it can pull is *persistence*
In the image below I have included a part of setting up WMI persistence

##### Finding it
Now, our task is to find this persistent evil.
Get-CimInstance comes out cleaner, but you can always rely on the alternate Get-WMIObject
```powershell
Get-CimInstance -Namespace root\Subscription -Class __FilterToConsumerBinding
Get-CimInstance -Namespace root\Subscription -Class __EventFilter
Get-CimInstance -Namespace root\Subscription -Class __EventConsumer
## OR
Get-WMIObject -Namespace root\Subscription -Class __EventFilter
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding
Get-WMIObject -Namespace root\Subscription -Class __EventConsumer
```



#### Removing it
Now we've identified the evil WMI persistence, let us be rid of it!
We can specify the Name as `EVIL` as that's what it was called across the three services. Whatever your persistence calls itself, change the name for that
```powershell
#notice this time, we use the abbrevated version of CIM and WMI
gcim -Namespace root\Subscription -Class __EventFilter |
? Name -eq "EVIL" | Remove-CimInstance -verbose
gcim -Namespace root\Subscription -Class __EventConsumer|
? Name -eq "EVIL" | Remove-CimInstance -verbose
#it's actually easier to use gwmi here instead of gcim
gwmi -Namespace root\Subscription -Class __FilterToConsumerBinding |
? Consumer -match "EVIL" | Remove-WmiObject -verbose
```

#### A note on CIM
You may see WMI and CIM talked about together, whether on the internet or on in the Blue Team Notes here.
CIM is a standard for language for vendor-side management of a lot of the physical and digital mechanics of what makes a computer tick. WMIC was and is Microsoft's interpretation of CIM.
However, Microsoft is going to decommision WMIC soon. So using `Get-Ciminstance` versions rather than `get-wmiobject` is probably better for us to learn in the long term. I dunno man, [It's complicated](https://devblogs.microsoft.com/scripting/should-i-use-cim-or-wmi-with-windows-powershell/).
### Run Keys
#### What are Run Keys
I've written in depth [about run keys, elsewhere](https://labs.jumpsec.com/running-once-running-twice-pwned-windows-registry-run-keys/)
Run and RunOnce registry entries will run tasks on startup. Specifically:
* Run reg keys will run the task every time there's a login.
* RunOnce reg kgeys will run the taks once and then self-delete keys.
* If a RunOnce key has a name with an exclemation mark (!likethis) then it will self-delete
* IF a RunOnce key has a name with an asterik (* LikeDIS) then it can run even in Safe Mode.
If you look in the reg, you'll find some normal executables.

### Finding Run Evil
A quick pwsh _for loop_ can collect the contents of the four registry locations.
```powershell
#Create HKU drive
mount -PSProvider Registry -Name HKU -Root HKEY_USERS
(gci HKLM:\Software\Microsoft\Windows\CurrentVersion\Run, HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce, HKU:\*\Software\Microsoft\Windows\CurrentVersion\Run, HKU:\*\Software\Microsoft\Windows\CurrentVersion\RunOnce ).Pspath |
Foreach-Object {
write-host "----Reg location is $_---" -ForegroundColor Magenta ;
gp $_ |
select -property * -exclude PS*, One*, vm* | #exclude results here
FL
}
#you can squish that all in one line if you need to
(gci HKLM:\Software\Microsoft\Windows\CurrentVersion\Run, HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce, HKU:\*\Software\Microsoft\Windows\CurrentVersion\Run, HKU:\*\Software\Microsoft\Windows\CurrentVersion\RunOnce ).Pspath | Foreach-Object {write-host "----Reg location is $_---" -ForegroundColor Magenta ; gp $_ | select -property * -exclude PS*, One*, vm* |sort| fl}
```

You can also achieve the same thing with these two alternative commands, but it isn't as cool as the above for loop
```powershell
get-itemproperty "HKU:\*\Software\Microsoft\Windows\CurrentVersion\Run*" |
select -property * -exclude PSPR*,PSD*,PSC*,PSPAR* | fl
get-itemproperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run*" |
select -property * -exclude PSPR*,PSD*,PSC*,PSPAR* | fl
```

### Removing Run evil
Be surgical here. You don't want to remove Run entries that are legitimate. It's important you remove with -verbose too and double-check it has gone, to make sure you have removed what you think you have.
Specify the SID
```powershell
#Create HKU drive
mount -PSProvider Registry -Name HKU -Root HKEY_USERS
#List the malicious reg by path
get-itemproperty "HKU:\SID\Software\Microsoft\Windows\CurrentVersion\RunOnce" | select -property * -exclude PS* | fl
#Then pick the EXACT name of the Run entry you want to remove. Copy paste it, include any * or ! too please
Remove-ItemProperty -Path "HKU:\SID-\Software\Microsoft\Windows\CurrentVersion\RunOnce" -Name "*EvilerRunOnce" -verbose
#Then check again to be sure it's gone
get-itemproperty "HKU:\*\Software\Microsoft\Windows\CurrentVersion\RunOnce" | select -property * -exclude PS* | fl
```

### Other Malicious Run Locations
Some *folders* can be the locations of persistence.
```powershell
#Create HKU drive
mount -PSProvider Registry -Name HKU -Root HKEY_USERS
$folders = @("HKU:\*\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders","HKU:\*\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders","HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders","HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders")
foreach ($folder in $folders) {
write-host "----Reg key is $folder--- -ForegroundColor Magenta ";
get-itemproperty -path "$folder" |
select -property * -exclude PS* | fl
}
```

Svchost startup persistence
```powershell
get-itemproperty -path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost"
```

Winlogon startup persistence
```powershell
#Create HKU drive
mount -PSProvider Registry -Name HKU -Root HKEY_USERS
(gci "HKU:\*\Software\Microsoft\Windows NT\CurrentVersion\Winlogon").PSPath |
Foreach-Object {
write-host "----Reg location is $_---" -ForegroundColor Magenta ;
gp $_ |
select -property * -exclude PS* |
FL
}
```

Find more examples of Run key evil from [Mitre ATT&CK](https://attack.mitre.org/techniques/T1547/001/)
##### Evidence of Run Key Execution
You can query the 'Microsoft-Windows-Shell-Core/Operational' log to find evidence if a registry run key was successful in executing.
```powershell
get-winevent -filterhashtable @{ logname = "Microsoft-Windows-Shell-Core/Operational" ; ID = 9707} |
select TimeCreated, Message,
@{Name="UserName";Expression = {$_.UserId.translate([System.Security.Principal.NTAccount]).value}} |
sort TimeCreated -desc| fl
```

### Screensaver Persistence
It can be done, I swear. [Mitre ATT&CK](https://attack.mitre.org/techniques/T1546/002/) has instances of .SCR's being used to maintain regular persistence
```powershell
#Create HKU drive
mount -PSProvider Registry -Name HKU -Root HKEY_USERS
gp "HKU:\*\Control Panel\Desktop\" | select SCR* | fl
# you can then go and collect the .scr listed in the full path, and reverse engineer the binary
#you can also collect wallpaper info from here
gp "HKU:\*\Control Panel\Desktop\" | select wall* | fl
```

### Query Group Policy
The group policy in an Windows can be leveraged and weaponised to propogate malware and even ransomware across the entire domain
You can query the changes made in the last X days with this line
```powershell
#collects the domain name as a variable to use later
$domain = (Get-WmiObject -Class win32_computersystem).domain;
Get-GPO -All -Domain $domain |
?{ ([datetime]::today - ($_.ModificationTime)).Days -le 10 } | sort
# Change the digit after -le to the number of days you want to go back for
```

#### Query GPO Scripts
We can hunt down the strange thinngs we might see in our above query
We can list all of the policies, and see where a policy contains a script or executable. You can change the `include` at the end to whatever you want
```powershell
$domain = (Get-WmiObject -Class win32_computersystem).domain;
gci -recurse \\$domain\\sysvol\$domain\Policies\ -file -include *.exe, *.ps1
```

We can hunt down where GPO scripts live
```powershell
$domain = (Get-WmiObject -Class win32_computersystem).domain;
gci -recurse \\$domain\\sysvol\*\scripts
```

### Autoruns
[Autoruns](https://docs.microsoft.com/en-us/sysinternals/downloads/autoruns) is a Sysinternals tool for Windows. It offers analysts a GUI method to examine the recurring tasks that an adversary might use for persistence and other scheduled malice.
Before you go anywhere cowboy, make sure you've filtered out the known-goods under options. It makes analysis a bit easier, as you're filtering out noise. Don't treat this as gospel though, so yes hide the things that VirusTotal and Microsoft SAY are okay.....but go and verify that those auto-running tasks ARE as legitimate as they suppose they are

I personally just stick to the 'Everything' folder, as I like to have full visibility rather than go into the options one by one

Some things in autorun may immediately stick out to you as strange. Take for example the malicious run key I inserted on the VM as an example:

You can right-click and ask Virus Total to see if the hash is a known-bad

And you can right-click and ask autoruns to delete this recurring task from existence

I like autoruns for digital forensics, where you take it one machine at a time. But - in my uneducated opinion - it does not scale well. A tool like Velociraptor that allows orchestration across thousands of machines can be leveraged to query things with greater granularity than Autoruns allows.
This is why I like to use PowerShell for much of my blue team work on a Windows machine, where possible. I can pre-filter my queries so I don't get distraced by noise, but moreover I can run that fine-tuned PowerShell query network-wide across thosuands of machines and recieve the results back rapidly.
---
## File Queries
section contents
+ [File Tree](#file-tree)
+ [Wildcard paths and files](#wildcard-paths-and-files)
+ [Check if a specific file or path is alive.](#check-if-a-specific-file-or-path-is-alive)
+ [test if files and directories are present or absent](#test-if--files-and-directories-are-present-or-absent)
+ [Query File Contents](#query-file-contents)
- [Alternate data streams](#alternate-data-streams)
- [Read hex of file](#read-hex-of-file)
+ [Recursively look for particular file types, and once you find the files get their hashes](#recursively-look-for-particular-file-types--and-once-you-find-the-files-get-their-hashes)
+ [Compare two files' hashes](#compare-two-files--hashes)
+ [Find files written after X date](#find-files-written-after-x-date)
- [Remove items written after x date](#Remove-items-written-after-x-date)
+ [copy multiple files to new location](#copy-multiple-files-to-new-location)
+ [Grep in Powershell](#grep-in-powershell)
### File tree
Fire off `tree` to list the directories and files underneath your current working directory, nestled under each other

### Wildcard paths and files
You can chuck wildcards in directories for gci, as well as wildcard to include file types.
Let's say we want to look in all of the Users \temp\ directories. We don't want to put their names in, so we wildcard it.
We also might only be interested in the pwsh scripts in their \temp\, so let's filter for those only
```powershell
gci "C:\Users\*\AppData\Local\Temp\*" -Recurse -Force -File -Include *.ps1, *.psm1, *.txt |
ft lastwritetime, name -autosize |
out-string -width 800
```

### Check if a specific file or path is alive.
I've found that this is a great one to quickly check for specific vulnerabilities. Take for example, CVE-2021-21551. The one below this one is an excellent way of utilising the 'true/false' binary results that test-path can give
``` powershell
test-path -path "C:\windows\temp\DBUtil_2_3.Sys"
```

### test if files and directories are present or absent
This is great to just sanity check if things exist. Great when you're trying to check if files or directories have been left behind when you're cleaning stuff up.
```powershell
$a = Test-Path "C:\windows\sysmon.exe"; $b= Test-Path "C:\Windows\SysmonDrv.sys"; $c = test-path "C:\Program Files (x86)\sysmon"; $d = test-path "C:\Program Files\sysmon";
IF ($a -eq 'True') {Write-Host "C:\windows\sysmon.exe present"} ELSE {Write-Host "C:\windows\sysmon.exe absent"};
IF ($b -eq 'True') {Write-Host "C:\Windows\SysmonDrv.sys present"} ELSE {Write-Host "C:\Windows\SysmonDrv.sys absent"} ;
IF ($c -eq 'True') {Write-Host "C:\Program Files (x86)\sysmon present"} ELSE {Write-Host "C:\Program Files (x86)\sysmon absent"};
IF ($d -eq 'True') {Write-Host "C:\Program Files\sysmon present"} ELSE {Write-Host "C:\Program Files\sysmon absent"}
```

^ The above is a bit over-engineered. Here's an an abbrevated version
```powershell
$Paths = "C:\windows" , "C:\temp", "C:\windows\system32", "C:\DinosaurFakeDir" ;
foreach ($Item in $Paths){if
(test-path $Item) {write "$Item present"}else{write "$Item absent"}}
```

We can also make this conditional. Let's say if Process MemeProcess is NOT running, we can then else it to go and check if files exist
```powershell
$Paths = "C:\windows" , "C:\temp", "C:\windows\system32", "C:\DinosaurFakeDir" ;
if (Get-Process | where-object Processname -eq "explorer") {write "process working"} else {
foreach ($Item in $Paths){if (test-path $Item) {write "$Item present"}else{write "$Item absent"}}}
```

You can use `test-path` to query Registry, but even the 2007 [Microsoft docs say](https://devblogs.microsoft.com/powershell/test-path-we-goofed/) that this can give inconsistent results, so I wouldn't bother with test-path for reg stuff when it's during an IR
### Query File Contents
Seen a file you don't recognise? Find out some more about it! Remember though: don't trust timestamps!
```powershell
Get-item C:\Temp\Computers.csv |
select-object -property @{N='Owner';E={$_.GetAccessControl().Owner}}, *time, versioninfo | fl
```

#### Alternate data streams
```powershell
# show streams that aren't the normal $DATA
get-item evil.ps1 -stream "*" | where stream -ne ":$DATA"
# If you see an option that isn't $DATA, hone in on it
get-content evil.ps1 -steam "evil_stream"
```
#### Read hex of file
```powershell
gc .\evil.ps1 -encoding byte |
Format-Hex
```

### Recursively look for particular file types, and once you find the files get their hashes
This one-liner was a godsend during the Microsoft Exchange ballache back in early 2021
```powershell
Get-ChildItem -path "C:\windows\temp" -Recurse -Force -File -Include *.aspx, *.js, *.zip|
Get-FileHash |
format-table hash, path -autosize | out-string -width 800
```

### Compare two files' hashes
```powershell
get-filehash "C:\windows\sysmondrv.sys" , "C:\Windows\HelpPane.exe"
```

### Find files written after X date
I personally wouldn't use this for DFIR. It's easy to manipulate timestamps....plus, Windows imports the original compiled date for some files and binaries if I'm not mistaken
Change the variables in the first time to get what you're looking. Remove the third line if you want to include directories
```powershell
$date = "12/01/2021"; $directory = "C:\temp"
get-childitem "$directory" -recurse|
where-object {$_.mode -notmatch "d"}|
where-object {$_.lastwritetime -gt [datetime]::parse("$date")}|
Sort-Object -property LastWriteTime | format-table lastwritetime, fullname -autosize
```

#### Remove items written after x date
And then you can recursively remove the files and directories, in case malicious
```powershell
$date = "31/01/2022"; $directory = "C:\Users\Frank\AppData\"
get-childitem "$directory" -recurse|
where-object {$_.lastwritetime -gt [datetime]::parse("$date")}|
Sort-Object -property LastWriteTime | remove-item -confirm -whatif
```

Remove the last -whatif flag to actaully detonate. Will ask you one at a time if you want to delete items. Please A to delete all

### copy multiple files to new location
```powershell
copy-item "C:\windows\System32\winevt\Logs\Security.evtx", "C:\windows\System32\winevt\Logs\Windows PowerShell.evtx" -destination C:\temp
```
### Grep in Powershell
Change the string in the second line. You should run these one after another, as it will grep for things in unicode and then ascii.
I like to use these as really lazy low-key yara rules. So grep for the string "educational purposes only" or something like that to catch malicious tooling - you'd be surprised how any vendors take open-source stuff, re-brand and compile it, and then sell it to you.....
```powershell
ls C:\Windows\System32\* -include '*.exe', '*.dll' |
select-string 'RunHTMLApplication' -Encoding unicode |
select-object -expandproperty path -unique
#and with ascii
ls C:\Windows\System32\* -include '*.exe', '*.dll' |
select-string 'RunHTMLApplication' -Encoding Ascii |
select-object -expandproperty path -unique
```

---
## Registry Queries
section contents
+ [A note on HKCU](#A-note-on-HKCU)
+ [Show reg keys](#show-reg-keys)
+ [Read a reg entry](#read-a-reg-entry)
+ [Quick useful reg keys](#quick-useful-reg-keys)
+ [Remove a reg entry](#remove-a-reg-entry)
- [Removing HKCurrentUser Keys](#Removing-HKCurrentUser-Keys)
+ [Example Malicious Reg](#example-malicious-reg)
- [Understanding Reg Permissions](#understanding-reg-permissions)
- [Get-ACl](#get-acl)
- [Convert SDDL](#convert-sddl)
- [What could they do with poor permissions?](#what-could-they-do-with-poor-permissions)
+ [Hunting for Reg evil](#hunting-for-reg-evil)
- [Filtering Reg ImagePath](#filtering-reg-imagepath)
+ [Query Background Activity Monitor](#query-background-activity-monitor)
## A note on HKCU
Just a note:
Anywhere you see a reg key does HKCU - this is Current User. Your results will be limited to the user you are.
To see more results, you should change the above from HKCU, to HKU.
You often need the [SID of the users](https://www.windows-commandline.com/get-sid-of-user/) you want to go and look at their information.
So for example, a query like this:
`HKCU:\Control Panel\Desktop\`
Becomes:
`HKU\s-1-12-1-707864876-1224890504-1467553947-2593736053\Control Panel\Desktop`
HKU needs to be set up to work
```powershell
New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS;
(Gci -Path HKU:\).name
```

### Show reg keys
[Microsoft Docs](https://docs.microsoft.com/en-us/troubleshoot/windows-server/performance/windows-registry-advanced-users) detail the regs: their full names, abbrevated names, and what their subkeys generally house
```powershell
##show all reg keys
(Gci -Path Registry::).name
# show HK users
mount -PSProvider Registry -Name HKU -Root HKEY_USERS;(Gci -Path HKU:\).name
##lets take HKEY_CURRENT_USER as a subkey example. Let's see the entries in this subkey
(Gci -Path HKCU:\).name
# If you want to absolutely fuck your life up, you can list the names recursively....will take forever though
(Gci -Path HKCU:\ -recurse).name
```

### Read a reg entry
```powershell
Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SysmonDrv"
```

### Quick useful reg keys
Query timezone on an endpoint. Look for the TimeZoneKeyName value
* `HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation`
Query the drives on the endpoint
* `HKLM\SYSTEM\MountedDevices`
Query the services on this machine, and if you want to see more about one of the results just add it to the path
* `HKLM\SYSTEM\CurrentControlSet\Services`
* `HKLM\SYSTEM\CurrentControlSet\Services\ACPI`
Query software on this machine
* `HKLM\Software`
* `HKLM\Software\PickOne`
Query SIDs
* `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList`
* `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\[Long-SID-Number-HERE]`
Query user's wallpaper. Once we know a user’s SID, we can go and look at these things:
* `HKU\S-1-5-18\Control Panel\Desktop\`
Query if credentials on a machine are being [cached maliciously](https://blog.netwrix.com/2022/10/11/wdigest-clear-text-passwords-stealing-more-than-a-hash/)
```powershell
# can run this network-wide
if ((Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest").UseLogonCredential -eq 1){write-host "Plain text credentials forced, likely malicious, on host: " -nonewline ;hostname } else { echo "/" }
#remediate the malice with this
reg add "HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest" /v UseLogonCredential /t REG_DWORD /d 0
```

### Remove a reg entry
If there's a malicious reg entry, you can remove it this way
```powershell
#Create HKU drive
mount -PSProvider Registry -Name HKU -Root HKEY_USERS
# Read the reg to make sure this is the bad boy you want
get-itemproperty -Path 'HKU:\*\Keyboard Layout\Preload\'
#remove it by piping it to remove-item
get-itemproperty -Path 'HKU:\*\Keyboard Layout\Preload\' | Remove-Item -Force -Confirm:$false -verbose
# double check it's gone by trying to re-read it
get-itemproperty -Path 'HKU:\*\Keyboard Layout\Preload\'
```

#### Removing HKCurrentUser Keys
If a Registry is under `HKCU`, it's not clear exactly WHO it can belong to.

If a Registry is under `HKCU`, you can figure out WHICH username it belongs to but you can't just go into HKCU in your PwSh to delete it....because YOU are the current user.
Instead, get the [SID of the user](https://www.windows-commandline.com/get-sid-of-user/)
And then you can traverse to that as the path as HKU. So for example, under User_Alfonso's reg keys
```powershell
#this
HKCU:\Software\AppDataLow\Software\Microsoft\FDBC3F8C-385A-37D8-2A81-EC5BFE45E0BF
#must become this. Notice the reg changes in the field field, and the SID gets sandwiched in
HKU:\S-1-5-21-912369493-653634481-1866108234-1004\Software\AppDataLow\Software\Microsoft\FDBC3F8C-385A-37D8-2A81-EC5BFE45E0BF
```
To just generally convert them
```powershell
mount -PSProvider Registry -Name HKU -Root HKEY_USERS
```

### Understanding Reg Permissions
Reg permissions, and ACL and SDDL in general really, are a bit long to understand. But worth it, as adversaries like using the reg.
Adversaries will look for registries with loose permissions, so let's show how we first can identify loose permissions
#### Get-ACl
The Access Control List (ACL) considers the permissions associated with an object on a Windows machine. It's how the machine understands privileges, and who is allowed to do what.
Problem is, if you get and `get-acl` for a particular object, it ain't a pretty thing
```powershell
Get-Acl -Path hklm:\System\CurrentControlSet\services\ | fl
```
There's a lot going on here. Moreover, what the fuck is that SDDL string at the bottom?
The Security Descriptor Definition Language (SDDL) is a representation for ACL permissions, essentially

#### Convert SDDL
You could figure out what the wacky ASCII chunks mean in SDDL....but I'd much rather convert the permissions to something human readable
Here, an adversary is looking for a user they control to have permissions to maniptulate the service, likely they want *Full Control*
```powershell
$acl = Get-Acl -Path hklm:\System\CurrentControlSet\services\;
ConvertFrom-SddlString -Sddl $acl.Sddl | Foreach-Object {$_.DiscretionaryAcl[0]};
ConvertFrom-SddlString -Sddl $acl.Sddl -Type RegistryRights | Foreach-Object {$_.DiscretionaryAcl[0]}
# bottom one specifices the registry access rights when you create RegistrySecurity objects
```

#### What could they do with poor permissions?
An adversary in control of a loosely permissioned registry entry for a service, for example, could give themselves a privesc or persistence. For example:
```powershell
#don't actually run this
Set-ItemProperty -path HKLM:\System\CurrentControlSet\services\example_service -name ImagePath -value "C:\temp\evil.exe"
```
### Hunting for Reg evil
Now we know how reg entries are compromised, how can we search?
The below takes the services reg as an example, and searches for specifically just the reg-key Name and Image Path.
```powershell
Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\services\*" |
ft PSChildName, ImagePath -autosize | out-string -width 800
#You can search recursively with this, kind of, if you use wildcards in the path names. Will take longer if you do recursively search though
Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\**\*" |
ft PSChildName, ImagePath -autosize | out-string -width 800
# This one-liner is over-engineered. # But it's a other way to be recursive if you start from a higher directory in reg
# will take a while though
$keys = Get-ChildItem -Path "HKLM:\System\CurrentControlSet\" -recurse -force ;
$Items = $Keys | Foreach-Object {Get-ItemProperty $_.PsPath };
ForEach ($Item in $Items) {"{0,-35} {1,-10} " -f $Item.PSChildName, $Item.ImagePath}
```

#### Filtering Reg ImagePath
Let's continue to use the \Services\ reg as our example.
Remember in the above example of a malicious reg, we saw the ImagePath had the value of C:\temp\evil.exe. And we're seeing a load of .sys here. So can we specifically just filter for .exes in the ImagePath.
I have to mention, don't write .sys files off as harmless. Rootkits and bootkits weaponise .sys, for example.
If you see a suspicious file in reg, you can go and collect it and investigate it, or collect it's hash. When it comes to the ImagePath, \SystemRoot\ is usually C:\Windows\, but you can confirm with `$Env:systemroot` .
```powershell
Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\services\*" |
where ImagePath -like "*.exe*" |
ft PSChildName, ImagePath -autosize | out-string -width 800
# if you notice, on line two we wrap .exe in TWO in wildcards. Why?
# The first wildcard is to ensure we're kind of 'grepping' for a file that ends in a .exe.
# Without the first wildcard, we'd be looking for literal .exe
# The second wildcard is to ensure we're looking for the things that come after the .exe
# This is to make sure we aren't losing the flags and args of an executable
# We can filter however we wish, so we can actively NOT look for .exes
Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\services\*" |
where ImagePath -notlike "*.exe*" |
ft PSChildName, ImagePath -autosize | out-string -width 800
#fuck it, double stack your filters to not look for an exe or a sys...not sure why, but go for it!
Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\services\*" |
? {($_.ImagePath -notlike "*.exe*" -and $_.Imagepath -notlike "*.sys*")} |
ft PSChildName, ImagePath -autosize | out-string -width 800
#If you don't care about Reg Entry name, and just want the ImagePath
(Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\services\*").ImagePath
```

### Query Background Activity Moderator
BAM only in certain Windows 10 machines. Provides full path of the executabled last execution time
```powershell
reg query "HKLM\SYSTEM\CurrentControlSet\Services\bam\state\UserSettings" /s
# or HKLM\SYSTEM\CurrentControlSet\Services\bam\UserSettings\
```
OR [BAMParser.ps1](https://github.com/mgreen27/Invoke-LiveResponse/blob/master/Content/Other/Get-BAMParser.ps1)


---
## Driver Queries
section contents
+ [Printer Drivers](#printer-drivers)
+ [System Drivers](#system-drivers)
- [Unsigned](#unsigned)
- [Signed](#Signed)
+ [Other Drivers](#other-drivers)
+ [Drivers by Registry](#drivers-by-registry)
+ [Drivers by Time](#drivers-by-time)
Drivers are an interesting one. It isn't everyday you'll see malware sliding a malicious driver in ; bootkits and rootkits have been known to weaponise drivers. But it's well worth it, because it's an excellent method for persistence if an adversary can pull it off without blue-screening a machine. You can read more about it [here](https://eclypsium.com/wp-content/uploads/2019/11/Mother-of-All-Drivers.pdf)
You can utilise [Winbindex](https://winbindex.m417z.com) to investigate drivers, and compare a local copy you have with the indexed info. Malicious copies may have a hash that doesn't match, or a file size that doesn't quite match.

### Printer Drivers
```powershell
Get-PrinterDriver | fl Name, *path*, *file*
```

### System Drivers
If drivers are or aren't signed, don't use that as the differentiation for what is legit and not legit. Some legitimate drivers are not signed ; some malicious drivers sneak a signature.
#### Unsigned
Get unsigned drivers. Likely to not return much
```powershell
gci C:\Windows\*\DriverStore\FileRepository\ -recurse -include *.inf|
Get-AuthenticodeSignature |
? Status -ne "Valid" | ft -autosize
gci -path C:\Windows\System32\drivers -include *.sys -recurse -ea SilentlyContinue |
Get-AuthenticodeSignature |
? Status -ne "Valid" | ft -autosize
```
#### Signed
Get the signed ones. Will return a lot.
```powershell
Get-WmiObject Win32_PnPSignedDriver |
fl DeviceName, FriendlyName, DriverProviderName, Manufacturer, InfName, IsSigned, DriverVersion
# alternatives
gci -path C:\Windows\System32\drivers -include *.sys -recurse -ea SilentlyContinue |
Get-AuthenticodeSignature |
? Status -eq "Valid" | ft -autosize
#or
gci C:\Windows\*\DriverStore\FileRepository\ -recurse -include *.inf|
Get-AuthenticodeSignature |
? Status -eq "Valid" | ft -autosize
```


### Other Drivers
Gets all 3rd party drivers
```powershell
Get-WindowsDriver -Online -All |
fl Driver, ProviderName, ClassName, ClassDescription, Date, OriginalFileName, DriverSignature
```

### Drivers by Registry
You can also leverage the Registry to look at drivers
```powershell
#if you know the driver, you can just give the full path and wildcard the end if you aren't sure of full spelling
get-itemproperty -path "HKLM:\System\CurrentControlSet\Services\DBUtil*"
#You'll likely not know the path though, so just filter for drivers that have \drivers\ in their ImagePath
get-itemproperty -path "HKLM:\System\CurrentControlSet\Services\*" |
? ImagePath -like "*drivers*" |
fl ImagePath, DisplayName
```
(
### Drivers by Time
Look for the drivers that exist via directory diving.. We can focus on .INF and .SYS files, and sort by the time last written.
```powershell
#change to LastWriteTimeUtc if you need to.
# first directory location
gci C:\Windows\*\DriverStore\FileRepository\ -recurse -include *.inf |
sort-object LastWriteTime -Descending |
ft FullName,LastWriteTime | out-string -width 850
# second driver location
gci -path C:\Windows\System32\drivers -include *.sys -recurse -ea SilentlyContinue |
sort-object LastWriteTime -Descending |
ft FullName,LastWriteTime | out-string -width 850
```

## DLL Queries
section contents
+ [DLLs used in Processes](#dlls-used-in-processes)
- [Investigate Process DLLs](#investigate-process-dlls)
+ [Investigate DLLs](#investigate-dlls)
- [Generically](#generically)
- [Invalid](#invalid)
- [Specifically](#specifically)
- [Verify](#verify)