Skip to content
SecureKhan
Go back

Bash and PowerShell for Security Automation: A Practical Guide

Bash and PowerShell for Security Automation

TL;DR: Scripting is essential for security engineers. Bash handles Linux/Unix automation while PowerShell dominates Windows environments. Both are crucial for automating recon, log analysis, incident response, and repetitive security tasks.


Table of Contents

Open Table of Contents

Quick Reference

Bash Essentials

ConceptSyntaxExample
VariableVAR="value"TARGET="10.10.10.1"
Command substitution$(command)DATE=$(date +%Y%m%d)
Loopfor i in list; do; donefor port in 22 80 443; do
Conditionalif [[ condition ]]; thenif [[ -f file ]]; then
Read filewhile read line; dowhile read ip; do scan $ip; done < ips.txt
Pipecmd1 | cmd2cat log | grep error
Redirect> (overwrite) >> (append)echo "test" >> log.txt
Exit code$?if [[ $? -eq 0 ]]; then

PowerShell Essentials

ConceptSyntaxExample
Variable$var = "value"$target = "10.10.10.1"
Loopforeach ($i in $list)foreach ($port in 22,80,443)
Conditionalif ($condition) { }if (Test-Path $file) { }
Pipelinecmd1 | cmd2Get-Process | Where-Object {$_.CPU -gt 50}
Read fileGet-ContentGet-Content ips.txt | ForEach-Object { }
ExportExport-Csv, Out-File$results | Export-Csv report.csv
Error handlingtry { } catch { }try { code } catch { Write-Error $_ }

Why Scripting Matters in Security

Security Engineers Need Automation

Manual tasks that scripting eliminates:

Manual TaskTime (Manual)Time (Scripted)
Check 100 IPs for open ports2 hours5 minutes
Parse 1GB of logs for IOCs4 hours2 minutes
Collect system info from 50 hosts8 hours10 minutes
Generate security report1 hourInstant
Validate firewall rules30 minutesSeconds

When to Use Each Language

ScenarioBashPowerShell
Linux server administration✅ Primary⚠️ Available (pwsh)
Windows endpoint investigation❌ Limited (WSL)✅ Primary
Network reconnaissance✅ Excellent✅ Good
Log parsing (Linux)✅ Primary⚠️ Secondary
Active Directory queries❌ No✅ Primary
Cloud automation (AWS CLI)✅ Primary✅ Good
Cloud automation (Azure)⚠️ Limited✅ Primary
Quick one-liners✅ Excellent⚠️ Verbose
Complex data manipulation⚠️ Limited✅ Excellent

The Security Automation Mindset

Before writing a script, ask:
1. Will I do this task more than once?
2. Is it error-prone when done manually?
3. Does it need to scale to many targets?
4. Does it need to be auditable/reproducible?

If YES to any → Script it!

Bash Basics for Security

Script Structure

#!/bin/bash
# Description: Security scan automation script
# Author: Security Team
# Date: 2024-01-15

# Exit on error
set -e

# Variables
TARGET=""
OUTPUT_DIR="./results"
LOG_FILE="scan.log"

# Functions
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

usage() {
    echo "Usage: $0 -t <target> [-o <output_dir>]"
    exit 1
}

# Parse arguments
while getopts "t:o:h" opt; do
    case $opt in
        t) TARGET="$OPTARG" ;;
        o) OUTPUT_DIR="$OPTARG" ;;
        h) usage ;;
        *) usage ;;
    esac
done

# Validate input
if [[ -z "$TARGET" ]]; then
    log "ERROR: Target is required"
    usage
fi

# Main logic
log "Starting scan of $TARGET"
mkdir -p "$OUTPUT_DIR"

# Your security tasks here

log "Scan complete. Results in $OUTPUT_DIR"

Essential Bash for Security

Working with Files and Directories

# Check if file exists
if [[ -f "/etc/passwd" ]]; then
    echo "File exists"
fi

# Check if directory exists
if [[ -d "/var/log" ]]; then
    echo "Directory exists"
fi

# Find files modified in last 24 hours (incident response)
find /var/log -type f -mtime -1

# Find files by permission (SUID hunting)
find / -perm -4000 -type f 2>/dev/null

# Secure file handling
TEMP_FILE=$(mktemp)
trap "rm -f $TEMP_FILE" EXIT

String Manipulation

# Extract domain from URL
URL="https://evil.com/malware.exe"
DOMAIN=$(echo "$URL" | awk -F/ '{print $3}')

# Check if string contains substring
if [[ "$LOG_LINE" == *"failed"* ]]; then
    echo "Found failure"
fi

# Remove whitespace
CLEAN=$(echo "$DIRTY" | tr -d '[:space:]')

# Convert to lowercase
LOWER=$(echo "$INPUT" | tr '[:upper:]' '[:lower:]')

Arrays and Loops

# Define array
PORTS=(22 80 443 8080 8443)

