BrowserUse_and_ComputerUse_.../skills/domain/domain-intel/SKILL.md
2026-02-27 17:07:13 +03:00

16 KiB

name description
domain-intel Passive domain reconnaissance using Python stdlib. Use this skill for subdomain discovery, SSL certificate inspection, WHOIS lookups, DNS records, domain availability checks, and bulk multi-domain analysis. No API keys required. Triggers on requests like "find subdomains", "check ssl cert", "whois lookup", "is this domain available", "bulk check these domains".

Domain Intelligence — Passive OSINT

Passive domain reconnaissance using only Python stdlib and public data sources.
Zero dependencies. Zero API keys. Works out of the box.

Data Sources

  • crt.sh — Certificate Transparency logs (subdomain discovery)
  • WHOIS servers — Direct TCP queries to 100+ authoritative TLD servers
  • Google DNS-over-HTTPS — MX/NS/TXT/CNAME resolution
  • System DNS — A/AAAA record resolution

Usage

When the user asks about a domain, use the terminal tool to run the appropriate Python snippet below.
All functions print structured JSON. Parse and summarize results for the user.


1. Subdomain Discovery (crt.sh)

import json, urllib.request, urllib.parse
from datetime import datetime, timezone

def subdomains(domain, include_expired=False, limit=200):
    url = f"https://crt.sh/?q=%25.{urllib.parse.quote(domain)}&output=json"
    req = urllib.request.Request(url, headers={"User-Agent": "domain-intel-skill/1.0", "Accept": "application/json"})
    with urllib.request.urlopen(req, timeout=15) as r:
        entries = json.loads(r.read().decode())

    seen, results = set(), []
    for e in entries:
        not_after = e.get("not_after", "")
        if not include_expired and not_after:
            try:
                dt = datetime.strptime(not_after[:19], "%Y-%m-%dT%H:%M:%S").replace(tzinfo=timezone.utc)
                if dt <= datetime.now(timezone.utc):
                    continue
            except ValueError:
                pass
        for name in e.get("name_value", "").splitlines():
            name = name.strip().lower()
            if name and name not in seen:
                seen.add(name)
                results.append({"subdomain": name, "issuer": e.get("issuer_name",""), "not_after": not_after})

    results.sort(key=lambda r: (r["subdomain"].startswith("*"), r["subdomain"]))
    results = results[:limit]
    print(json.dumps({"domain": domain, "count": len(results), "subdomains": results}, indent=2))

subdomains("DOMAIN_HERE")

Example: Replace DOMAIN_HERE with example.com


2. SSL Certificate Inspection

import json, ssl, socket
from datetime import datetime, timezone

def check_ssl(host, port=443, timeout=10):
    def flat(rdns):
        r = {}
        for rdn in rdns:
            for item in rdn:
                if isinstance(item, (list,tuple)) and len(item)==2:
                    r[item[0]] = item[1]
        return r

    def extract_uris(entries):
        return [e[-1] if isinstance(e,(list,tuple)) else str(e) for e in entries]

    def parse_date(s):
        for fmt in ("%b %d %H:%M:%S %Y %Z", "%b  %d %H:%M:%S %Y %Z"):
            try: return datetime.strptime(s, fmt).replace(tzinfo=timezone.utc)
            except ValueError: pass
        return None

    warning = None
    try:
        ctx = ssl.create_default_context()
        with socket.create_connection((host, port), timeout=timeout) as sock:
            with ctx.wrap_socket(sock, server_hostname=host) as s:
                cert, cipher, proto = s.getpeercert(), s.cipher(), s.version()
    except ssl.SSLCertVerificationError as e:
        warning = str(e)
        ctx = ssl.create_default_context()
        ctx.check_hostname = False
        ctx.verify_mode = ssl.CERT_NONE
        with socket.create_connection((host, port), timeout=timeout) as sock:
            with ctx.wrap_socket(sock, server_hostname=host) as s:
                cert, cipher, proto = s.getpeercert(), s.cipher(), s.version()

    not_after = parse_date(cert.get("notAfter",""))
    not_before = parse_date(cert.get("notBefore",""))
    now = datetime.now(timezone.utc)
    days = (not_after - now).days if not_after else None
    is_expired = days is not None and days < 0

    if is_expired: status = f"EXPIRED ({abs(days)} days ago)"
    elif days is not None and days <= 14: status = f"CRITICAL — {days} day(s) left"
    elif days is not None and days <= 30: status = f"WARNING — {days} day(s) left"
    else: status = f"OK — {days} day(s) remaining" if days is not None else "unknown"

    print(json.dumps({
        "host": host, "port": port,
        "subject": flat(cert.get("subject",[])),
        "issuer": flat(cert.get("issuer",[])),
        "subject_alt_names": [f"{t}:{v}" for t,v in cert.get("subjectAltName",[])],
        "not_before": not_before.isoformat() if not_before else "",
        "not_after": not_after.isoformat() if not_after else "",
        "days_remaining": days, "is_expired": is_expired, "expiry_status": status,
        "tls_version": proto, "cipher_suite": cipher[0] if cipher else None,
        "serial_number": cert.get("serialNumber",""),
        "ocsp_urls": extract_uris(cert.get("OCSP",[])),
        "ca_issuers": extract_uris(cert.get("caIssuers",[])),
        "verification_warning": warning,
    }, indent=2))

