work / IntraSec
client-less network vulnerability scanner and asset management for Active Directory environments.
Built with Muhammad Ahmad Hanif
the problem
Enterprise networks don't match their own inventories. Active Directory has stale records: decommissioned machines, unregistered hosts, ghost assets. A scanner that trusts the inventory misses these gaps.
The constraint the project set itself was no agents. No agents means you can't just install software on each machine and ask it what's running. You have to figure it out from the outside: scanning, LDAP queries, remote sessions.
architecture
IntraSec is a Gateway API in front of five microservices. Each handles one thing. REST between them. Frontend is Next.js.
- Asset Discovery: Go service. Authenticates against LDAP, enumerates all AD-joined computers, opens PowerShell sessions on each to collect software inventory. Stores results in PostgreSQL.
- Risk Assessment: Python + FastAPI. Matches the software inventory against CVE/CPE data, runs the risk scoring algorithm, produces per-host and network-wide risk metrics.
- Vulnerability DB: Python service that ingests CVE and CPE data from NIST NVD into Elasticsearch. Keeps the local vulnerability index in sync with upstream feeds.
- Report Generation: Produces structured reports from risk assessment output, exportable for audits.
- Alerts: Real-time notifications for high-severity findings, with configurable thresholds.
Redis for caching. PostgreSQL for assets, patches, reports. Elasticsearch for CVE/CPE because you need to search millions of records fast.
deployment
how discovery works
The Go service authenticates to LDAP, queries for all computer objects, then opens a PowerShell remote session on each to collect software, services, and open ports. Results go into PostgreSQL and downstream to risk assessment.
If a host returns no data, it's not that nothing is installed: it means the host is offline, unreachable, has remoting disabled, or the credentials don't have access. That's flagged separately.
Go for this because handling hundreds of concurrent LDAP queries and PowerShell sessions needs proper concurrency primitives. Python for the risk scoring because the math libraries are there.
risk scoring
Risk scoring is based on the Reyes et al. [2022] model. For each host, the algorithm calculates a probability of occurrence (PO) factoring in:
- POE: probability of exploit, weighted by how many CVEs affect this host vs. the network total
- POP: probability of port exploitation, proportional to open ports
- AVT: network-wide age-weighted vulnerability time, where older unpatched CVEs score higher
- CVSS impact score: the severity multiplier from NIST
Final risk factor: risk = PO × avg_impact, where PO = ((avg_TR + avg_EP) / 2) × ((POE + POP) / 2) × AVT. Hosts are then bucketed into LOW / MEDIUM-LOW / MEDIUM / MEDIUM-HIGH / HIGH.
external intelligence
The vulnerability database is fed from four APIs to get a complete picture of each CVE:
- NIST NVD: authoritative CVE and CPE data, CVSS scores
- FIRST.org EPSS: exploit prediction scores, the probability a CVE gets exploited in the wild
- CISA KEV: known exploited vulnerabilities catalog, actively exploited in the wild
- VulnCheck: enriched CVE intelligence including ransomware associations
data model
the interface
what i'd do differently
Scan operations are currently request-response via REST. A message queue (RabbitMQ, Kafka) would let scans run async without polling for status. Right now we're building status tracking on top of HTTP.
PowerShell remoting is the first choice but fragile. A fallback chain would help: WMI if remoting fails, SNMP if that fails, passive fingerprinting as last resort. We'd catch more hosts at the cost of more code paths.
Logging came late. Should have added distributed tracing from the start: hard to debug why a scan hit a 10-minute timeout without traces across service boundaries.
links