Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

Building an Interactive Security Awareness Training Tool with Automated Tracking

I recently developed an interactive security awareness training system that transforms a static office image into a clickable security threat detection exercise. This post will walk you through creating your own version, from image mapping to automated file generation and user tracking through IIS logs.

Overview

The system presents users with an office scene where they must identify security threats while avoiding decoy items. Their interactions are tracked through IIS logs, providing detailed analytics on user performance.


The website then provides feedback when threats or decoys are found as you can see below, however the goal is to find only threats not decoys:


Requirements

  • A base image showing an office/workplace environment
  • Image editing software to create individual popup images
  • Access to image-map.net for creating clickable hotspots
  • IIS web server with Windows Authentication enabled
  • PowerShell for running the automation scripts

Step 1: Prepare Your Images

First, we need to create:

  • One main office overview image (e.g., security-overview.PNG)
  • Individual images for each security threat (e.g., dooropen.PNGtailgating.PNG)
  • Individual images for each decoy item (e.g., no-keypad.PNGno-monitor.PNG)
  • Status indicator images (green-tick.PNG and red-cross.PNG)

Important naming convention: Decoy images must start with "no-" prefix.

Step 2: Create the Image Map

  1. Navigate to http://www.image-map.net/
  2. Upload your main office image
  3. Draw rectangular or circular areas over each security threat and decoy
  4. Critical: Name each area exactly as your image filename (without .PNG extension)
    • For a threat: if the area is named dooropen, you need dooropen.PNG
    • For a decoy: if the area is named no-keypad, you need no-keypad.PNG
  5. Generate the HTML code
  6. Save the complete output as map.txt

Your map.txt should look similar to this:

<!-- Image Map Generated by http://www.image-map.net/ -->
<img src="security-overview.PNG" usemap="#image-map">

<map name="image-map">
    <area target="" alt="" title="" href="alarmoffline" coords="1012,197,969,153" shape="rect">
    <area target="" alt="" title="" href="tailgating" coords="284,449,461,590" shape="rect">
    <area target="" alt="" title="" href="no-keypad" coords="309,88,26" shape="circle">
    <!-- more areas... -->
</map>

Step 3: Run the Generator Script

I created a PowerShell script that reads map.txt and automatically generates both the HTML interface and IIS log parser. Save this as  infographics-generator.ps1:

# Infographic File Generator
# Reads map.txt and generates index.html and iislogparser.ps1

param(
    [string]$MapFile = "map.txt",
    [string]$OutputFolder = ".",
    [string]$BaseImageName = "security-overview.PNG"
)

Write-Host "=== SecSpot File Generator ===" -ForegroundColor Cyan
Write-Host "Reading from: $MapFile" -ForegroundColor Yellow
Write-Host "Output folder: $OutputFolder" -ForegroundColor Yellow
Write-Host ""

# Read and parse map.txt
if (-not (Test-Path $MapFile)) {
    Write-Host "Error: $MapFile not found!" -ForegroundColor Red
    exit
}

Write-Host "Parsing map file..." -ForegroundColor Green
$MapContent = Get-Content $MapFile -Raw

# Extract all area tags
$AreaPattern = '<area[^>]+>'
$Areas = [regex]::Matches($MapContent, $AreaPattern)

# Extract image source from img tag
$ImgPattern = '<img src="([^"]+)"'
$ImgMatch = [regex]::Match($MapContent, $ImgPattern)
$ImageSource = if ($ImgMatch.Success) { $ImgMatch.Groups[1].Value } else { $BaseImageName }

# Parse areas and categorize threats vs decoys
$Threats = @()
$Decoys = @()
$AllAreas = @()