check_ssl("DOMAIN_HERE")

3. WHOIS Lookup (100+ TLDs)

import json, socket, re
from datetime import datetime, timezone

WHOIS_SERVERS = {
    "com":"whois.verisign-grs.com","net":"whois.verisign-grs.com","org":"whois.pir.org",
    "io":"whois.nic.io","co":"whois.nic.co","ai":"whois.nic.ai","dev":"whois.nic.google",
    "app":"whois.nic.google","tech":"whois.nic.tech","shop":"whois.nic.shop",
    "store":"whois.nic.store","online":"whois.nic.online","site":"whois.nic.site",
    "cloud":"whois.nic.cloud","digital":"whois.nic.digital","media":"whois.nic.media",
    "blog":"whois.nic.blog","info":"whois.afilias.net","biz":"whois.biz",
    "me":"whois.nic.me","tv":"whois.nic.tv","cc":"whois.nic.cc","ws":"whois.website.ws",
    "uk":"whois.nic.uk","co.uk":"whois.nic.uk","de":"whois.denic.de","nl":"whois.domain-registry.nl",
    "fr":"whois.nic.fr","it":"whois.nic.it","es":"whois.nic.es","pl":"whois.dns.pl",
    "ru":"whois.tcinet.ru","se":"whois.iis.se","no":"whois.norid.no","fi":"whois.fi",
    "ch":"whois.nic.ch","at":"whois.nic.at","be":"whois.dns.be","cz":"whois.nic.cz",
    "br":"whois.registro.br","ca":"whois.cira.ca","mx":"whois.mx","au":"whois.auda.org.au",
    "jp":"whois.jprs.jp","cn":"whois.cnnic.cn","in":"whois.inregistry.net","kr":"whois.kr",
    "sg":"whois.sgnic.sg","hk":"whois.hkirc.hk","tr":"whois.nic.tr","ae":"whois.aeda.net.ae",
    "za":"whois.registry.net.za","ng":"whois.nic.net.ng","ly":"whois.nic.ly",
    "space":"whois.nic.space","zone":"whois.nic.zone","ninja":"whois.nic.ninja",
    "guru":"whois.nic.guru","rocks":"whois.nic.rocks","social":"whois.nic.social",
    "network":"whois.nic.network","global":"whois.nic.global","design":"whois.nic.design",
    "studio":"whois.nic.studio","agency":"whois.nic.agency","finance":"whois.nic.finance",
    "legal":"whois.nic.legal","health":"whois.nic.health","green":"whois.nic.green",
    "city":"whois.nic.city","land":"whois.nic.land","live":"whois.nic.live",
    "game":"whois.nic.game","games":"whois.nic.games","pw":"whois.nic.pw",
    "mn":"whois.nic.mn","sh":"whois.nic.sh","gg":"whois.gg","im":"whois.nic.im",
}

def whois_query(domain, server, port=43):
    with socket.create_connection((server, port), timeout=10) as s:
        s.sendall((domain+"\r\n").encode())
        chunks = []
        while True:
            c = s.recv(4096)
            if not c: break
            chunks.append(c)
        return b"".join(chunks).decode("utf-8", errors="replace")

