Hey folks! 👋 I was tired of seeing our security team get alerts from our vuln scanner (we use Tenable) and then having to manually cross-reference with Zscaler logs to see if any of those detected vulnerabilities were actually being *exploited* or at least probed. So I built a little Python script to correlate the two data sources.
The idea is simple: if Zscaler blocked a request to a known vulnerable endpoint *from* an internal asset, that's a much higher priority ticket. Here's the basic flow:
1. **Ingest Zscaler logs** (we pull them via their API into a small Postgres DB, but a CSV export works too).
2. **Pull the latest vuln scan results** from Tenable's API for the same time window.
3. **Correlate** by internal IP address and (where possible) the vulnerable port/path.
Here's the core matching logic of the script:
```python
# This is a simplified snippet
for block in zscaler_blocks:
for vuln in vuln_scan_results:
if vuln['ip'] == block['internal_ip']:
# Check if the blocked URL/port aligns with the vulnerable service
if is_match(block['url'], vuln['cve_details']):
create_incident_ticket(
asset=vuln['asset_name'],
ip=vuln['ip'],
cve=vuln['cve_id'],
block_reason=block['reason'],
timestamp=block['time']
)
```
I then have it output a formatted Markdown report and also create a low-priority PagerDuty incident if it finds a high-confidence match. The whole thing runs as a daily cron job.
**What I've learned / pitfalls:**
* You need to normalize IP addresses (Zscaler logs can have NAT IPs, so you need your internal IP mapping).
* The timezone handling between the two systems was a headache initially.
* It's not perfect, but it cut down manual cross-checking from a few hours a week to about 10 minutes of report review.
Has anyone else tried something similar? I'm curious if there are better ways to key the matching, maybe using asset hostnames instead of IPs. I've attached a screenshot of the simple dashboard I built in Datadog to track these correlated events over time.
Dashboards or it didn't happen.