# Loop through array
for port in "${PORTS[@]}"; do
    echo "Checking port $port"
done

# Read file into loop
while IFS= read -r ip; do
    echo "Processing $ip"
done < targets.txt

# Loop with counter
for i in {1..100}; do
    echo "Iteration $i"
done

Process and Network Commands

# Check if process is running
if pgrep -x "nginx" > /dev/null; then
    echo "Nginx is running"
fi

# Get listening ports
ss -tulnp | grep LISTEN

# Check network connections
netstat -an | grep ESTABLISHED

# DNS lookup
dig +short evil.com

# HTTP request
curl -s -o /dev/null -w "%{http_code}" https://target.com

Bash One-Liners for Security

# Find all IP addresses in a file
grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' access.log | sort -u

# Count failed SSH logins by IP
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn

# Extract URLs from file
grep -oP 'https?://[^\s"<>]+' file.txt | sort -u

# Check SSL certificate expiry
echo | openssl s_client -connect google.com:443 2>/dev/null | openssl x509 -noout -dates

# Find large files (data exfil detection)
find / -type f -size +100M 2>/dev/null

# List users with bash shell
grep "/bin/bash" /etc/passwd | cut -d: -f1

# Quick port check
timeout 1 bash -c "echo >/dev/tcp/10.10.10.1/22" 2>/dev/null && echo "Open" || echo "Closed"

PowerShell Basics for Security

Script Structure

<#
.SYNOPSIS
    Security investigation script
.DESCRIPTION
    Collects security-relevant information from Windows systems
.PARAMETER Target
    Target computer name or IP
.EXAMPLE
    .\Investigate.ps1 -Target SERVER01
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)]
    [string]$Target,

    [Parameter(Mandatory=$false)]
    [string]$OutputPath = ".\results"
)

# Strict mode for better error catching
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

# Functions
function Write-Log {
    param([string]$Message)
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    "$timestamp - $Message" | Tee-Object -FilePath "$OutputPath\investigation.log" -Append
}

function Test-AdminPrivilege {
    $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
    return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}

# Main
try {
    if (-not (Test-AdminPrivilege)) {
        throw "This script requires administrator privileges"
    }

    Write-Log "Starting investigation of $Target"

    # Create output directory
    if (-not (Test-Path $OutputPath)) {
        New-Item -ItemType Directory -Path $OutputPath | Out-Null
    }

    # Your security tasks here

    Write-Log "Investigation complete"
}
catch {
    Write-Log "ERROR: $_"
    throw
}

Essential PowerShell for Security

Working with Objects

# PowerShell returns objects, not text!
$processes = Get-Process

# Filter objects
$suspicious = $processes | Where-Object { $_.CPU -gt 50 -and $_.ProcessName -notlike "System*" }

# Select specific properties
$suspicious | Select-Object ProcessName, Id, CPU, Path

# Sort objects
$processes | Sort-Object CPU -Descending | Select-Object -First 10

# Group objects
Get-EventLog -LogName Security | Group-Object EventID | Sort-Object Count -Descending

Working with Event Logs

# Get failed logons (Event ID 4625)
Get-WinEvent -FilterHashtable @{
    LogName='Security'
    ID=4625
    StartTime=(Get-Date).AddDays(-1)
} | Select-Object TimeCreated, Message

# Get successful logons (Event ID 4624)
Get-WinEvent -FilterHashtable @{
    LogName='Security'
    ID=4624
} -MaxEvents 100

# Search event logs for keyword
Get-WinEvent -LogName Security | Where-Object { $_.Message -like "*password*" }

# Export to CSV for analysis
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4625} |
    Select-Object TimeCreated, @{N='IP';E={$_.Properties[19].Value}} |
    Export-Csv failed_logons.csv -NoTypeInformation

Active Directory Queries

# Import AD module
Import-Module ActiveDirectory

# Find all domain admins
Get-ADGroupMember -Identity "Domain Admins" | Select-Object Name, SamAccountName

# Find users who haven't logged in for 90 days
$cutoff = (Get-Date).AddDays(-90)
Get-ADUser -Filter {LastLogonDate -lt $cutoff} -Properties LastLogonDate |
    Select-Object Name, LastLogonDate

# Find computers with old OS
Get-ADComputer -Filter * -Properties OperatingSystem |
    Where-Object { $_.OperatingSystem -like "*2008*" -or $_.OperatingSystem -like "*XP*" }

# Find accounts with passwords that don't expire
Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires

# Find service accounts with SPNs (Kerberoastable)
Get-ADUser -Filter {ServicePrincipalName -like "*"} -Properties ServicePrincipalName

Network and System Information

# Get network connections
Get-NetTCPConnection | Where-Object State -eq 'Established' |
    Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, OwningProcess

# Get process with connections
Get-NetTCPConnection | ForEach-Object {
    $process = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
    [PSCustomObject]@{
        LocalPort = $_.LocalPort
        RemoteAddress = $_.RemoteAddress
        RemotePort = $_.RemotePort
        ProcessName = $process.ProcessName
        ProcessPath = $process.Path
    }
}