def parse_iso(s):
    if not s: return None
    for fmt in ("%Y-%m-%dT%H:%M:%S","%Y-%m-%dT%H:%M:%SZ","%Y-%m-%d %H:%M:%S","%Y-%m-%d"):
        try: return datetime.strptime(s[:19],fmt).replace(tzinfo=timezone.utc)
        except ValueError: pass
    return None

def whois(domain):
    parts = domain.split(".")
    server = WHOIS_SERVERS.get(".".join(parts[-2:])) or WHOIS_SERVERS.get(parts[-1])
    if not server:
        print(json.dumps({"error": f"No WHOIS server for .{parts[-1]}"}))
        return
    try:
        raw = whois_query(domain, server)
    except Exception as e:
        print(json.dumps({"error": str(e)}))
        return

    patterns = {
        "registrar": r"(?:Registrar|registrar):\s*(.+)",
        "creation_date": r"(?:Creation Date|Created|created):\s*(.+)",
        "expiration_date": r"(?:Registry Expiry Date|Expiration Date|Expiry Date):\s*(.+)",
        "updated_date": r"(?:Updated Date|Last Modified):\s*(.+)",
        "name_servers": r"(?:Name Server|nserver):\s*(.+)",
        "status": r"(?:Domain Status|status):\s*(.+)",
        "dnssec": r"DNSSEC:\s*(.+)",
    }
    result = {"domain": domain, "whois_server": server}
    for key, pat in patterns.items():
        matches = re.findall(pat, raw, re.IGNORECASE)
        if matches:
            if key in ("name_servers","status"):
                result[key] = list(dict.fromkeys(m.strip().lower() for m in matches))
            else:
                result[key] = matches[0].strip()
    for field in ("creation_date","expiration_date","updated_date"):
        if field in result:
            dt = parse_iso(result[field][:19])
            if dt:
                result[field] = dt.isoformat()
                if field == "expiration_date":
                    days = (dt - datetime.now(timezone.utc)).days
                    result["expiration_days_remaining"] = days
                    result["is_expired"] = days < 0
    print(json.dumps(result, indent=2))

whois("DOMAIN_HERE")

4. DNS Records

import json, socket, urllib.request, urllib.parse

def dns(domain, types=None):
    if not types: types = ["A","AAAA","MX","NS","TXT","CNAME"]
    records = {}

    for qtype in types:
        if qtype == "A":
            try: records["A"] = list(dict.fromkeys(i[4][0] for i in socket.getaddrinfo(domain,None,socket.AF_INET)))
            except: records["A"] = []
        elif qtype == "AAAA":
            try: records["AAAA"] = list(dict.fromkeys(i[4][0] for i in socket.getaddrinfo(domain,None,socket.AF_INET6)))
            except: records["AAAA"] = []
        else:
            url = f"https://dns.google/resolve?name={urllib.parse.quote(domain)}&type={qtype}"
            try:
                req = urllib.request.Request(url, headers={"User-Agent":"domain-intel-skill/1.0"})
                with urllib.request.urlopen(req, timeout=10) as r:
                    data = json.loads(r.read())
                records[qtype] = [a.get("data","").strip().rstrip(".") for a in data.get("Answer",[]) if a.get("data")]
            except:
                records[qtype] = []

    print(json.dumps({"domain": domain, "records": records}, indent=2))

dns("DOMAIN_HERE")

5. Domain Availability Check

import json, socket, ssl