foreach ($Area in $Areas) {
    $AreaTag = $Area.Value
    
    # Extract href value
    $HrefPattern = 'href="([^"]+)"'
    $HrefMatch = [regex]::Match($AreaTag, $HrefPattern)
    
    if ($HrefMatch.Success) {
        $HrefValue = $HrefMatch.Groups[1].Value
        
        # Extract coordinates
        $CoordsPattern = 'coords="([^"]+)"'
        $CoordsMatch = [regex]::Match($AreaTag, $CoordsPattern)
        $Coords = if ($CoordsMatch.Success) { $CoordsMatch.Groups[1].Value } else { "" }
        
        # Extract shape
        $ShapePattern = 'shape="([^"]+)"'
        $ShapeMatch = [regex]::Match($AreaTag, $ShapePattern)
        $Shape = if ($ShapeMatch.Success) { $ShapeMatch.Groups[1].Value } else { "rect" }
        
        # Create area object
        $AreaObj = @{
            Name = $HrefValue
            Coords = $Coords
            Shape = $Shape
        }
        
        $AllAreas += $AreaObj
        
        # Categorize as threat or decoy
        if ($HrefValue.StartsWith("no-")) {
            $Decoys += $HrefValue
        } else {
            $Threats += $HrefValue
        }
    }
}

Write-Host "Found $($Threats.Count) threats and $($Decoys.Count) decoys" -ForegroundColor Green

# Generate index.html
Write-Host "`nGenerating index.html..." -ForegroundColor Cyan

$IndexHtml = @'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Security Overview - Find the Hidden Risks</title>
<!-- Hotjar Tracking Code for SecSpot -->
<script>
    (function(h,o,t,j,a,r){
        h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
        h._hjSettings={hjid:6540139,hjsv:6};
        a=o.getElementsByTagName('head')[0];
        r=o.createElement('script');r.async=1;
        r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
        a.appendChild(r);
    })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
</script>          
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f0f0;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            padding: 20px;
        }
        
        .counters-container {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
        }
        
        .counter-display {
            color: white;
            padding: 15px 30px;
            border-radius: 10px;
            font-size: 24px;
            font-weight: bold;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
        }
        
        .threats-counter {
            background-color: #28a745;
        }
        
        .decoys-counter {
            background-color: #dc3545;
        }
        
        .counter-number {
            color: #ffd700;
            font-size: 28px;
        }
        
        .container {
            max-width: 100%;
            padding: 20px;
        }
        
        img {
            max-width: 100%;
            height: auto;
            display: block;
        }

        /* Popup Modal Styles */
        .popup-overlay {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.8);
            z-index: 1000;
            justify-content: center;
            align-items: center;
        }

        .popup-content {
            background-color: white;
            padding: 20px;
            border-radius: 15px;
            text-align: center;
            max-width: 500px;
            box-shadow: 0 4px 30px rgba(0, 0, 0, 0.5);
            position: relative;
        }

        .popup-card-image {
            max-width: 100%;
            height: auto;
            border-radius: 10px;
        }

        .tick-cross-icon {
            position: absolute;
            top: 10px;
            right: 10px;
            width: 60px;
            height: 60px;
        }

        .close-btn {
            margin-top: 20px;
            padding: 12px 40px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 16px;
            font-weight: bold;
        }

        .close-btn:hover {
            background-color: #0056b3;
        }

        .decoy-content {
            padding: 20px;
        }

        .decoy-icon {
            width: 80px;
            height: 80px;
            margin: 0 auto 20px;
        }

        .decoy-title {
            font-size: 28px;
            color: #dc3545;
            margin-bottom: 15px;
        }

        .decoy-text {
            font-size: 18px;
            color: #666;
            margin-bottom: 20px;
        }

        /* Click indicator on main image */
        .click-indicator {
            position: absolute;
            width: 40px;
            height: 40px;
            pointer-events: none;
            z-index: 100;
            animation: popIn 0.3s ease-out;
        }

        @keyframes popIn {
            0% {
                transform: scale(0);
            }
            50% {
                transform: scale(1.2);
            }
            100% {
                transform: scale(1);
            }
        }

        .image-wrapper {
            position: relative;
            display: inline-block;
        }
    </style>
</head>
<body>
    <div class="counters-container">
        <div class="counter-display threats-counter">
            Security Threats Found: <span class="counter-number" id="threat-counter">0</span>/___THREAT_COUNT___
        </div>
        <div class="counter-display decoys-counter">
            Decoys Found: <span class="counter-number" id="decoy-counter">0</span>/___DECOY_COUNT___
        </div>
    </div>
    
    <div class="container">
        <div class="image-wrapper" id="image-wrapper">
            <!-- Image Map Generated by http://www.image-map.net/ -->
            <img src="___IMAGE_SOURCE___" usemap="#image-map" alt="Office Security Overview" id="main-image">
        
            <map name="image-map">
