Vulnerability Assessment and Management Questions
Finding, prioritizing, and remediating vulnerabilities across systems. Covers vulnerability assessment methodologies, scanning and automation, interpreting and validating scan results, vulnerability classification and scoring (CVSS), prioritization based on exploitability and business impact, and driving remediation to closure. The operational vulnerability-lifecycle discipline, distinct from adversarial penetration testing.
Name several vulnerability scanning tools across categories (network, web app, container, software composition analysis). For each, what's it best suited for?
Sample Answer
Direct answer
Vulnerability scanning tools split into distinct categories by what they scan, and each category has real, distinct tools worth naming: network and host scanners for infrastructure, dynamic application security testing (DAST) tools for running web applications, container scanners for image layers, and software composition analysis (SCA) tools for open-source and third-party library dependencies. Knowing which category a given tool belongs to, and what it's genuinely good at, matters more than memorizing brand names, since using the wrong category of tool for the job (a network scanner against application logic, for example) simply won't find what you're looking for.
Structured elaboration
| Category | Example tools | Best suited for |
|---|---|---|
| Network and host scanning | Tenable Nessus, Qualys VMDR, Rapid7 InsightVM, the open-source OpenVAS/Greenbone | Broad infrastructure scanning across on-prem servers, network devices, and cloud hosts: missing patches, open ports, and configuration weaknesses |
| Web application (dynamic application security testing, or DAST) | OWASP ZAP (open-source), Burp Suite (commonly used with manual testing as well as automated scanning), Acunetix | Finding vulnerabilities like cross-site scripting and injection flaws in a running web application by crawling it and sending test inputs |
| Container image scanning | Trivy (Aqua Security, open-source), Grype (Anchore), Clair | Scanning container image layers for known vulnerabilities in operating system packages and application dependencies, typically integrated directly into a continuous integration/continuous deployment (CI/CD) pipeline |
| Software composition analysis (SCA) | Snyk, GitHub Dependabot, OWASP Dependency-Check | Identifying known vulnerabilities in open-source and third-party libraries a codebase depends on, including transitive (indirect) dependencies the development team may not even know they're using |
Worked example
A team building and deploying a web application uses all four categories at different points in the same pipeline, each catching something the others structurally can't: OWASP Dependency-Check (software composition analysis) flags a vulnerable version of a third-party logging library the moment it's added to the project, well before any code runs. Trivy (container scanning) then scans the built container image and separately flags an outdated base operating system package that the dependency scanner never looked at, since that's not an application dependency at all. Once deployed, Tenable Nessus (network and host scanning) finds an open management port on the underlying server that shouldn't be internet-reachable. Finally, OWASP ZAP (dynamic application security testing) crawls the running application itself and finds a cross-site scripting flaw in a search field, a class of issue none of the other three tools were ever positioned to catch, since it only exists once the application is actually running and handling real requests.
Trade-offs and pitfalls
- Relying on only one category of tool creates a predictable, sizable blind spot: a network scanner alone will never find an application-logic flaw, and a software composition analysis tool alone will never find a misconfigured server.
- Tool names shift in popularity and get acquired or rebranded over time; the categories and what each is fundamentally suited for are far more stable and useful to reason from in an interview than a specific product name.
- Running every category of scanner against every asset regardless of relevance wastes effort; matching the tool category to the asset (containers get container scanning, dependencies get software composition analysis, running web apps get dynamic application security testing) is what actually produces useful coverage.
Why shouldn't you prioritize remediation purely by raw CVSS score? Give a concrete example where a CVSS 9.8 finding wouldn't warrant immediate action.
Sample Answer
Direct answer: Raw Base score measures theoretical severity in a vacuum: it assumes an attacker with the specified access already exists and says nothing about whether that access is realistic, whether the asset matters to the business, or whether the vulnerability is being exploited anywhere. Two findings with the same 9.8 can carry wildly different real-world urgency, so prioritizing on the number alone routinely sends a team chasing an unreachable finding while an actively-exploited, lower-scored one waits in the backlog.
Structured elaboration:
- Base score deliberately ignores exposure. It doesn't know whether the vulnerable service is internet-facing or sealed behind three layers of network segmentation with no route from an untrusted zone; both get the same 9.8 if the underlying flaw is identical.
- Base score deliberately ignores exploitability-in-practice. A critical remote code execution finding with no public proof-of-concept and no evidence of active exploitation is a very different risk from one already listed in a known-exploited-vulnerabilities catalog with attackers actively scanning for it, yet both can carry the same Base score.
- Base score ignores compensating controls and lifecycle context. A finding can sit on a system that's already scheduled for decommission next week, or behind a virtual patch on a web application firewall, or on a host with the vulnerable feature disabled entirely, none of which the Base score knows about.
- This is exactly why the Temporal and Environmental metric groups exist on top of Base, and why mature programs layer exploit intelligence, exposure, and asset criticality on top of CVSS rather than sorting a queue by Base score alone.
Worked example: a critical Base score 9.8 remote code execution finding sits in an internal administrative console that's bound to localhost only, reachable solely from a jump box that itself requires multi-factor authentication and is monitored, on a server slated for decommission in two weeks as part of a planned migration. There's no public exploit, no evidence of scanning activity against it, and the asset holds no sensitive data. That finding does not need to jump the remediation queue ahead of a Base score 6.1 cross-site scripting finding on the public marketing site that a bot is already probing, because the 6.1 is realistically reachable today and the 9.8 realistically is not.
Trade-offs and pitfalls: the risk in over-correcting is treating every high-scoring finding on an internal system as low priority by default; internal-only doesn't mean unreachable, since a phishing-compromised laptop or a supply-chain foothold can put an attacker on the internal network anyway. A senior candidate's answer (and often a professional penetration tester's finding writeup) doesn't just cite the Base score in a ticket; it states the specific compensating factors, in this case network isolation, lack of known exploitation, and imminent decommission, so the remediation owner can see the reasoning rather than a bare number they have no way to challenge or confirm.
Write a script that checks a list of hosts for available OS updates in dry-run mode, safely and idempotently, with logging.
Sample Answer
Approach
The safety and idempotency both come from the same design choice: the script only reads package state, the equivalent of apt list --upgradable or yum check-update, and never installs or modifies anything, so running it once or a hundred times in a row produces the same result and changes nothing on the host. Each host is checked independently with its own error handling, so one unreachable host does not abort the whole run, and every step is logged so a failed or partial run is diagnosable afterward.
import logging
import json
from dataclasses import dataclass, field
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger("patch-check")
@dataclass
class HostResult:
host: str
updates_available: int
packages: list = field(default_factory=list)
status: str = "ok"
def check_host_updates(host, transport):
"""Read-only check: queries package state, changes nothing, so it is
safe to call repeatedly (dry-run by construction)."""
try:
stdout = transport(host)
packages = [line.split("/")[0] for line in stdout.strip().splitlines() if line]
return HostResult(host=host, updates_available=len(packages), packages=packages, status="ok")
except Exception as e:
logger.error("failed to check %s: %s", host, e)
return HostResult(host=host, updates_available=0, packages=[], status=f"error: {e}")
def fake_transport(host):
"""Stand-in for a real remote call (SSH via a library such as paramiko,
or Windows' remote-management protocol, WinRM, or an existing
configuration-management tool's own check mode). Deterministic per
host so this demo is reproducible without live hosts."""
canned = {
"web-01": "openssl/now 3.0.2-1 amd64\nlibcurl4/now 7.81.0-1 amd64\n",
"web-02": "",
"db-01": "postgresql-14/now 14.9-1 amd64\n",
}
if host not in canned:
raise ValueError(f"unknown host {host}")
return canned[host]
def dry_run_scan(hosts, transport=fake_transport):
logger.info("starting dry-run update scan for %d hosts (no changes will be made)", len(hosts))
results = []
for host in hosts:
result = check_host_updates(host, transport)
logger.info("%s: %d update(s) available: %s", result.host, result.updates_available, result.packages)
results.append(result)
ok = sum(1 for r in results if r.status == "ok")
logger.info("dry-run scan complete: %d/%d hosts reachable", ok, len(results))
return results
if __name__ == "__main__":
hosts = ["web-01", "web-02", "db-01"]
results = dry_run_scan(hosts)
summary = {r.host: r.updates_available for r in results}
print(json.dumps(summary, indent=2))
Output (stdout):
{
"web-01": 2,
"web-02": 0,
"db-01": 1
}
The logging calls write structured progress lines (host reached, count found, per-host status) at INFO level; the script's stdout is reserved for the final machine-readable summary so it can be piped into another tool without log noise mixed in.
Key points
fake_transport stands in for the real remote call; swapping it out for an actual SSH session or configuration-management check is the only change needed to point this at real hosts. The check never calls an install or upgrade command, which is what makes it a true dry run rather than a "dry run flag" on a command that could still mutate state if the flag were ever dropped. The per-host try/except means a single unreachable or misconfigured host produces one error line, not a crashed run; the summary distinguishes "0 updates" from "could not check."
Complexity
O(n) in the number of hosts, since each host is checked once and independently. The checks have no dependency on each other, so this is straightforward to parallelize with a thread pool or async gather if the host count gets large enough that sequential round-trips dominate runtime.
Edge cases
An unreachable host or authentication failure is caught and logged as its own status, not silently treated as "zero updates." Empty or malformed output from the remote command should not crash the parser, which the list comprehension here handles by returning an empty list. Duplicate hostnames in the input list would check and report the same host twice as written; a production version should deduplicate the host list up front and log a warning if it changed the input. A host that authenticates but whose package-manager command itself fails, for instance a corrupted local package index, should ideally be distinguished from "zero available updates" rather than collapsed into the same result.
How would you set remediation SLAs (e.g., 7/30/90 days) by severity and asset category? What escalation happens if an SLA is missed?
Sample Answer
Direct answer
Set SLAs (service-level agreements) as a matrix, not a flat table: combine severity with asset exposure and criticality, since a critical finding on an internet-facing production system needs a much shorter clock than the same severity on an isolated internal test box. Escalation should be automatic and staged, firing well before the deadline, moving from the assignee to their manager to leadership as the SLA clock runs out, with a required documented exception (not silence) if it's genuinely going to be missed.
Structured elaboration
A common starting matrix, using severity crossed with exposure/criticality:
| Severity | Internet-facing or critical asset | Internal-only, lower criticality |
|---|---|---|
| Critical | 24 to 72 hours (or immediate if actively exploited) | 7 days |
| High | 7 days | 30 days |
| Medium | 30 days | 90 days |
| Low | 90 days | Best-effort / next patch cycle |
The exact numbers matter less than the principle: severity alone (as in the raw CVSS, or Common Vulnerability Scoring System, score) is a poor sole basis for an SLA, because it ignores whether the finding is realistically reachable and what it would actually cost the business if exploited. Two findings with an identical CVSS score can reasonably carry very different SLAs.
Escalation on a missed or at-risk SLA should be staged, not a single deadline cliff:
- At roughly 50% of the SLA window elapsed, an automated reminder goes to the assignee.
- At roughly 80%, a reminder escalates to the assignee's manager, flagging that the deadline is approaching.
- At 100% (the SLA deadline itself), the finding auto-escalates: visibility moves to a leadership-facing dashboard, and the assignee's manager is formally notified the SLA was missed.
- If it remains open well past the deadline (a defined grace period, for example 10 additional days), it escalates further, up to a CISO-level review, and requires either a documented remediation plan with a firm new date or a formal, time-boxed risk acceptance signed by an accountable owner.
The exception process matters as much as the SLA itself: an SLA with no consequence for missing it, and no path to a documented exception, quietly becomes a suggestion rather than a commitment.
Worked example
A high-severity finding (30-day SLA) is disclosed on day 0, and a ticket is created and assigned on day 1. An automated reminder fires at day 15 (50% of 30 days). A manager-escalation reminder fires at day 24 (80% of 30 days). If it's still open at day 30, it auto-escalates onto the SLA-compliance dashboard leadership reviews weekly. If it's still open at day 40 (10 days past due), it escalates to the CISO, and the assignee's manager must either commit to a new firm date with a stated reason for the delay, or the asset owner must sign a documented, time-boxed risk acceptance explaining why the exposure is tolerable in the interim.
Trade-offs & pitfalls
- A single SLA table applied uniformly regardless of asset context creates false urgency on low-value systems (burning engineering goodwill on findings that don't matter much) and false complacency on high-value ones (a "medium" severity finding on a crown-jewel asset getting 90 days when it should get much less attention time).
- Watch for SLA gaming: a known failure pattern is marking a finding as a false positive to stop the clock without actually validating that claim. Any false-positive dismissal on a finding close to or past its SLA deserves a second look, not automatic acceptance.
- Escalation without a real decision-maker at the top of the chain is just noise; make sure the CISO-level (or equivalent) escalation path actually has the authority to approve a risk acceptance or force a resourcing conversation, not just receive a notification.
What are some practical rules of thumb for initial patch prioritization (short/medium/long remediation windows) in a mixed enterprise environment? When should a rule NOT be followed?
Sample Answer
Direct answer
A few durable rules of thumb: patch internet-facing and actively-exploited findings on a short window regardless of anything else, treat vendor-unpatchable findings as a compensating-control problem rather than a patch-SLA (service-level agreement) problem, and let asset criticality pull a finding's window shorter (or, for genuinely low-value assets, longer) even when severity alone wouldn't suggest it. None of these rules should be followed blindly when the specific context contradicts them.
Structured elaboration
Practical short/medium/long window rules for a mixed enterprise environment:
- Short window: internet-facing exposure, or the vulnerability is actively exploited (on a known-exploited list) or has a public proof-of-concept, regardless of the raw CVSS (Common Vulnerability Scoring System) score. This is the "someone could reasonably do this to you soon" tier.
- Medium window: internal-only exposure with meaningful severity but no confirmed exploitability signal; a real risk, but not one under active attack.
- Long window (or deprioritize/best-effort): low severity, isolated or low-criticality asset, no exploitability signal.
- A fourth case that isn't really a "window" at all: no vendor patch is available. This shouldn't sit on a patch-SLA clock at all, since there's nothing to patch; it needs to move immediately onto the compensating-control track (segmentation, a WAF or web application firewall rule, feature disablement) with its own timeline for when that control goes live.
When NOT to follow a rule (the harder, more senior half of this question):
- "Internet-facing means short window" doesn't automatically apply if the exposed service already sits behind an effective compensating control, like a WAF rule that specifically blocks the exploitation technique; the real remaining risk may be much lower than the exposure alone suggests.
- "Low severity means long window" breaks down when that low-severity finding is one link in a chain that reaches a much more valuable asset (the same reasoning as an attack-path analysis): a low CVSS finding that, chained with something else, opens a path to a crown-jewel system deserves more urgency than its severity score alone implies.
- An asset scheduled for imminent decommission (say, retiring in two weeks) may not be worth patching at all, even for a critical finding, if it can instead be isolated from the network for its remaining lifetime and destroyed on schedule, as long as that decision is documented rather than just assumed.
Worked example
Three findings land the same week. Finding A is on an internet-facing login page and has a public proof-of-concept: short window, patch within days. Finding B is a medium-severity issue on an internal reporting server with no exploitability signal: medium window, patch within a month or so. Finding C is a low-severity issue on an internal service, which by the plain rule would get a long window, except that service happens to be the one hop standing between a public-facing app and the customer database. Applying the rule literally would deprioritize it; applying the exception (a low-severity finding that's part of a path to a high-value asset deserves more urgency than its own score implies) correctly pulls it into the medium-window tier instead, with the override documented and the reasoning attached to the ticket.
Trade-offs & pitfalls
- Rules of thumb exist to make the common case fast, not to replace judgment on the exceptions; a team that follows them mechanically without checking for the exception cases listed above will misprioritize a meaningful fraction of real findings.
- Overriding a rule needs to be documented (why this finding is an exception), not just done silently, or the exception becomes indistinguishable from someone just not following the process.
Unlock Full Question Bank
Get access to all 32 Vulnerability Assessment and Management interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.