def available(domain):
    import urllib.request, urllib.parse, re
    from datetime import datetime, timezone

    signals = {}

    # DNS check
    try: a = [i[4][0] for i in socket.getaddrinfo(domain,None,socket.AF_INET)]
    except: a = []
    try: ns_url = f"https://dns.google/resolve?name={urllib.parse.quote(domain)}&type=NS"
        req = urllib.request.Request(ns_url, headers={"User-Agent":"domain-intel-skill/1.0"})
        with urllib.request.urlopen(req, timeout=10) as r:
            ns = [x.get("data","") for x in json.loads(r.read()).get("Answer",[])]
    except: ns = []
    signals["dns_a"] = a
    signals["dns_ns"] = ns
    dns_exists = bool(a or ns)

    # SSL check
    ssl_up = False
    try:
        ctx = ssl.create_default_context()
        ctx.check_hostname = False; ctx.verify_mode = ssl.CERT_NONE
        with socket.create_connection((domain,443),timeout=3) as s:
            with ctx.wrap_socket(s, server_hostname=domain): ssl_up = True
    except: pass
    signals["ssl_reachable"] = ssl_up

    # WHOIS check (simple)
    WHOIS = {"com":"whois.verisign-grs.com","net":"whois.verisign-grs.com","org":"whois.pir.org",
             "io":"whois.nic.io","co":"whois.nic.co","ai":"whois.nic.ai","dev":"whois.nic.google",
             "me":"whois.nic.me","app":"whois.nic.google","tech":"whois.nic.tech"}
    tld = domain.rsplit(".",1)[-1]
    whois_avail = None
    whois_note = ""
    server = WHOIS.get(tld)
    if server:
        try:
            with socket.create_connection((server,43),timeout=10) as s:
                s.sendall((domain+"\r\n").encode())
                raw = b""
                while True:
                    c = s.recv(4096)
                    if not c: break
                    raw += c
                raw = raw.decode("utf-8",errors="replace").lower()
            if any(p in raw for p in ["no match","not found","no data found","status: free"]):
                whois_avail = True; whois_note = "WHOIS: not found"
            elif "registrar:" in raw or "creation date:" in raw:
                whois_avail = False; whois_note = "WHOIS: registered"
            else: whois_note = "WHOIS: inconclusive"
        except Exception as e: whois_note = f"WHOIS error: {e}"
    signals["whois_available"] = whois_avail
    signals["whois_note"] = whois_note

    if not dns_exists and whois_avail is True: verdict,conf = "LIKELY AVAILABLE","high"
    elif dns_exists or whois_avail is False or ssl_up: verdict,conf = "REGISTERED / IN USE","high"
    elif not dns_exists and whois_avail is None: verdict,conf = "POSSIBLY AVAILABLE","medium"
    else: verdict,conf = "UNCERTAIN","low"

    print(json.dumps({"domain":domain,"verdict":verdict,"confidence":conf,"signals":signals},indent=2))

available("DOMAIN_HERE")

6. Bulk Analysis (Multiple Domains in Parallel)

import json
from concurrent.futures import ThreadPoolExecutor, as_completed

# Paste any of the functions above (check_ssl, whois, dns, available, subdomains)
# then use this runner:

def bulk_check(domains, checks=None, max_workers=5):
    if not checks: checks = ["ssl", "whois", "dns", "available"]
    
    def run_one(domain):
        result = {"domain": domain}
        # Import/define individual functions above, then:
        if "ssl" in checks:
            try: result["ssl"] = json.loads(check_ssl_json(domain))
            except Exception as e: result["ssl"] = {"error": str(e)}
        if "whois" in checks:
            try: result["whois"] = json.loads(whois_json(domain))
            except Exception as e: result["whois"] = {"error": str(e)}
        if "dns" in checks:
            try: result["dns"] = json.loads(dns_json(domain))
            except Exception as e: result["dns"] = {"error": str(e)}
        if "available" in checks:
            try: result["available"] = json.loads(available_json(domain))
            except Exception as e: result["available"] = {"error": str(e)}
        return result

    results = []
    with ThreadPoolExecutor(max_workers=min(max_workers,10)) as ex:
        futures = {ex.submit(run_one, d): d for d in domains[:20]}
        for f in as_completed(futures):
            results.append(f.result())

    print(json.dumps({"total": len(results), "checks": checks, "results": results}, indent=2))

Quick Reference

Task What to run
Find subdomains Snippet 1 — replace DOMAIN_HERE
Check SSL cert Snippet 2 — replace DOMAIN_HERE
WHOIS lookup Snippet 3 — replace DOMAIN_HERE
DNS records Snippet 4 — replace DOMAIN_HERE
Is domain available? Snippet 5 — replace DOMAIN_HERE
Bulk check 20 domains Snippet 6

Notes

  • All requests are passive — no active scanning, no packets sent to target hosts (except SSL check which makes a TCP connection)
  • subdomains only queries crt.sh — the target domain is never contacted
  • WHOIS queries go to registrar servers, not the target
  • Results are structured JSON — summarize key findings for the user
  • For expired cert warnings or WHOIS redaction, mention these to the user as notable findings