Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Censys search engine for internet-connected hosts, TLS certificates, and domains. Use when: certificate transparency monitoring, finding hosts by certificate fingerprint, alternative to Shodan for TLS/SSL analysis, discovering hosts running specific services, or tracking infrastructure changes via cert issuance.
.claude/skills/terminalskills-censys/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 121% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 200% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 275% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 150% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 156% | 0% |
Censys continuously scans the entire internet and indexes every reachable host with detailed information about open ports, TLS/SSL certificates, service banners, and configurations. Censys is particularly strong for certificate-based discovery — it indexes certificate transparency logs and lets you pivot from certificate subject names to IP addresses and vice versa. This makes it excellent for finding unknown infrastructure tied to a target organization.
Requires: Censys API key (free account at censys.io gives 250 queries/month).
bashpip install censys
pythonimport os from censys.search import CensysHosts, CensysCerts from censys.common.exceptions import CensysRateLimitExceededException, CensysNotFoundException import json import time # Set credentials via environment variables (recommended) # export CENSYS_API_ID="your-api-id" # export CENSYS_API_SECRET="your-api-secret" # Or pass directly CENSYS_API_ID = os.getenv("CENSYS_API_ID", "YOUR_API_ID") CENSYS_API_SECRET = os.getenv("CENSYS_API_SECRET", "YOUR_API_SECRET") # Initialize clients h = CensysHosts(api_id=CENSYS_API_ID, api_secret=CENSYS_API_SECRET) # Check account quota account = h.account() print(f"Quota: {account.get('quota', {})}")
pythondef search_hosts(query, max_results=100, fields=None): """ Search Censys for hosts matching a query. Common query examples: - services.tls.certificates.leaf_data.subject.common_name: "example.com" - services.port: 3389 and autonomous_system.name: "Company" - services.http.response.html_title: "Dashboard" - services.service_name: "REDIS" and not ip: "10.0.0.0/8" """ if fields is None: fields = ["ip", "services.port", "services.service_name", "autonomous_system.name", "autonomous_system.asn", "location.country", "services.tls.certificates.leaf_data.subject.common_name"] print(f"Searching: {query}") results = [] try: for hit in h.search(query, fields=fields, pages=max_results // 100 + 1): results.append(hit) if len(results) >= max_results: break time.sleep(0.1) # Gentle rate limiting except CensysRateLimitExceededException: print("Rate limit reached. Results so far:") print(f"Found {len(results)} hosts") for r in results[:20]: ip = r.get("ip") services = r.get("services", []) ports = [str(s.get("port", "?")) for s in services] asn_name = r.get("autonomous_system", {}).get("name", "N/A") country = r.get("location", {}).get("country", "N/A") print(f" {ip:<20} ports: {','.join(ports):<20} {asn_name} ({country})") return results # Find hosts serving TLS certs for a domain search_hosts('services.tls.certificates.leaf_data.subject.common_name: "*.example.com"') # Find exposed Redis servers search_hosts('services.service_name: "REDIS"', max_results=50) # Find hosts in a specific org search_hosts('autonomous_system.name: "Example Corporation" and services.port: 443')
pythondef lookup_host(ip_address): """Get detailed information about a specific IP from Censys.""" try: host = h.view(ip_address) print(f"\n=== Censys Host View: {ip_address} ===") print(f"IP: {host.get('ip')}") asn = host.get("autonomous_system", {}) print(f"ASN: {asn.get('asn')} — {asn.get('name')} ({asn.get('country_code')})") loc = host.get("location", {}) print(f"Location: {loc.get('city')}, {loc.get('country')}") print(f"\nServices:") for service in host.get("services", []): port = service.get("port") svc_name = service.get("service_name", "unknown") transport = service.get("transport_protocol", "tcp") banner = service.get("banner", "")[:80] tls = service.get("tls", {}) cert_cn = "" if tls: leaf = tls.get("certificates", {}).get("leaf_data", {}) cert_cn = leaf.get("subject", {}).get("common_name", "") print(f" {port}/{transport} — {svc_name}", end="") if cert_cn: print(f" | cert: {cert_cn}", end="") if banner: print(f" | banner: {banner}", end="") print() return host except CensysNotFoundException: print(f"Host {ip_address} not found in Censys.") return None lookup_host("8.8.8.8")
pythondef find_hosts_by_domain_cert(domain, include_subdomains=True): """ Find all IP addresses serving TLS certificates for a domain. This is highly effective for finding unknown/shadow infrastructure. """ if include_subdomains: query = f'services.tls.certificates.leaf_data.names: "{domain}"' else: query = f'services.tls.certificates.leaf_data.subject.common_name: "{domain}"' fields = [ "ip", "services.port", "services.tls.certificates.leaf_data.subject.common_name", "services.tls.certificates.leaf_data.names", "services.tls.certificates.leaf_data.issuer.common_name", "autonomous_system.name", "location.country", ] print(f"Finding hosts with TLS certs for: {domain}") results = [] for hit in h.search(query, fields=fields, pages=5): results.append(hit) print(f"\n{len(results)} hosts found:\n") for r in results: ip = r.get("ip") asn = r.get("autonomous_system", {}).get("name", "?") country = r.get("location", {}).get("country", "?") services = r.get("services", []) for svc in services: tls = svc.get("tls", {}) if tls: leaf = tls.get("certificates", {}).get("leaf_data", {}) cn = leaf.get("subject", {}).get("common_name", "") names = leaf.get("names", []) issuer = leaf.get("issuer", {}).get("common_name", "") port = svc.get("port") print(f" {ip}:{port} | CN: {cn} | SAN: {names[:3]} | Issuer: {issuer} | {asn} ({country})") return results find_hosts_by_domain_cert("example.com")
pythondef aggregate_query(query, field, num_buckets=10): """ Aggregate Censys results to get a distribution overview. Useful for understanding what products/versions/countries/orgs are common. """ result = h.aggregate(query, field, num_buckets=num_buckets) print(f"\nAggregation: {query}") print(f"Field: {field}") print(f"Total matching: {result.get('total', 0):,}") print(f"\nTop {num_buckets} values:") for bucket in result.get("buckets", []): print(f" {bucket['key']:<50} {bucket['count']:>10,}") # Distribution of countries for Apache servers on port 80 aggregate_query("services.http.response.headers.Server: Apache", "location.country", 15) # Distribution of services for a specific ASN aggregate_query("autonomous_system.asn: 15169", "services.service_name", 20) # TLS version distribution aggregate_query("services.tls: *", "services.tls.version_selected", 10)
pythondef export_hosts_to_json(query, output_file, max_results=500): """Export Censys search results to a JSON file for offline analysis.""" print(f"Exporting up to {max_results} hosts for query: {query}") results = [] for hit in h.search(query, pages=max_results // 100 + 1): results.append(hit) if len(results) >= max_results: break with open(output_file, "w") as f: json.dump(results, f, indent=2) print(f"Exported {len(results)} records to {output_file}") return results # Export all exposed Elasticsearch instances export_hosts_to_json( 'services.service_name: "ELASTICSEARCH" and services.elasticsearch.indices_count > 0', "exposed_elasticsearch.json", max_results=200 )
| Query | Description | |-------|-------------| | services.port: 443 | Hosts with port 443 open | | services.service_name: "HTTP" | Hosts running HTTP | | services.tls.certificates.leaf_data.subject.common_name: "*.example.com" | Wildcard cert for domain | | services.tls.certificates.leaf_data.names: "example.com" | Any cert naming the domain | | autonomous_system.name: "Amazon" | Hosts in Amazon ASN | | autonomous_system.asn: 16509 | Hosts in ASN 16509 | | location.country: "Germany" | Hosts in Germany | | ip: "8.8.8.0/24" | Hosts in CIDR range | | services.http.response.html_title: "Kibana" | Exposed Kibana instances | | labels: "cloud" | Cloud-hosted infrastructure |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 18,736 | 24,392 | +30% | 1 | 1 | 0% | 2,204 | 4,861 | +121% | 0 | 0 | — |
case-02 | pass→pass | 11,178 | 7,332 | -34% | 1 | 1 | 0% | 2,330 | 4,332 | +86% | 0 | 0 | — |
case-03 | pass→pass | 9,773 | 7,851 | -20% | 1 | 1 | 0% | 1,822 | 4,296 | +136% | 0 | 0 | — |
case-04 | pass→pass | 15,832 | 11,504 | -27% | 1 | 1 | 0% | 3,242 | 5,228 | +61% | 0 | 0 | — |
case-05 | pass→pass | 5,086 | 3,351 | -34% | 1 | 1 | 0% | 1,105 | 3,416 | +209% | 0 | 0 | — |
case-06 | pass→pass | 5,922 | 1,596 | -73% | 1 | 1 | 0% | 1,104 | 3,091 | +180% | 0 | 0 | — |
case-07 | fail→pass | 5,619 | 2,180 | -61% | 1 | 1 | 0% | 1,077 | 3,230 | +200% | 0 | 0 | — |
case-08 | pass→pass | 6,075 | 4,521 | -26% | 1 | 1 | 0% | 1,167 | 3,554 | +205% | 0 | 0 | — |
case-09 | fail→pass | 4,640 | 2,784 | -40% | 1 | 1 | 0% | 881 | 3,300 | +275% | 0 | 0 | — |
case-10 | pass→pass | 4,632 | 4,343 | -6% | 1 | 1 | 0% | 989 | 3,776 | +282% | 0 | 0 | — |
case-11 | pass→fail | 7,133 | 3,900 | -45% | 1 | 1 | 0% | 1,189 | 3,450 | +190% | 0 | 0 | — |
case-12 | pass→pass | 8,924 | 5,751 | -36% | 1 | 1 | 0% | 941 | 3,293 | +250% | 0 | 0 | — |
case-13 | pass→pass | 4,175 | 3,044 | -27% | 1 | 1 | 0% | 658 | 3,363 | +411% | 0 | 0 | — |
case-14 | pass→pass | 7,519 | 5,311 | -29% | 1 | 1 | 0% | 1,527 | 3,991 | +161% | 0 | 0 | — |
case-15 | fail→pass | 6,969 | 3,363 | -52% | 1 | 1 | 0% | 1,368 | 3,417 | +150% | 0 | 0 | — |
case-16 | pass→pass | 10,651 | 6,389 | -40% | 1 | 1 | 0% | 2,273 | 4,100 | +80% | 0 | 0 | — |
case-17 | pass→pass | 4,935 | 3,020 | -39% | 1 | 1 | 0% | 889 | 3,334 | +275% | 0 | 0 | — |
case-18 | fail→pass | 8,620 | 4,416 | -49% | 1 | 1 | 0% | 1,457 | 3,733 | +156% | 0 | 0 | — |
case-19 | fail→pass | 7,576 | 2,185 | -71% | 1 | 1 | 0% | 1,335 | 3,145 | +136% | 0 | 0 | — |
case-20 | pass→pass | 7,923 | 2,605 | -67% | 1 | 1 | 0% | 1,288 | 3,237 | +151% | 0 | 0 | — |
case-21 | pass→pass | 7,443 | 4,589 | -38% | 1 | 1 | 0% | 1,218 | 3,539 | +191% | 0 | 0 | — |
case-22 | pass→pass | 2,541 | 2,166 | -15% | 1 | 1 | 0% | 442 | 3,145 | +612% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +23 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.