___MAP_AREAS___
            </map>
        </div>
    </div>

    <!-- Popup Modal -->
    <div id="popup" class="popup-overlay">
        <div class="popup-content" id="popup-content">
            <!-- Content will be inserted here by JavaScript -->
        </div>
    </div>

    <script>
        // Track found threats and decoys
        let threatsFound = 0;
        let decoysFound = 0;
        const foundThreats = new Set();
        const foundDecoys = new Set();
        
        // Get all area elements
        const areas = document.querySelectorAll('area');
        
        areas.forEach(area => {
            area.addEventListener('click', function(e) {
                e.preventDefault(); // Prevent any default behavior
                
                const riskType = this.getAttribute('data-risk');
                const coords = this.getAttribute('coords');
                const shape = this.getAttribute('shape');
                
                // Calculate click position
                const clickPos = calculateClickPosition(coords, shape);
                
                // Check if this is a decoy (starts with "no-")
                if (riskType.startsWith('no-')) {
                    // Wrong answer - not a security risk
                    addClickIndicator(clickPos.x, clickPos.y, 'red-cross.PNG');
                    
                    // Create unique ID for this decoy based on coordinates
                    const decoyId = coords;
                    if (!foundDecoys.has(decoyId)) {
                        foundDecoys.add(decoyId);
                        decoysFound++;
                        updateDecoyCounter();
                    }
                    
                    showNotSecurePopup(riskType);
                } else {
                    // Correct answer - security risk found
                    addClickIndicator(clickPos.x, clickPos.y, 'green-tick.PNG');
                    
                    // Increment counter only if this threat hasn't been found yet
                    if (!foundThreats.has(riskType)) {
                        foundThreats.add(riskType);
                        threatsFound++;
                        updateThreatCounter();
                    }
                    
                    showSecurityRiskPopup(riskType);
                }
            });
        });

        function calculateClickPosition(coords, shape) {
            const coordArray = coords.split(',').map(Number);
            let x, y;
            
            if (shape === 'rect') {
                // Rectangle: x1,y1,x2,y2 - use center
                x = (coordArray[0] + coordArray[2]) / 2;
                y = (coordArray[1] + coordArray[3]) / 2;
            } else if (shape === 'circle') {
                // Circle: x,y,radius - use center
                x = coordArray[0];
                y = coordArray[1];
            }
            
            return { x, y };
        }

        function addClickIndicator(x, y, iconSrc) {
            const wrapper = document.getElementById('image-wrapper');
            const img = document.getElementById('main-image');
            const indicator = document.createElement('img');
            
            indicator.src = iconSrc;
            indicator.className = 'click-indicator';
            
            // Get the actual displayed size of the image
            const imgRect = img.getBoundingClientRect();
            const wrapperRect = wrapper.getBoundingClientRect();
            
            // Calculate scale factor between natural size and displayed size
            const scaleX = imgRect.width / img.naturalWidth;
            const scaleY = imgRect.height / img.naturalHeight;
            
            // Scale the coordinates
            const scaledX = x * scaleX;
            const scaledY = y * scaleY;
            
            indicator.style.left = (scaledX - 20) + 'px'; // Center the 40px icon
            indicator.style.top = (scaledY - 20) + 'px';
            
            wrapper.appendChild(indicator);
        }

        function updateThreatCounter() {
            const counter = document.getElementById('threat-counter');
            counter.textContent = threatsFound;
            
            // Celebrate when all threats found!
            if (threatsFound === ___THREAT_COUNT___) {
                setTimeout(() => {
                    alert('🎉 Congratulations! You found all ___THREAT_COUNT___ security threats!');
                }, 500);
            }
        }

        function updateDecoyCounter() {
            const counter = document.getElementById('decoy-counter');
            counter.textContent = decoysFound;
        }

        function showSecurityRiskPopup(riskType) {
            const popup = document.getElementById('popup');
            const content = document.getElementById('popup-content');
            
            const imageSrc = riskType + '.PNG';
            
            content.innerHTML = `
                <img class="tick-cross-icon" src="green-tick.PNG" alt="Correct">
                <img class="popup-card-image" src="${imageSrc}" alt="Security Risk">
                <button class="close-btn" onclick="closePopup()">Continue</button>
            `;
            
            popup.style.display = 'flex';
        }

        function showNotSecurePopup(decoyType) {
            const popup = document.getElementById('popup');
            const content = document.getElementById('popup-content');
            
            // For decoys, show both the red cross and the specific decoy image
            const decoyImageSrc = decoyType + '.PNG';
            
            content.innerHTML = `
                <img class="tick-cross-icon" src="red-cross.PNG" alt="Incorrect">
                <img class="popup-card-image" src="${decoyImageSrc}" alt="Not a Security Risk">
                <button class="close-btn" onclick="closePopup()">Continue Looking</button>
            `;
            
            popup.style.display = 'flex';
        }

        function closePopup() {
            const popup = document.getElementById('popup');
            popup.style.display = 'none';
        }

        // Close popup when clicking outside the content box
        document.getElementById('popup').addEventListener('click', function(e) {
            if (e.target === this) {
                closePopup();
            }
        });

        // Close popup with Escape key
        document.addEventListener('keydown', function(e) {
            if (e.key === 'Escape') {
                closePopup();
            }
        });
    </script>