# Get installed software
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |
    Select-Object DisplayName, DisplayVersion, Publisher, InstallDate

# Get scheduled tasks
Get-ScheduledTask | Where-Object State -eq 'Ready' |
    Select-Object TaskName, TaskPath, @{N='Actions';E={$_.Actions.Execute}}

# Get services running as non-System accounts
Get-WmiObject Win32_Service | Where-Object {
    $_.StartName -ne 'LocalSystem' -and
    $_.StartName -ne 'NT AUTHORITY\LocalService' -and
    $_.StartName -ne 'NT AUTHORITY\NetworkService'
} | Select-Object Name, StartName, State

PowerShell One-Liners for Security

# Find files modified in last 24 hours
Get-ChildItem -Path C:\ -Recurse -File -ErrorAction SilentlyContinue |
    Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-1) }

# Check for unsigned executables
Get-ChildItem C:\Windows\System32\*.exe | ForEach-Object {
    $sig = Get-AuthenticodeSignature $_.FullName
    if ($sig.Status -ne 'Valid') { $_.FullName }
}

# Find processes with no parent (potential injection)
Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq 0 -and $_.ProcessId -ne 0 }

# Get hash of suspicious file
Get-FileHash -Algorithm SHA256 C:\suspicious.exe

# Check for persistence in Run keys
Get-ItemProperty -Path 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Run'
Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run'

# Find PowerShell execution in logs
Get-WinEvent -LogName 'Windows PowerShell' | Where-Object { $_.Message -like "*Invoke-*" }

Automating Reconnaissance and Scans

Bash: Network Reconnaissance Script

#!/bin/bash
# recon.sh - Automated reconnaissance script

TARGET=$1
OUTPUT_DIR="./recon_$TARGET"

if [[ -z "$TARGET" ]]; then
    echo "Usage: $0 <target>"
    exit 1
fi

mkdir -p "$OUTPUT_DIR"
echo "[*] Starting reconnaissance on $TARGET"

# DNS Enumeration
echo "[*] DNS Enumeration..."
{
    echo "=== DNS Records ==="
    dig +short A "$TARGET"
    dig +short MX "$TARGET"
    dig +short NS "$TARGET"
    dig +short TXT "$TARGET"
} > "$OUTPUT_DIR/dns.txt"

# Subdomain enumeration (basic)
echo "[*] Subdomain enumeration..."
for sub in www mail ftp vpn remote admin dev staging api; do
    result=$(dig +short "$sub.$TARGET" 2>/dev/null)
    if [[ -n "$result" ]]; then
        echo "$sub.$TARGET: $result"
    fi
done > "$OUTPUT_DIR/subdomains.txt"

# Port scan (top ports)
echo "[*] Port scanning..."
nmap -sV -sC -oA "$OUTPUT_DIR/nmap" "$TARGET"

# Web enumeration (if port 80/443 open)
if grep -q "80/open\|443/open" "$OUTPUT_DIR/nmap.nmap" 2>/dev/null; then
    echo "[*] Web enumeration..."

    # HTTP headers
    curl -sI "http://$TARGET" > "$OUTPUT_DIR/http_headers.txt" 2>/dev/null
    curl -sI "https://$TARGET" > "$OUTPUT_DIR/https_headers.txt" 2>/dev/null

    # Robots.txt
    curl -s "http://$TARGET/robots.txt" > "$OUTPUT_DIR/robots.txt" 2>/dev/null

    # Directory bruteforce (if gobuster available)
    if command -v gobuster &>/dev/null; then
        gobuster dir -u "http://$TARGET" -w /usr/share/wordlists/dirb/common.txt -o "$OUTPUT_DIR/dirs.txt" 2>/dev/null
    fi
fi

# SSL Certificate info
echo "[*] SSL Certificate check..."
echo | openssl s_client -connect "$TARGET:443" 2>/dev/null | openssl x509 -noout -text > "$OUTPUT_DIR/ssl_cert.txt" 2>/dev/null

echo "[+] Reconnaissance complete. Results in $OUTPUT_DIR"

PowerShell: Windows Host Investigation

# Investigate-Host.ps1 - Windows security investigation

param(
    [Parameter(Mandatory=$false)]
    [string]$ComputerName = $env:COMPUTERNAME,
    [string]$OutputPath = ".\investigation_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
)

New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null

Write-Host "[*] Investigating $ComputerName" -ForegroundColor Cyan

# System Information
Write-Host "[*] Collecting system information..."
Get-ComputerInfo | Out-File "$OutputPath\system_info.txt"

# User Information
Write-Host "[*] Collecting user information..."
Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordRequired |
    Export-Csv "$OutputPath\local_users.csv" -NoTypeInformation

