Skip to content

klyrek-osint

Status: live on PyPI — pip install klyrek-osint

Public intelligence gathering: certificate-transparency subdomain discovery (crt.sh), WHOIS lookups, and NVD CVE search.

from klyrek_osint.certificate_transparency import query_crtsh
from klyrek_osint.whois import whois_lookup
from klyrek_osint.cve_lookup import search_cves, cve_to_finding

hosts = query_crtsh("klyrek.com")          # certificate-transparency subdomain discovery
record = whois_lookup("klyrek.com")
cves = search_cves("express 4.18", max_results=3)

Real WHOIS result for klyrek.com:

registrar: NameSilo, LLC
creation_date: 2026-06-09T11:39:21Z
expiration_date: 2027-06-09T11:39:21Z
name_servers: ['mitch.ns.cloudflare.com', 'pola.ns.cloudflare.com']

whois_lookup does a raw-socket WHOIS query: it asks whois.iana.org for a refer: referral, then queries that registrar's WHOIS server directly.

Real CVE search result for "express 4.18" — NVD's search is free-text keyword matching, not a precise CPE match, so always review hits rather than trusting them blindly:

CVE-2022-24729  MEDIUM  6.5  CKEditor4 is an open source WYSIWYG HTML editor...
CVE-2025-59302  MEDIUM  4.7  Apache CloudStack improper control of code generation...
CVE-2026-4800   HIGH    8.1  Impact: the fix for CVE-2021-23337 added validation...

crt.sh is genuinely flaky

crt.sh's public API returns 502 Bad Gateway fairly often under load — three consecutive attempts against klyrek.com failed this way while writing this documentation. That's crt.sh, not a Klyrek bug. Retry, or query a mirror.

API reference

klyrek_osint.certificate_transparency

Passive subdomain discovery via crt.sh certificate transparency logs.

query_crtsh

query_crtsh(domain: str, client: Client | None = None) -> list[str]

Query crt.sh for certificates matching *.domain and return the unique hostnames found.

This queries a public certificate transparency log about the domain, not the domain's own infrastructure, so it deliberately doesn't go through klyrek-http's AuthorizationScope the way a direct request to the target would.

Source code in klyrek_osint\certificate_transparency.py
def query_crtsh(domain: str, client: httpx.Client | None = None) -> list[str]:
    """Query crt.sh for certificates matching ``*.domain`` and return the unique hostnames found.

    This queries a public certificate transparency log about the domain, not the domain's
    own infrastructure, so it deliberately doesn't go through klyrek-http's
    AuthorizationScope the way a direct request to the target would.
    """
    owns_client = client is None
    client = client or httpx.Client(timeout=_TIMEOUT)
    try:
        response = client.get(_CRTSH_URL, params={"q": f"%.{domain}", "output": "json"})
        response.raise_for_status()
        records = response.json()
    finally:
        if owns_client:
            client.close()

    hostnames: set[str] = set()
    for record in records:
        for name in record.get("name_value", "").split("\n"):
            name = name.strip().lower()
            if name and not name.startswith("*."):
                hostnames.add(name)
    return sorted(hostnames)

klyrek_osint.whois

Minimal WHOIS client using the standard two-step IANA-referral pattern.

Uses a raw socket rather than a python-whois dependency: ask whois.iana.org which registrar server owns the TLD, then query that server directly.

whois_lookup

whois_lookup(domain: str) -> WhoisRecord

Look up WHOIS data for a domain via the standard IANA-referral pattern.

Source code in klyrek_osint\whois.py
def whois_lookup(domain: str) -> WhoisRecord:
    """Look up WHOIS data for a domain via the standard IANA-referral pattern."""
    iana_response = _query(_IANA_WHOIS_HOST, domain)
    referral_match = _REFERRAL_PATTERN.search(iana_response)
    raw = _query(referral_match.group(1), domain) if referral_match else iana_response

    def _extract(pattern: re.Pattern[str]) -> str | None:
        match = pattern.search(raw)
        return match.group(1).strip() if match else None

    name_servers = sorted({m.group(1).strip().lower() for m in _NAME_SERVER_PATTERN.finditer(raw)})

    return WhoisRecord(
        domain=domain,
        registrar=_extract(_FIELD_PATTERNS["registrar"]),
        creation_date=_extract(_FIELD_PATTERNS["creation_date"]),
        expiration_date=_extract(_FIELD_PATTERNS["expiration_date"]),
        name_servers=name_servers,
        raw=raw,
    )

klyrek_osint.cve_lookup

Look up known CVEs for a detected technology via the public NVD REST API.

cve_to_finding

cve_to_finding(record: CveRecord) -> Finding

Convert a CveRecord into a klyrek_core Finding, so it composes with other modules.

Source code in klyrek_osint\cve_lookup.py
def cve_to_finding(record: CveRecord) -> Finding:
    """Convert a CveRecord into a klyrek_core Finding, so it composes with other modules."""
    severity = _SEVERITY_MAP.get((record.severity or "").upper(), Severity.INFO)
    score_note = (
        f"CVSS base score: {record.base_score}"
        if record.base_score is not None
        else "no CVSS score available"
    )
    return Finding(
        title=f"Known vulnerability: {record.cve_id}",
        severity=severity,
        description=record.description,
        evidence=[score_note],
        module="klyrek-osint",
    )

search_cves

search_cves(keyword: str, max_results: int = 10, client: Client | None = None) -> list[CveRecord]

Search the NVD database for CVEs matching a free-text keyword (e.g. 'nginx 1.24.0').

Source code in klyrek_osint\cve_lookup.py
def search_cves(
    keyword: str, max_results: int = 10, client: httpx.Client | None = None
) -> list[CveRecord]:
    """Search the NVD database for CVEs matching a free-text keyword (e.g. 'nginx 1.24.0')."""
    owns_client = client is None
    client = client or httpx.Client(timeout=_TIMEOUT)
    try:
        response = client.get(
            _NVD_CVE_URL, params={"keywordSearch": keyword, "resultsPerPage": max_results}
        )
        response.raise_for_status()
        data = response.json()
    finally:
        if owns_client:
            client.close()

    records: list[CveRecord] = []
    for item in data.get("vulnerabilities", []):
        cve = item.get("cve", {})
        severity, score = _extract_severity_and_score(cve)
        records.append(
            CveRecord(
                cve_id=cve.get("id", "unknown"),
                description=_extract_description(cve),
                severity=severity,
                base_score=score,
                published=cve.get("published"),
            )
        )
    return records