</body>
</html>
'@

# Build area tags
$AreaTags = ""
foreach ($Area in $AllAreas) {
    $AreaTags += "                <area alt=`"`" title=`"`" data-risk=`"$($Area.Name)`" coords=`"$($Area.Coords)`" shape=`"$($Area.Shape)`" style=`"cursor: pointer;`">`n"
}

# Replace placeholders
$IndexHtml = $IndexHtml -replace '___THREAT_COUNT___', $Threats.Count
$IndexHtml = $IndexHtml -replace '___DECOY_COUNT___', $Decoys.Count
$IndexHtml = $IndexHtml -replace '___IMAGE_SOURCE___', $ImageSource
$IndexHtml = $IndexHtml -replace '___MAP_AREAS___', $AreaTags.TrimEnd()

# Save index.html
$IndexPath = Join-Path $OutputFolder "index.html"
$IndexHtml | Out-File -FilePath $IndexPath -Encoding UTF8
Write-Host "Created: $IndexPath" -ForegroundColor Green

# Generate iislogparser.ps1
Write-Host "`nGenerating iislogparser.ps1..." -ForegroundColor Cyan

# Convert arrays to PowerShell array format
$ThreatsArrayStr = ($Threats | ForEach-Object { "    `"$_`"" }) -join ",`n"
$DecoysArrayStr = ($Decoys | ForEach-Object { "    `"$_`"" }) -join ",`n"

$IISParserScript = @"
# SecSpot IIS Log Analyzer
# Analyzes last 7 days of IIS logs for SecSpot usage

param(
    [string]`$ServerName = "st1w1660",
    [string]`$LogPath = "\\st1w1660\c$\inetpub\logs\LogFiles\W3SVC1",
    [int]`$DaysToAnalyze = 7,
    [string]`$VirtualDirectory = "SecSpot2"  # Virtual directory parameter
)

# Calculate date threshold
`$DateThreshold = (Get-Date).AddDays(-`$DaysToAnalyze)

Write-Host "=== SecSpot Usage Analyzer ===" -ForegroundColor Cyan
Write-Host "Server: `$ServerName" -ForegroundColor Yellow
Write-Host "Virtual Directory: /`$VirtualDirectory" -ForegroundColor Yellow
Write-Host "Analyzing logs from: `$(`$DateThreshold.ToString('yyyy-MM-dd'))" -ForegroundColor Yellow
Write-Host ""

# Get log files from last 7 days
Write-Host "Retrieving log files..." -ForegroundColor Green
`$LogFiles = Get-ChildItem -Path `$LogPath -Filter "*.log" | 
    Where-Object { `$_.LastWriteTime -ge `$DateThreshold } |
    Sort-Object LastWriteTime

if (`$LogFiles.Count -eq 0) {
    Write-Host "No log files found in the specified date range!" -ForegroundColor Red
    exit
}

Write-Host "Found `$(`$LogFiles.Count) log file(s) to analyze``n" -ForegroundColor Green

# Initialize tracking variables
`$UserActivity = @{}
`$TotalRequests = 0

# Define threats and decoys based on the HTML structure
# Threats are the actual security risks
`$ThreatsList = @(
$ThreatsArrayStr
)

# Decoys start with "no-"
`$DecoysList = @(
$DecoysArrayStr
)

# Process each log file
foreach (`$LogFile in `$LogFiles) {
    Write-Host "Processing: `$(`$LogFile.Name)..." -ForegroundColor Gray
    
    `$Content = Get-Content -Path `$LogFile.FullName
    
    foreach (`$Line in `$Content) {
        # Skip comment lines
        if (`$Line -match "^#") { continue }
        
        # Check if line contains virtual directory reference (case-insensitive)
        if (`$Line -match `$VirtualDirectory) {
            `$TotalRequests++
            
            # Parse the log line (space-delimited)
            `$Fields = `$Line -split '\s+'
            
            # IIS W3C Extended format fields
            if (`$Fields.Count -ge 9) {
                `$Date = `$Fields[0]
                `$Time = `$Fields[1]
                `$Method = `$Fields[3]
                `$UriStem = `$Fields[4]
                `$Username = `$Fields[7]  # STWATER\username format
                `$ClientIP = `$Fields[8]
                `$StatusCode = if (`$Fields.Count -ge 12) { `$Fields[11] } else { "Unknown" }
                
                # Clean up username (remove domain prefix if present)
                if (`$Username -match "\\") {
                    `$Username = `$Username.Split('\')[-1]
                }
                
                # Skip if username is empty or is a dash
                if ([string]::IsNullOrWhiteSpace(`$Username) -or `$Username -eq "-") {
                    `$Username = "Anonymous"
                }
                
                # Initialize user tracking if needed
                if (-not `$UserActivity.ContainsKey(`$Username)) {
                    `$UserActivity[`$Username] = [PSCustomObject]@{
                        Username = `$Username
                        ClientIP = `$ClientIP
                        FirstAccess = "`$Date `$Time"
                        LastAccess = "`$Date `$Time"
                        TotalRequests = 0
                        PageViews = 0
                        ThreatsFound = 0
                        DecoysClicked = 0
                        Failed = "no"  # Defaults to "no"
                        ThreatsViewed = New-Object System.Collections.ArrayList
                        DecoysViewed = New-Object System.Collections.ArrayList
                        ImagesViewed = New-Object System.Collections.ArrayList
                        StatusImages = ""
                        ThreatsDetails = ""
                        DecoysDetails = ""
                    }
                }
                
                # Update user stats
                `$UserActivity[`$Username].TotalRequests++
                `$UserActivity[`$Username].LastAccess = "`$Date `$Time"
                
                # Track specific resource types
                if (`$UriStem -match "$ImageSource") {
                    `$UserActivity[`$Username].PageViews++
                }
                # Check for threat images
                elseif (`$UriStem -match "/(`$(`$ThreatsList -join '|'))\.PNG") {
                    `$ThreatType = `$Matches[1]
                    if (`$UserActivity[`$Username].ThreatsViewed -notcontains `$ThreatType) {
                        [void]`$UserActivity[`$Username].ThreatsViewed.Add(`$ThreatType)
                    }
                }
                # Check for decoy images
                elseif (`$UriStem -match "/(`$(`$DecoysList -join '|'))\.PNG") {
                    `$DecoyType = `$Matches[1]
                    if (`$UserActivity[`$Username].DecoysViewed -notcontains `$DecoyType) {
                        [void]`$UserActivity[`$Username].DecoysViewed.Add(`$DecoyType)
                    }
                }
                # Track status images
                elseif (`$UriStem -match "(green-tick|red-cross)\.PNG") {
                    `$ImageType = `$Matches[1]
                    if (`$UserActivity[`$Username].ImagesViewed -notcontains `$ImageType) {
                        [void]`$UserActivity[`$Username].ImagesViewed.Add(`$ImageType)
                    }
                }
            }
        }
    }
}

# Update calculated fields for each user
foreach (`$User in `$UserActivity.Values) {
    `$User.ThreatsFound = `$User.ThreatsViewed.Count
    `$User.DecoysClicked = `$User.DecoysViewed.Count
    `$User.StatusImages = `$User.ImagesViewed -join '; '
    `$User.ThreatsDetails = `$User.ThreatsViewed -join '; '
    `$User.DecoysDetails = `$User.DecoysViewed -join '; '
    
    # Set Failed to "yes" only if 2 or more decoys clicked
    if (`$User.DecoysViewed.Count -ge 2) {
        `$User.Failed = "yes"
    }
}

# Display Results
Write-Host "``n========================================" -ForegroundColor Cyan
Write-Host "          USAGE SUMMARY" -ForegroundColor Cyan
Write-Host "========================================``n" -ForegroundColor Cyan

Write-Host "Total `$VirtualDirectory Requests: `$TotalRequests" -ForegroundColor Yellow
Write-Host "Unique Users: `$(`$UserActivity.Count)" -ForegroundColor Yellow
Write-Host ""

if (`$UserActivity.Count -eq 0) {
    Write-Host "No `$VirtualDirectory usage found in the analyzed logs." -ForegroundColor Red
    exit
}

# Sort users by last access (most recent first)
`$SortedUsers = `$UserActivity.Values | Sort-Object LastAccess -Descending

Write-Host "========================================" -ForegroundColor Cyan
Write-Host "          USER ACTIVITY REPORT" -ForegroundColor Cyan
Write-Host "========================================``n" -ForegroundColor Cyan

foreach (`$User in `$SortedUsers) {
    Write-Host "User: `$(`$User.Username)" -ForegroundColor Green
    Write-Host "  Client IP:      `$(`$User.ClientIP)" -ForegroundColor White
    Write-Host "  First Access:   `$(`$User.FirstAccess)" -ForegroundColor White
    Write-Host "  Last Access:    `$(`$User.LastAccess)" -ForegroundColor White
    Write-Host "  Total Requests: `$(`$User.TotalRequests)" -ForegroundColor White
    Write-Host "  Page Views:     `$(`$User.PageViews)" -ForegroundColor White
    
    if (`$User.ImagesViewed.Count -gt 0) {
        Write-Host "  Status Images:  `$(`$User.StatusImages)" -ForegroundColor Cyan
    }
    
    # Show threats found
    if (`$User.ThreatsViewed.Count -gt 0) {
        Write-Host "  Threats Found:  `$(`$User.ThreatsFound)/$($Threats.Count)" -ForegroundColor Yellow
        Write-Host "    - `$(`$User.ThreatsDetails -replace '; ', "``n    - ")" -ForegroundColor Gray
    } else {
        Write-Host "  Threats Found:  0/$($Threats.Count)" -ForegroundColor Gray
    }
    
    # Show decoys clicked
    if (`$User.DecoysViewed.Count -gt 0) {
        Write-Host "  Decoys Clicked: `$(`$User.DecoysClicked)/$($Decoys.Count)" -ForegroundColor Red
        Write-Host "    - `$(`$User.DecoysDetails -replace '; ', "``n    - ")" -ForegroundColor Gray
        Write-Host "  Failed:         `$(`$User.Failed)" -ForegroundColor Red
    } else {
        Write-Host "  Decoys Clicked: 0/$($Decoys.Count)" -ForegroundColor Green
        Write-Host "  Failed:         `$(`$User.Failed)" -ForegroundColor Green
    }
    
    Write-Host ""
}

# Summary Statistics
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "          STATISTICS" -ForegroundColor Cyan
Write-Host "========================================``n" -ForegroundColor Cyan

`$UsersWhoCompleted = (`$SortedUsers | Where-Object { `$_.ThreatsFound -eq $($Threats.Count) }).Count
`$UsersWhoFailed = (`$SortedUsers | Where-Object { `$_.Failed -eq "yes" }).Count
`$UsersWhoPassed = (`$SortedUsers | Where-Object { `$_.Failed -eq "no" }).Count
`$TotalThreats = (`$SortedUsers | ForEach-Object { `$_.ThreatsFound } | Measure-Object -Sum).Sum
`$TotalDecoys = (`$SortedUsers | ForEach-Object { `$_.DecoysClicked } | Measure-Object -Sum).Sum
`$AverageThreatsFound = if (`$SortedUsers.Count -gt 0) { `$TotalThreats / `$SortedUsers.Count } else { 0 }
`$AverageDecoysClicked = if (`$SortedUsers.Count -gt 0) { `$TotalDecoys / `$SortedUsers.Count } else { 0 }

Write-Host "Users who found all $($Threats.Count) threats: `$UsersWhoCompleted" -ForegroundColor Green
Write-Host "Users who clicked decoys (failed): `$UsersWhoFailed" -ForegroundColor Red
Write-Host "Users who avoided all decoys (passed): `$UsersWhoPassed" -ForegroundColor Green
Write-Host "Average threats found per user: `$([math]::Round(`$AverageThreatsFound, 2))" -ForegroundColor Yellow
Write-Host "Average decoys clicked per user: `$([math]::Round(`$AverageDecoysClicked, 2))" -ForegroundColor Yellow
Write-Host ""

# Export to CSV option
`$ExportChoice = Read-Host "Would you like to export this data to CSV? (Y/N)"
if (`$ExportChoice -eq "Y" -or `$ExportChoice -eq "y") {
    `$ExportPath = "`${VirtualDirectory}_Usage_Report_`$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
    
    # Export using the PSCustomObject directly
    `$SortedUsers | Select-Object Username, ClientIP, FirstAccess, LastAccess, TotalRequests, PageViews, ThreatsFound, DecoysClicked, Failed, ThreatsDetails, DecoysDetails, StatusImages | 
        Export-Csv -Path `$ExportPath -NoTypeInformation
    
    Write-Host "``nReport exported to: `$ExportPath" -ForegroundColor Green
}

Write-Host "``n========================================" -ForegroundColor Cyan
Write-Host "Analysis Complete!" -ForegroundColor Cyan
Write-Host "========================================``n" -ForegroundColor Cyan
"@

# Save iislogparser.ps1
$ParserPath = Join-Path $OutputFolder "iislogparser.ps1"
$IISParserScript | Out-File -FilePath $ParserPath -Encoding UTF8
Write-Host "Created: $ParserPath" -ForegroundColor Green

Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "File Generation Complete!" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Generated files based on:" -ForegroundColor Yellow
Write-Host "  - $($Threats.Count) threats: $($Threats -join ', ')" -ForegroundColor Green
Write-Host "  - $($Decoys.Count) decoys: $($Decoys -join ', ')" -ForegroundColor Red

Run the generator:

.\infographics-generator.ps1

This creates two files:

1. index.html

The interactive web interface that:

  • Displays counters for threats and decoys found
  • Shows green ticks for correct identifications
  • Shows red crosses for incorrect clicks
  • Displays popup images for each clicked item
  • Tracks user progress

2. iislogparser.ps1

The log analysis script that generates detailed reports showing:

  • User activity (first/last access, total requests)
  • Threats found by each user
  • Decoys clicked by each user
  • Pass/Fail status (users fail if they click 2 or more decoys)
  • Summary statistics
  • CSV export option

Step 4: Configure IIS

  1. Create a new virtual directory (e.g., /SecuritySpot2)
  2. Critical: Disable Anonymous Authentication
  3. Enable Windows Authentication
  4. Copy all files to the virtual directory:
    • index.html
    • security-overview.PNG
    • All threat images (dooropen.PNG, etc.)
    • All decoy images (no-keypad.PNG, etc.)
    • green-tick.PNG and red-cross.PNG

Step 5: Analyze Results

After users complete the exercise, run the log parser:

.\iislogparser.ps1 -VirtualDirectory "SecuritySpot2" -DaysToAnalyze 7

Example output:

User: JSmith
  Client IP:      10.245.187.29
  First Access:   2025-11-26 09:15:32
  Last Access:    2025-11-26 09:18:45
  Total Requests: 22
  Page Views:     1
  Status Images:  green-tick; red-cross
  Threats Found:  8/8
    - dooropen
    - cctvoffline
    - alarmoffline
    - gateunlocked
    - sharingkeys
    - tailgating
    - managingvisitors
    - loitering
  Decoys Clicked: 1/5
    - no-monitor
  Failed:         no

Success Metrics

  • Pass: User identifies threats but clicks fewer than 2 decoys
  • Fail: User clicks 2 or more decoy items
  • Complete: User finds all security threats

This system provides an engaging way to test security awareness while automatically tracking detailed user performance through IIS logs.

Previous Post Next Post

نموذج الاتصال