Get-LocalGroupMember -Group "Administrators" |
    Export-Csv "$OutputPath\local_admins.csv" -NoTypeInformation

# Running Processes
Write-Host "[*] Collecting process information..."
Get-Process | Select-Object ProcessName, Id, Path, Company, StartTime |
    Export-Csv "$OutputPath\processes.csv" -NoTypeInformation

# Network Connections
Write-Host "[*] Collecting network connections..."
Get-NetTCPConnection | Where-Object State -eq 'Established' |
    Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, OwningProcess |
    Export-Csv "$OutputPath\connections.csv" -NoTypeInformation

# Scheduled Tasks
Write-Host "[*] Collecting scheduled tasks..."
Get-ScheduledTask | Where-Object State -eq 'Ready' |
    Select-Object TaskName, TaskPath, @{N='Actions';E={$_.Actions.Execute}} |
    Export-Csv "$OutputPath\scheduled_tasks.csv" -NoTypeInformation

# Services
Write-Host "[*] Collecting services..."
Get-Service | Select-Object Name, DisplayName, Status, StartType |
    Export-Csv "$OutputPath\services.csv" -NoTypeInformation

# Startup Programs
Write-Host "[*] Collecting startup programs..."
$startup = @()
$startup += Get-ItemProperty -Path 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Run' -ErrorAction SilentlyContinue
$startup += Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -ErrorAction SilentlyContinue
$startup | Out-File "$OutputPath\startup_programs.txt"

# Recent Security Events
Write-Host "[*] Collecting security events..."
Get-WinEvent -FilterHashtable @{LogName='Security'; StartTime=(Get-Date).AddDays(-1)} -MaxEvents 1000 -ErrorAction SilentlyContinue |
    Select-Object TimeCreated, Id, Message |
    Export-Csv "$OutputPath\security_events.csv" -NoTypeInformation

# PowerShell History
Write-Host "[*] Collecting PowerShell history..."
$historyPath = (Get-PSReadLineOption).HistorySavePath
if (Test-Path $historyPath) {
    Copy-Item $historyPath "$OutputPath\powershell_history.txt"
}

# File Hashes of Common Persistence Locations
Write-Host "[*] Collecting file hashes..."
$suspiciousPaths = @(
    "$env:TEMP",
    "$env:APPDATA",
    "C:\Windows\Temp"
)
foreach ($path in $suspiciousPaths) {
    Get-ChildItem -Path $path -File -ErrorAction SilentlyContinue |
        ForEach-Object { Get-FileHash $_.FullName -ErrorAction SilentlyContinue } |
        Export-Csv "$OutputPath\file_hashes_$(Split-Path $path -Leaf).csv" -Append -NoTypeInformation
}

Write-Host "[+] Investigation complete. Results in $OutputPath" -ForegroundColor Green

Bash: Mass Port Scanner

#!/bin/bash
# mass_scan.sh - Scan multiple targets from file

TARGETS_FILE=$1
PORTS="22,80,443,3389,445,3306,5432"

if [[ ! -f "$TARGETS_FILE" ]]; then
    echo "Usage: $0 <targets_file>"
    exit 1
fi

OUTPUT_DIR="./mass_scan_$(date +%Y%m%d)"
mkdir -p "$OUTPUT_DIR"

# Parallel scanning with GNU parallel (if available)
if command -v parallel &>/dev/null; then
    cat "$TARGETS_FILE" | parallel -j 10 "nmap -Pn -p $PORTS {} -oG $OUTPUT_DIR/{}.gnmap"
else
    # Sequential fallback
    while read -r target; do
        echo "[*] Scanning $target"
        nmap -Pn -p "$PORTS" "$target" -oG "$OUTPUT_DIR/${target//\//_}.gnmap"
    done < "$TARGETS_FILE"
fi

