// Overview
Built to emulate enterprise environments, the ascendant.design SOC Homelab is a fully virtualized cybersecurity training ground for hands-on threat detection. Hosted on VirtualBox within a securely isolated network, the architecture consists of five virtual machines: a Windows Server 2022 Domain Controller, a Windows 10 Pro endpoint, a vulnerable Metasploitable 2 instance, and a Kali Linux attack node. Centralized monitoring is handled by a Wazuh SIEM, which actively detects simulated malicious behavior and correlates it directly with the MITRE ATT&CK framework.
// Overview
Built to emulate enterprise environments, the ascendant.design SOC Homelab is a fully virtualized cybersecurity training ground for hands-on threat detection. Hosted on VirtualBox within a securely isolated network, the architecture consists of five virtual machines: a Windows Server 2022 Domain Controller, a Windows 10 Pro endpoint, a vulnerable Metasploitable 2 instance, and a Kali Linux attack node. Centralized monitoring is handled by a Wazuh SIEM, which actively detects simulated malicious behavior and correlates it directly with the MITRE ATT&CK framework.
// SOC Live Dashboard
This live dashboard provides a real-time window into the automated telemetry captured across your virtualized cybersecurity infrastructure. It aggregates threat intelligence from active Wazuh SIEM endpoints—tracking total event volume, classifying alerts by risk severity level, and mapping incoming security events directly to the MITRE ATT&CK framework. Designed to showcase automated endpoint protection and continuous threat monitoring, the feed highlights active attack vectors and system telemetry in real time.
Architecture Overview: Decoupled Telemetry Pipeline
This dashboard relies on a decoupled, push-based telemetry pipeline to securely bridge an isolated SIEM with a public web environment. An autonomous Python script on the Wazuh server queries the local OpenSearch API for security metrics—such as MITRE ATT&CK mappings and alert severities—and pushes them outbound as a lightweight JSON payload. Utilizing a pre-shared key (PSK) and custom headers, this outbound-only transmission bypasses external firewalls and maintains strict network segmentation, completely hiding the internal SIEM from inbound internet traffic.
Upon receipt, a custom WordPress REST API endpoint validates the payload and caches it in memory using Transients. This stateless caching prevents database thrashing and guarantees instantaneous page loads. Custom shortcodes then dynamically render the cached intelligence into responsive dashboards, delivering real-time SOC visibility with zero direct infrastructure exposure.
Event Volume (Last 24 Hours – UTC)
Live SOC Telemetry 3,588 Events
Severities
- Level 31,369
- Level 51,012
- Level 71,005
- Level 4127
- Level 1031
Top MITRE Tactics
- Defense Evasion 482
- Privilege Escalation 272
- Persistence 242
Active Agents
- WIN-QK6A1G7ET00 1,477
- HomelabWindowsP 1,318
- Homelab-Windows 564
Top Security Events
- Name resolution for the name settings-win.data.microsoft.com timed out 367
- Software protection service scheduled successfully. 237
- Windows User Logoff 190
- Windows Logon Success 186
Security Framework & Auth Groupings
NIST Control Hits
- Control CM.1 1,363
- Control AU.14 524
- Control AU.6 314
Top Event Categories
- Windows 1,724
- Sca 1,362
- Windows_system 626
- Windows_security 548
Telemetry Sources
Event Channels
- EventChannel 1,724
- sca 1,363
- syscheck 282
- journald 60
- /var/log/audit/audit.log 50
SOC Live Dashboard
This live dashboard provides a real-time window into the automated telemetry captured across your virtualized cybersecurity infrastructure. It aggregates threat intelligence from active Wazuh SIEM endpoints—tracking total event volume, classifying alerts by risk severity level, and mapping incoming security events directly to the MITRE ATT&CK framework. Designed to showcase automated endpoint protection and continuous threat monitoring, the feed highlights active attack vectors and system telemetry in real time.
Architecture Overview: Decoupled Telemetry Pipeline
This dashboard relies on a decoupled, push-based telemetry pipeline to securely bridge an isolated SIEM with a public web environment. An autonomous Python script on the Wazuh server queries the local OpenSearch API for security metrics—such as MITRE ATT&CK mappings and alert severities—and pushes them outbound as a lightweight JSON payload. Utilizing a pre-shared key (PSK) and custom headers, this outbound-only transmission bypasses external firewalls and maintains strict network segmentation, completely hiding the internal SIEM from inbound internet traffic.
Upon receipt, a custom WordPress REST API endpoint validates the payload and caches it in memory using Transients. This stateless caching prevents database thrashing and guarantees instantaneous page loads. Custom shortcodes then dynamically render the cached intelligence into responsive dashboards, delivering real-time SOC visibility with zero direct infrastructure exposure.
Event Volume (Last 24 Hours – UTC)
Live SOC Telemetry 3,588 Events
Severities
- Level 31,369
- Level 51,012
- Level 71,005
- Level 4127
- Level 1031
Top MITRE Tactics
- Defense Evasion 482
- Privilege Escalation 272
- Persistence 242
Active Agents
- WIN-QK6A1G7ET00 1,477
- HomelabWindowsP 1,318
- Homelab-Windows 564
Top Security Events
- Name resolution for the name settings-win.data.microsoft.com timed out 367
- Software protection service scheduled successfully. 237
- Windows User Logoff 190
- Windows Logon Success 186
Security Framework & Auth Groupings
NIST Control Hits
- Control CM.1 1,363
- Control AU.14 524
- Control AU.6 314
Top Event Categories
- Windows 1,724
- Sca 1,362
- Windows_system 626
- Windows_security 548
Telemetry Sources
Event Channels
- EventChannel 1,724
- sca 1,363
- syscheck 282
- journald 60
- /var/log/audit/audit.log 50
// Tech Stack
Wazuh SIEM Manager
Kali Linux Virtual Machine
Windows 2022 Active Dir
Ubuntu Linux Instance
Windows 11 Pro Instance
Windows 11 Home Instance
// Network Overview
| VM | ROLE | OS | IP |
|---|---|---|---|
| DC1 | Domain Controller | Windows 2022 Server | 192.168.128.6 |
| Wazuh SIEM | SIEM | Ubuntu 22.04 | 192.168.128.8 |
| Client1 | Workstation | Windows 11 Pro | 192.168.128.7 |
| Client2 | Workstation | Windows 11 Home | 192.168.128.4 |
| Client3 | Workstation | Ubuntu 22.04 | 192.168.128.5 |
| Kali Linux Attacker | Attacker | Kali Linux | 192.168.128.9 |
// Wazuh SIEM PHP Construction Script
'POST',
'callback' => 'update_wazuh_telemetry',
'permission_callback' => '__return_true'
));
});
function update_wazuh_telemetry($request) {
$secret_token = 'INSERT_YOUR_SECRET_HERE';
if ($request->get_header('x-api-key') !== $secret_token) {
return new WP_Error('unauthorized', 'Invalid Token', array('status' => 401));
}
set_transient('wazuh_alert_stats_cache', $request->get_json_params(), 600);
return rest_ensure_response(array('status' => 'success'));
}
function get_wz_color($index) {
$colors = array('#ef4444', '#f97316', '#facc15', '#22c55e', '#009fe3');
return $colors[$index % count($colors)];
}
// --- 2. WIDGET 1: MASTER DASHBOARD ---
function display_wazuh_telemetry() {
$data = get_transient('wazuh_alert_stats_cache');
if (empty($data) || !isset($data['severities'])) return 'Awaiting Data...
';
$html = '';
$html .= 'Live SOC Telemetry ' . number_format($data['total']) . ' Events
';
$html .= '';
$html .= 'Severities
';
foreach ($data['severities'] as $sev) {
$lvl = intval($sev['level']);
$class = ($lvl >= 12) ? 'wz-critical' : (($lvl >= 8) ? 'wz-high' : (($lvl >= 4) ? 'wz-medium' : 'wz-low'));
$html .= '- Level ' . $lvl . '' . number_format($sev['count']) . '
';
}
$html .= '
';
$html .= '';
$html .= 'Top MITRE Tactics
';
$mitre_data = isset($data['mitre']) ? $data['mitre'] : [];
if (empty($mitre_data)) { $html .= '- No MITRE data 0
'; }
else { $i = 0; foreach ($mitre_data as $m) { $c = get_wz_color($i++); $html .= '- ' . esc_html(ucfirst($m['tactic'])) . ' ' . number_format($m['count']) . '
'; } }
$html .= '
';
$html .= 'Active Agents
';
$agent_data = isset($data['agents']) ? $data['agents'] : [];
if (empty($agent_data)) { $html .= '- No agent data 0
'; }
else { $i = 0; foreach ($agent_data as $a) { $c = get_wz_color($i++); $html .= '- ' . esc_html($a['name']) . ' ' . number_format($a['count']) . '
'; } }
$html .= '
';
$html .= 'Top Security Events
';
$event_data = isset($data['events']) ? $data['events'] : [];
if (empty($event_data)) { $html .= '- No events 0
'; }
else { $i = 0; foreach ($event_data as $e) { $c = get_wz_color($i++); $html .= '- ' . esc_html($e['desc']) . ' ' . number_format($e['count']) . '
'; } }
$html .= '
';
$html .= '';
return $html;
}
add_shortcode('wazuh_live_stats', 'display_wazuh_telemetry');
// --- 3. WIDGET 2: COMPLIANCE & AUTHENTICATION ---
function display_wazuh_compliance() {
$data = get_transient('wazuh_alert_stats_cache');
if (empty($data)) return 'Awaiting Data...
';
$html = 'Security Framework & Auth Groupings
';
$html .= '';
$html .= 'NIST Control Hits
';
$nist_data = isset($data['nist']) ? $data['nist'] : [];
if (empty($nist_data)) { $html .= '- No NIST data 0
'; }
else { $i = 0; foreach ($nist_data as $n) { $c = get_wz_color($i++); $html .= '- Control ' . esc_html($n['control']) . ' ' . number_format($n['count']) . '
'; } }
$html .= '
';
$html .= 'Top Event Categories
';
$group_data = isset($data['groups']) ? $data['groups'] : [];
if (empty($group_data)) { $html .= '- No category data 0
'; }
else { $i = 0; foreach ($group_data as $g) { $c = get_wz_color($i++); $html .= '- ' . esc_html(ucfirst($g['group'])) . ' ' . number_format($g['count']) . '
'; } }
$html .= '
';
return $html;
}
add_shortcode('wazuh_compliance_stats', 'display_wazuh_compliance');
// --- 4. WIDGET 3: TELEMETRY SOURCES ---
function display_wazuh_sources() {
$data = get_transient('wazuh_alert_stats_cache');
if (empty($data)) return 'Awaiting Data...
';
$html = 'Telemetry Sources
';
$html .= 'Event Channels
';
$loc_data = isset($data['locations']) ? $data['locations'] : [];
if (empty($loc_data)) { $html .= '- No source data 0
'; }
else { $i = 0; foreach ($loc_data as $l) { $c = get_wz_color($i++); $loc_name = (strlen($l['loc']) > 30) ? substr($l['loc'], 0, 30) . '...' : $l['loc']; $html .= '- ' . esc_html($loc_name) . ' ' . number_format($l['count']) . '
'; } }
$html .= '
';
return $html;
}
add_shortcode('wazuh_source_stats', 'display_wazuh_sources');
// --- 5. WIDGET 4: TIMELINE GRAPH ---
function display_wazuh_graph() {
$data = get_transient('wazuh_alert_stats_cache');
if (empty($data) || !isset($data['timeline'])) return 'Awaiting Graph Data...
';
$labels = [];
$counts = [];
foreach ((array)$data['timeline'] as $t) {
$labels[] = gmdate('g A', intval($t['time'] / 1000));
$counts[] = intval($t['count']);
}
$chart_id = 'wazuhChart_' . uniqid();
$html = '';
$html .= 'Event Volume (Last 24 Hours - UTC)
';
$html .= '';
$html .= '';
$html .= '';
static $chart_js_loaded = false;
if (!$chart_js_loaded) {
$html .= '';
$chart_js_loaded = true;
}
$html .= '';
return $html;
}
add_shortcode('wazuh_graph_stats', 'display_wazuh_graph');
// --- 6. WIDGET 5: INCIDENT RESPONSE DRILL-DOWN MOCKUP ---
function display_wazuh_investigation() {
$time = isset($_GET['time']) ? sanitize_text_field($_GET['time']) : 'Selected Hour';
$events = isset($_GET['events']) ? intval($_GET['events']) : 'Multiple';
$html = '';
$html .= 'SOC Investigation Drill-Down ' . esc_html($events) . ' Events at ' . esc_html($time) . '
';
$html .= 'Architecture Notice: For security and performance, this portfolio environment does not expose raw internal SIEM logs to the public internet. Below is a sanitized, representative sample of the telemetry captured during this time window.
';
$mock_logs = [
['time' => '14:02:11 UTC', 'agent' => 'WIN-QK6A1G7ET00', 'level' => 3, 'desc' => 'Windows Logon Success', 'mitre' => 'Initial Access'],
['time' => '14:15:43 UTC', 'agent' => 'Homelab-Windows', 'level' => 7, 'desc' => 'File added to the system', 'mitre' => 'Persistence'],
['time' => '14:22:09 UTC', 'agent' => 'HomelabWindowsP', 'level' => 8, 'desc' => 'Name resolution for the name settings-win.data... timed out', 'mitre' => 'Defense Evasion'],
['time' => '14:38:12 UTC', 'agent' => 'WIN-QK6A1G7ET00', 'level' => 12, 'desc' => 'High amount of POST requests in a small period of time', 'mitre' => 'Command and Control'],
['time' => '14:41:55 UTC', 'agent' => 'WIN-QK6A1G7ET00', 'level' => 5, 'desc' => 'Windows User Logoff', 'mitre' => 'None']
];
$html .= '';
$html .= '';
$html .= '';
$html .= 'Timestamp Agent Level Description MITRE Tactic ';
$html .= ' ';
foreach ($mock_logs as $log) {
$lvl = $log['level'];
$bg_color = ($lvl >= 12) ? '#ef4444' : (($lvl >= 8) ? '#f97316' : (($lvl >= 4) ? '#facc15' : '#009fe3'));
$text_color = ($lvl >= 12 || $lvl < 4 || $lvl >= 8) ? '#ffffff' : '#1a1a1a';
$html .= '';
$html .= '' . $log['time'] . ' ';
$html .= '' . $log['agent'] . ' ';
$html .= 'L' . $lvl . ' ';
$html .= '' . $log['desc'] . ' ';
$html .= '' . $log['mitre'] . ' ';
$html .= ' ';
}
$html .= '
';
$html .= '';
$html .= '← Back to Dashboard';
$html .= '';
return $html;
}
add_shortcode('wazuh_investigation_demo', 'display_wazuh_investigation');
// --- 7. UNIVERSAL CSS ---
add_action('wp_footer', function() {
echo '';
});
?>
// Wazuh SIEM Python Push Script
import urllib.request
import json
import ssl
import base64
# --- 1. CONFIGURATION ---
WAZUH_URL = "https://INSERT_YOUR_WAZUH_IP:9200/wazuh-alerts*/_search"
WAZUH_USER = "INSERT_WAZUH_USERNAME"
WAZUH_PASS = "INSERT_WAZUH_PASSWORD"
WP_URL = "https://INSERT_YOUR_DOMAIN.com/wp-json/soc/v1/telemetry"
WP_SECRET = "INSERT_YOUR_SECRET_HERE"
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
# --- 2. EXPANDED TIME-SERIES QUERY ---
query_body = json.dumps({
"size": 0,
"aggs": {
"top_severities": { "terms": { "field": "rule.level", "size": 5 } },
"top_agents": { "terms": { "field": "agent.name", "size": 3 } },
"top_mitre": { "terms": { "field": "rule.mitre.tactic", "size": 3 } },
"top_events": { "terms": { "field": "rule.description", "size": 4 } },
"top_nist": { "terms": { "field": "rule.nist_800_53", "size": 3 } },
"top_groups": { "terms": { "field": "rule.groups", "size": 4 } },
"top_locations": { "terms": { "field": "location", "size": 5 } },
"timeline": {
"filter": { "range": { "@timestamp": { "gte": "now-24h" } } },
"aggs": {
"hourly": {
"date_histogram": {
"field": "@timestamp",
"fixed_interval": "1h",
"min_doc_count": 0
}
}
}
}
}
}).encode('utf-8')
auth_string = f"{WAZUH_USER}:{WAZUH_PASS}"
base64_auth = base64.b64encode(auth_string.encode('utf-8')).decode('utf-8')
req_local = urllib.request.Request(WAZUH_URL, data=query_body, method="POST")
req_local.add_header("Authorization", f"Basic {base64_auth}")
req_local.add_header("Content-Type", "application/json")
try:
with urllib.request.urlopen(req_local, context=ctx) as response:
data = json.loads(response.read().decode())
except Exception as e:
print(f"Failed to query Wazuh: {e}")
exit(1)
# --- 3. FORMAT THE MULTI-PART PAYLOAD ---
aggs = data.get('aggregations', {})
timeline_buckets = aggs.get('timeline', {}).get('hourly', {}).get('buckets', [])
payload = {
"total": data.get('hits', {}).get('total', {}).get('value', 0),
"severities": [{"level": b['key'], "count": b['doc_count']} for b in aggs.get('top_severities', {}).get('buckets', [])],
"agents": [{"name": b['key'], "count": b['doc_count']} for b in aggs.get('top_agents', {}).get('buckets', [])],
"mitre": [{"tactic": b['key'], "count": b['doc_count']} for b in aggs.get('top_mitre', {}).get('buckets', [])],
"events": [{"desc": b['key'], "count": b['doc_count']} for b in aggs.get('top_events', {}).get('buckets', [])],
"nist": [{"control": b['key'], "count": b['doc_count']} for b in aggs.get('top_nist', {}).get('buckets', [])],
"groups": [{"group": b['key'], "count": b['doc_count']} for b in aggs.get('top_groups', {}).get('buckets', [])],
"locations": [{"loc": b['key'], "count": b['doc_count']} for b in aggs.get('top_locations', {}).get('buckets', [])],
"timeline": [{"time": b['key'], "count": b['doc_count']} for b in timeline_buckets[-24:]]
}
# --- 4. PUSH TO WORDPRESS ---
req_remote = urllib.request.Request(WP_URL, data=json.dumps(payload).encode('utf-8'), method="POST")
req_remote.add_header("x-api-key", WP_SECRET)
req_remote.add_header("Content-Type", "application/json")
req_remote.add_header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
try:
with urllib.request.urlopen(req_remote) as response:
print(f"Successfully pushed expanded data. Status: {response.status}")
except Exception as e:
print(f"Failed to push to WordPress: {e}")
// Wazuh Code Formatting
/* Force strict boundary boxing on all widget elements */
.wazuh-stats-widget, .wazuh-stats-widget * {
box-sizing: border-box !important;
}
/* Master Widget Container */
.wazuh-stats-widget {
background: #ffffff;
border-radius: 16px;
padding: 24px;
width: 100%;
max-width: 100%;
box-shadow: 0 4px 24px rgba(0,0,0,0.04);
font-family: "Inter", sans-serif;
color: #1a1a1a;
margin-bottom: 20px;
}
/* Header Styling & Pulsing Blue Dot */
.wazuh-stats-widget h4 {
margin: 0 0 20px 0;
font-size: 1.1rem;
font-weight: 800;
letter-spacing: 0.02em;
text-transform: uppercase;
color: #1a1a1a !important;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 2px solid #f3f4f6;
padding-bottom: 12px;
}
.wazuh-stats-widget h4::before {
content: "";
display: inline-block;
width: 10px;
height: 10px;
background-color: #009fe3;
border-radius: 50%;
margin-right: 10px;
box-shadow: 0 0 8px rgba(0, 159, 227, 0.6);
animation: wazuh-pulse 2s infinite;
}
.wazuh-total-badge {
font-size: 0.75rem;
background: #e5e7eb;
padding: 4px 10px;
border-radius: 20px;
color: #4b5563;
font-weight: 700;
margin-left: auto;
}
@keyframes wazuh-pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.wz-section-title {
margin: 0 0 10px 0;
font-size: 0.75rem;
color: #6b7280;
text-transform: uppercase;
font-weight: 700;
letter-spacing: 0.05em;
}
/* Master Flex Layouts */
.wz-master-layout {
display: flex;
gap: 20px;
align-items: flex-start;
max-width: 100%;
}
.wz-sidebar-severities {
width: 240px;
flex-shrink: 0;
background: #f4f5f7;
padding: 14px;
border-radius: 12px;
}
.wz-main-grid {
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
min-width: 0;
max-width: 100%;
}
.wz-sub-row {
display: flex;
gap: 16px;
width: 100%;
max-width: 100%;
}
.wz-card {
flex: 1;
background: #f4f5f7;
padding: 14px;
border-radius: 12px;
min-width: 0;
max-width: 100%;
}
.wz-full-card {
width: 100%;
max-width: 100%;
}
/* Severity Stack Styling */
.wazuh-stats-widget ul.wz-severities-stack {
display: flex !important;
flex-direction: column !important;
gap: 8px !important;
list-style: none !important;
padding: 0 !important;
margin: 0 !important;
}
.wz-severities-stack li {
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
padding: 10px 12px !important;
background: #ffffff !important;
border-radius: 8px !important;
font-size: 0.8rem !important;
font-weight: 600 !important;
border-left: 4px solid #e5e7eb !important;
color: #1a1a1a !important;
}
.wz-severities-stack li.wz-critical { border-left-color: #ef4444 !important; }
.wz-severities-stack li.wz-high { border-left-color: #f97316 !important; }
.wz-severities-stack li.wz-medium { border-left-color: #facc15 !important; }
.wz-severities-stack li.wz-low { border-left-color: #009fe3 !important; }
.wz-severities-stack .wz-count {
background-color: #1a1a1a;
color: #ffffff;
padding: 2px 8px;
border-radius: 6px;
font-weight: 700;
font-size: 0.85rem;
width: 45px;
text-align: center;
}
/* Text List Styling & Wrapping */
.wazuh-stats-widget ul.wz-text-list {
display: flex !important;
flex-direction: column !important;
gap: 0 !important;
list-style: none !important;
padding: 0 !important;
margin: 0 !important;
width: 100%;
}
.wz-text-list li {
display: flex !important;
flex-direction: row !important;
justify-content: space-between !important;
align-items: center !important;
font-size: 0.8rem !important;
font-weight: 500 !important;
padding: 10px 0 !important;
border-bottom: 2px solid #e5e7eb !important;
background: transparent !important;
color: #1a1a1a !important;
width: 100%;
}
.wz-text-list li:last-child {
border-bottom: none !important;
padding-bottom: 0 !important;
}
.wz-text-list li > span:first-child {
flex: 1;
padding-right: 12px;
text-align: left;
word-break: break-word;
overflow-wrap: break-word;
white-space: normal;
}
.wz-text-list li > span:last-child {
font-weight: 700 !important;
background: #ffffff !important;
padding: 4px 0 !important;
border-radius: 10px !important;
font-size: 0.75rem !important;
flex-shrink: 0 !important;
width: 60px !important;
text-align: center !important;
display: inline-block !important;
margin-left: auto !important;
}
/* Mobile Responsiveness */
@media (max-width: 768px) {
.wazuh-stats-widget { padding: 16px; }
.wz-master-layout { flex-direction: column; }
.wz-sidebar-severities { width: 100%; margin-bottom: 10px; }
.wz-main-grid { width: 100%; }
.wz-sub-row { flex-direction: column; gap: 16px; width: 100%; }
.wazuh-stats-widget h4 { flex-direction: column; align-items: flex-start; gap: 12px; }
.wazuh-total-badge { margin-left: 0; }
}