# Summary report
echo "=== Open Ports Summary ===" > "$OUTPUT_DIR/summary.txt"
grep "open" "$OUTPUT_DIR"/*.gnmap | while read -r line; do
    echo "$line" >> "$OUTPUT_DIR/summary.txt"
done

echo "[+] Scan complete. Summary in $OUTPUT_DIR/summary.txt"

Log Parsing Examples

Bash: Parse Authentication Logs

#!/bin/bash
# parse_auth_logs.sh - Analyze Linux authentication logs

LOG_FILE="${1:-/var/log/auth.log}"

echo "=== Authentication Log Analysis ==="
echo "Log file: $LOG_FILE"
echo ""

# Failed SSH logins by IP
echo "--- Top 10 Failed SSH Login IPs ---"
grep "Failed password" "$LOG_FILE" |
    awk '{print $(NF-3)}' |
    sort | uniq -c | sort -rn | head -10

echo ""

# Successful logins
echo "--- Successful Logins ---"
grep "Accepted" "$LOG_FILE" |
    awk '{print $1, $2, $3, $9, $11}' | tail -20

echo ""

# Sudo commands
echo "--- Recent Sudo Commands ---"
grep "sudo:" "$LOG_FILE" |
    grep "COMMAND=" |
    awk -F'COMMAND=' '{print $2}' | tail -10

echo ""

# User additions/changes
echo "--- User Account Changes ---"
grep -E "(useradd|usermod|userdel|passwd)" "$LOG_FILE" | tail -10

echo ""

# Failed login timeline (hourly)
echo "--- Failed Logins by Hour (Last 24h) ---"
grep "Failed password" "$LOG_FILE" |
    awk '{print $3}' |
    cut -d: -f1 |
    sort | uniq -c

PowerShell: Parse Windows Event Logs

# Parse-SecurityLogs.ps1 - Analyze Windows Security Event Logs

param(
    [int]$Days = 7,
    [string]$OutputPath = ".\event_analysis"
)

New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
$startDate = (Get-Date).AddDays(-$Days)

Write-Host "=== Windows Security Log Analysis ===" -ForegroundColor Cyan
Write-Host "Analyzing last $Days days of events..."

# Failed Logons (4625)
Write-Host "`n--- Failed Logons (4625) ---"
$failedLogons = Get-WinEvent -FilterHashtable @{
    LogName='Security'
    ID=4625
    StartTime=$startDate
} -ErrorAction SilentlyContinue

$failedLogons | Group-Object { $_.Properties[5].Value } | # Group by username
    Sort-Object Count -Descending |
    Select-Object Count, @{N='Username';E={$_.Name}} -First 10 |
    Format-Table

$failedLogons | Export-Csv "$OutputPath\failed_logons.csv" -NoTypeInformation

# Successful Logons (4624)
Write-Host "--- Successful Logons (4624) ---"
$successLogons = Get-WinEvent -FilterHashtable @{
    LogName='Security'
    ID=4624
    StartTime=$startDate
} -MaxEvents 1000 -ErrorAction SilentlyContinue

# Filter for interactive and remote logons (types 2, 10)
$interactiveLogons = $successLogons | Where-Object {
    $_.Properties[8].Value -in @(2, 10)
}
$interactiveLogons | Select-Object TimeCreated,
    @{N='User';E={$_.Properties[5].Value}},
    @{N='LogonType';E={$_.Properties[8].Value}},
    @{N='SourceIP';E={$_.Properties[18].Value}} |
    Export-Csv "$OutputPath\successful_logons.csv" -NoTypeInformation

Write-Host "Interactive logons: $($interactiveLogons.Count)"

# Account Lockouts (4740)
Write-Host "`n--- Account Lockouts (4740) ---"
$lockouts = Get-WinEvent -FilterHashtable @{
    LogName='Security'
    ID=4740
    StartTime=$startDate
} -ErrorAction SilentlyContinue

if ($lockouts) {
    $lockouts | Select-Object TimeCreated,
        @{N='User';E={$_.Properties[0].Value}},
        @{N='Computer';E={$_.Properties[1].Value}} |
        Format-Table
}

# Privilege Escalation Events (4672, 4673)
Write-Host "--- Privilege Use Events ---"
$privEvents = Get-WinEvent -FilterHashtable @{
    LogName='Security'
    ID=4672
    StartTime=$startDate
} -MaxEvents 500 -ErrorAction SilentlyContinue

$privEvents | Group-Object { $_.Properties[1].Value } |
    Sort-Object Count -Descending |
    Select-Object Count, @{N='User';E={$_.Name}} -First 10 |
    Format-Table

# Process Creation (4688) - requires advanced auditing
Write-Host "--- Process Creation Events (4688) ---"
$processEvents = Get-WinEvent -FilterHashtable @{
    LogName='Security'
    ID=4688
    StartTime=$startDate
} -MaxEvents 500 -ErrorAction SilentlyContinue

if ($processEvents) {
    $processEvents | Select-Object TimeCreated,
        @{N='User';E={$_.Properties[1].Value}},
        @{N='Process';E={$_.Properties[5].Value}},
        @{N='CommandLine';E={$_.Properties[8].Value}} |
        Export-Csv "$OutputPath\process_creation.csv" -NoTypeInformation
    Write-Host "Process creation events: $($processEvents.Count)"
}

Write-Host "`n[+] Analysis complete. Reports in $OutputPath" -ForegroundColor Green

Bash: Parse Web Server Logs

#!/bin/bash
# parse_web_logs.sh - Analyze Apache/Nginx access logs

LOG_FILE="${1:-/var/log/apache2/access.log}"

echo "=== Web Server Log Analysis ==="

# Top IPs
echo "--- Top 10 Source IPs ---"
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10

# Top requested paths
echo -e "\n--- Top 10 Requested Paths ---"
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10

# HTTP status codes distribution
echo -e "\n--- HTTP Status Codes ---"
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn

# 404 errors (potential reconnaissance)
echo -e "\n--- Top 10 404 Requests ---"
awk '$9 == 404 {print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10

# Potential SQL injection attempts
echo -e "\n--- Potential SQL Injection Attempts ---"
grep -iE "(union|select|insert|update|delete|drop|'|--|;)" "$LOG_FILE" | head -10

# Potential XSS attempts
echo -e "\n--- Potential XSS Attempts ---"
grep -iE "(<script|javascript:|onerror|onload)" "$LOG_FILE" | head -10

# Large responses (potential data exfiltration)
echo -e "\n--- Largest Responses ---"
awk '{print $10, $7}' "$LOG_FILE" | sort -rn | head -10

# User agents
echo -e "\n--- Top User Agents ---"
awk -F'"' '{print $6}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10

Red Team vs Blue Team Use Cases

Red Team Scripts

Bash: Quick Host Enumeration

#!/bin/bash
# redteam_enum.sh - Post-exploitation enumeration

echo "=== System Information ==="
hostname
uname -a
cat /etc/os-release 2>/dev/null

echo -e "\n=== Current User ==="
id
whoami

echo -e "\n=== Interesting Files ==="
ls -la /home/
cat /etc/passwd
cat /etc/crontab

echo -e "\n=== Network ==="
ip a
ss -tulnp
cat /etc/hosts

echo -e "\n=== SUID Binaries ==="
find / -perm -4000 -type f 2>/dev/null

echo -e "\n=== Writable Directories ==="
find / -writable -type d 2>/dev/null | head -20

echo -e "\n=== Running Processes ==="
ps auxf

echo -e "\n=== Sudo Permissions ==="
sudo -l 2>/dev/null

PowerShell: Domain Enumeration

# redteam_ad_enum.ps1 - Active Directory enumeration

Write-Host "=== Domain Information ===" -ForegroundColor Red
$domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$domain

Write-Host "`n=== Current User Privileges ===" -ForegroundColor Red
whoami /all

Write-Host "`n=== Domain Admins ===" -ForegroundColor Red
net group "Domain Admins" /domain

Write-Host "`n=== Domain Controllers ===" -ForegroundColor Red
nltest /dclist:$($domain.Name)

Write-Host "`n=== Interesting Shares ===" -ForegroundColor Red
net view /domain

Write-Host "`n=== Kerberoastable Accounts ===" -ForegroundColor Red
# Requires AD module or direct LDAP
Get-ADUser -Filter {ServicePrincipalName -like "*"} -Properties ServicePrincipalName 2>$null

Write-Host "`n=== Computers in Domain ===" -ForegroundColor Red
Get-ADComputer -Filter * | Select-Object Name, DNSHostName 2>$null

Blue Team Scripts

Bash: Threat Hunting Script

#!/bin/bash
# blueteam_hunt.sh - Linux threat hunting

OUTPUT_DIR="./hunt_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"

echo "[*] Starting threat hunt..."

# Check for unexpected users
echo "[*] Checking users..."
awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' /etc/passwd > "$OUTPUT_DIR/users.txt"
awk -F: '$3 == 0 {print}' /etc/passwd > "$OUTPUT_DIR/uid0_users.txt"

# Check for unusual processes
echo "[*] Checking processes..."
ps auxf > "$OUTPUT_DIR/processes.txt"
# Processes running from /tmp or /dev/shm
ps aux | awk '$11 ~ /^\/tmp|^\/dev\/shm/ {print}' > "$OUTPUT_DIR/suspicious_processes.txt"

# Check network connections
echo "[*] Checking network..."
ss -tulnp > "$OUTPUT_DIR/listening_ports.txt"
ss -tnp | grep ESTABLISHED > "$OUTPUT_DIR/established_connections.txt"

# Check cron jobs
echo "[*] Checking cron..."
for user in $(cut -d: -f1 /etc/passwd); do
    crontab -l -u "$user" 2>/dev/null
done > "$OUTPUT_DIR/cron_jobs.txt"
cat /etc/cron.* > "$OUTPUT_DIR/system_cron.txt" 2>/dev/null

# Check for suspicious files
echo "[*] Checking suspicious files..."
find /tmp /dev/shm /var/tmp -type f -executable 2>/dev/null > "$OUTPUT_DIR/executable_temp.txt"
find / -name ".*" -type f 2>/dev/null | head -100 > "$OUTPUT_DIR/hidden_files.txt"

# Check SSH authorized keys
echo "[*] Checking SSH keys..."
find /home -name "authorized_keys" -exec cat {} \; 2>/dev/null > "$OUTPUT_DIR/authorized_keys.txt"
find /root -name "authorized_keys" -exec cat {} \; 2>/dev/null >> "$OUTPUT_DIR/authorized_keys.txt"

# Check for persistence
echo "[*] Checking persistence mechanisms..."
cat /etc/rc.local 2>/dev/null > "$OUTPUT_DIR/rc_local.txt"
systemctl list-unit-files --type=service | grep enabled > "$OUTPUT_DIR/enabled_services.txt"

echo "[+] Hunt complete. Results in $OUTPUT_DIR"

PowerShell: Incident Response Collector

# blueteam_ir.ps1 - Windows incident response collection

param(
    [string]$OutputPath = ".\IR_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
)

$ErrorActionPreference = "SilentlyContinue"
New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null

Write-Host "[*] Starting IR collection..." -ForegroundColor Blue

# System Info
Write-Host "[*] Collecting system info..."
Get-ComputerInfo | Out-File "$OutputPath\system_info.txt"
systeminfo | Out-File "$OutputPath\systeminfo.txt"

# Running Processes with full paths
Write-Host "[*] Collecting processes..."
Get-Process | Select-Object Name, Id, Path, StartTime, Company |
    Export-Csv "$OutputPath\processes.csv" -NoTypeInformation

# Process with command lines (WMI)
Get-WmiObject Win32_Process |
    Select-Object Name, ProcessId, ParentProcessId, CommandLine, ExecutablePath |
    Export-Csv "$OutputPath\processes_wmi.csv" -NoTypeInformation

# Network Connections with process info
Write-Host "[*] Collecting network connections..."
Get-NetTCPConnection | ForEach-Object {
    $proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
    [PSCustomObject]@{
        LocalAddress = $_.LocalAddress
        LocalPort = $_.LocalPort
        RemoteAddress = $_.RemoteAddress
        RemotePort = $_.RemotePort
        State = $_.State
        ProcessName = $proc.ProcessName
        ProcessPath = $proc.Path
    }
} | Export-Csv "$OutputPath\connections.csv" -NoTypeInformation

# Persistence Mechanisms
Write-Host "[*] Collecting persistence mechanisms..."

# Registry Run 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'
)
$runKeys | ForEach-Object {
    Get-ItemProperty -Path $_ 2>$null
} | Out-File "$OutputPath\run_keys.txt"

# Scheduled Tasks
Get-ScheduledTask | Where-Object { $_.State -eq 'Ready' } |
    ForEach-Object {
        [PSCustomObject]@{
            Name = $_.TaskName
            Path = $_.TaskPath
            Action = ($_.Actions | ForEach-Object { $_.Execute + " " + $_.Arguments }) -join "; "
            Trigger = ($_.Triggers | ForEach-Object { $_.ToString() }) -join "; "
        }
    } | Export-Csv "$OutputPath\scheduled_tasks.csv" -NoTypeInformation

# Services
Get-WmiObject Win32_Service |
    Select-Object Name, DisplayName, State, StartMode, StartName, PathName |
    Export-Csv "$OutputPath\services.csv" -NoTypeInformation

# Recent Security Events
Write-Host "[*] Collecting security events..."
Get-WinEvent -LogName Security -MaxEvents 5000 |
    Select-Object TimeCreated, Id, LevelDisplayName, Message |
    Export-Csv "$OutputPath\security_events.csv" -NoTypeInformation

# PowerShell Event Logs
Get-WinEvent -LogName 'Windows PowerShell' -MaxEvents 1000 |
    Export-Csv "$OutputPath\powershell_events.csv" -NoTypeInformation

# DNS Cache
Get-DnsClientCache | Export-Csv "$OutputPath\dns_cache.csv" -NoTypeInformation

# ARP Cache
Get-NetNeighbor | Export-Csv "$OutputPath\arp_cache.csv" -NoTypeInformation

# Prefetch (evidence of execution)
Write-Host "[*] Collecting prefetch files..."
Get-ChildItem C:\Windows\Prefetch\*.pf -ErrorAction SilentlyContinue |
    Select-Object Name, LastWriteTime, Length |
    Export-Csv "$OutputPath\prefetch.csv" -NoTypeInformation

# Browser History (Chrome)
$chromeHistory = "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\History"
if (Test-Path $chromeHistory) {
    Copy-Item $chromeHistory "$OutputPath\chrome_history.sqlite" -Force
}

Write-Host "[+] IR collection complete. Results in $OutputPath" -ForegroundColor Green

Hands-On Labs

Lab 1: Bash Reconnaissance Script

Objective: Create a script that enumerates a target network.

# Create a file: lab1_recon.sh
# Requirements:
# 1. Accept target IP/range as argument
# 2. Perform ping sweep
# 3. Port scan live hosts
# 4. Save results to timestamped directory

# Starter code:
#!/bin/bash
TARGET=${1:?"Usage: $0 <target>"}
OUTPUT_DIR="recon_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"

# TODO: Implement ping sweep
# TODO: Implement port scan
# TODO: Generate summary report

Solution approach:

  1. Use ping or nmap -sn for host discovery
  2. Use nmap with service detection
  3. Parse and format results

Lab 2: PowerShell Event Log Parser

Objective: Create a script that finds suspicious Windows events.

# Create a file: Lab2-EventParser.ps1
# Requirements:
# 1. Find failed logons in last 24 hours
# 2. Group by source IP
# 3. Alert if any IP has > 10 failures
# 4. Export results

# Starter code:
param([int]$Threshold = 10)

$failedLogons = Get-WinEvent -FilterHashtable @{
    LogName = 'Security'
    ID = 4625
    StartTime = (Get-Date).AddDays(-1)
}

# TODO: Extract source IPs
# TODO: Count per IP
# TODO: Alert on threshold
# TODO: Export report

Interview Questions & Answers

Basic Questions

Q1: Why is scripting important for security engineers?

Strong Answer: “Scripting is essential because security work involves repetitive tasks at scale. You might need to:

  • Scan hundreds of hosts for vulnerabilities
  • Parse gigabytes of logs for indicators of compromise
  • Collect forensic artifacts from multiple systems
  • Automate compliance checks across infrastructure

Manual execution is slow, error-prone, and doesn’t scale. A 4-hour manual task might take 5 minutes scripted. Additionally, scripts provide reproducibility and documentation of exactly what was done.”

Q2: When would you choose Bash over PowerShell?

Strong Answer: “Bash is my choice for:

  • Linux/Unix server administration and security
  • Quick one-liners and text processing with pipes
  • Network reconnaissance using native tools
  • Log parsing on Linux systems

PowerShell is better for:

  • Windows endpoint investigation
  • Active Directory enumeration and auditing
  • Working with structured objects (not just text)
  • Azure cloud automation

For cross-platform needs, Python might be the best choice.”

Intermediate Questions

Q3: How would you write a script to detect brute-force attacks?

Strong Answer: “I’d approach it differently for each OS:

Linux (Bash):

grep 'Failed password' /var/log/auth.log |
  awk '{print $(NF-3)}' |
  sort | uniq -c | sort -rn |
  awk '$1 > 10 {print "ALERT:", $2, $1, "attempts"}'

Windows (PowerShell):

Get-WinEvent -FilterHashtable @{LogName='Security';ID=4625} |
  Group-Object {$_.Properties[19].Value} |
  Where-Object Count -gt 10 |
  Select-Object Count, Name

The key elements are: identify the right log source, extract the attacker IP, count occurrences, and alert on threshold.”

Q4: How do you handle errors in your security scripts?

Strong Answer: “Error handling is critical in security scripts because silent failures could mean missed threats.

Bash:

  • Use set -e to exit on error
  • Use set -o pipefail to catch errors in pipelines
  • Check $? after critical commands
  • Use trap for cleanup on exit

PowerShell:

  • Set $ErrorActionPreference = 'Stop' for critical scripts
  • Use try/catch/finally blocks
  • Log errors with timestamps
  • Validate inputs with parameter validation

I also ensure scripts fail loudly rather than silently continuing with incomplete data.”

Advanced Questions

Q5: Describe a complex automation task you’ve built.

Strong Answer: “I built an automated threat hunting framework that:

  1. Collected - Gathered process lists, network connections, and scheduled tasks from all domain workstations via PowerShell remoting
  2. Normalized - Standardized data into consistent CSV format
  3. Analyzed - Compared against known-good baselines and threat intel IOCs
  4. Alerted - Generated alerts for anomalies like unsigned executables, unusual network connections, or persistence mechanisms
  5. Reported - Created daily summary reports for the SOC

The challenge was handling thousands of endpoints efficiently. I used PowerShell jobs for parallelization and implemented error handling for offline or unreachable systems.”

Q6: How would you ensure your security scripts don’t become attack tools?

Strong Answer: “This is an important consideration. I implement several controls:

  1. Least privilege - Scripts run with minimum necessary permissions
  2. Logging - All script executions are logged with timestamp, user, and target
  3. Input validation - Sanitize all inputs to prevent injection
  4. Code signing - Sign scripts and enforce execution policy
  5. Access control - Store scripts in protected locations with limited access
  6. No hardcoded credentials - Use secure vaults or integrated authentication
  7. Review process - Peer review before deployment

The goal is that even if the script is obtained by an attacker, it’s less useful without the authorized context.”


Glossary

TermDefinition
Shebang#!/bin/bash - Specifies script interpreter
PipelineConnecting commands with | to pass output
CmdletPowerShell’s native commands (Verb-Noun format)
PSObjectPowerShell’s structured data object
RegexRegular expressions for pattern matching
STDIN/STDOUTStandard input/output streams
Exit CodeNumeric return value (0 = success)
Here-StringMulti-line string (@" "@ in PS, <<EOF in Bash)

What’s Next

Continue building your automation skills with:

  1. Git for Security Engineers - Version control for scripts
  2. Linux Hardening - Apply what you automate
  3. Detection Engineering Basics - Automate threat detection

Questions or feedback? Open an issue on GitHub.


Share this post on:

Previous Post
Git and Version Control for Security Engineers: A Practical Guide
Next Post
Linux Hardening for Security Engineers: A Practical Guide