Subdomain Enumeration: Harnessing Certificate Transparency for Comprehensive Discovery

In the ever-expanding landscape of cybersecurity, subdomain enumeration remains a pivotal aspect of reconnaissance. Traditional methods often involve brute-force or dictionary attacks, but a more sophisticated approach involves querying certificate transparency logs provided by services like crt.sh and Censys.io. In this article, we’ll explore the rationale behind using certificate records for subdomain enumeration, understand the inner workings of certificate transparency, and provide practical code examples using Go to leverage these services effectively.

Why Certificate Transparency for Subdomain Enumeration?

Certificate Transparency (CT) was introduced to enhance the security of the web by providing a public, verifiable log of all issued SSL/TLS certificates. By querying CT logs, security professionals can gain valuable insights into subdomains associated with a domain. This method offers a non-intrusive and reliable way to discover subdomains, as certificates are issued when a new subdomain is set up.

Understanding Certificate Transparency

Certificate Transparency works by logging certificates in a tamper-evident public ledger. Certificate Authorities (CAs) submit certificates to these logs, and anyone can query the logs to obtain information about issued certificates. The logs are often organized by domain, allowing for efficient subdomain enumeration.

Code Implementation in Go: Querying crt.sh

Let’s dive into a practical example using Go to query the crt.sh database for subdomains associated with a target domain.

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "regexp"
)

func queryCRTSH(domain string) ([]string, error) {
    url := fmt.Sprintf("https://crt.sh/?q=%%25.%s&output=json", domain)
    resp, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }

    // Extract subdomains from the JSON response using regular expression
    re := regexp.MustCompile(`"name_value":"(.*?)"`)
    matches := re.FindAllStringSubmatch(string(body), -1)

    var subdomains []string
    for _, match := range matches {
        subdomains = append(subdomains, match[1])
    }

    return subdomains, nil
}

func main() {
    targetDomain := "example.com"
    subdomains, err := queryCRTSH(targetDomain)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Discovered Subdomains:")
    for _, subdomain := range subdomains {
        fmt.Println(subdomain)
    }
}

In this Go code snippet, the queryCRTSH function queries crt.sh for subdomains associated with the target domain. It then extracts subdomains from the JSON response using regular expressions.

Code Implementation in Go: Querying Censys.io

Now, let’s explore querying Censys.io for subdomains using the Censys API. You’ll need an API key from Censys to run this example.

package main

import (
    "encoding/base64"
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
)

const censysAPIURL = "https://censys.io/api/v1/search/certificates"

func queryCensys(domain string, apiKey string) ([]string, error) {
    query := fmt.Sprintf("parsed.names: %s", domain)
    url := fmt.Sprintf("%s?q=%s", censysAPIURL, base64.URLEncoding.EncodeToString([]byte(query)))

    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        return nil, err
    }

    req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(apiKey)))

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }

    // Extract subdomains from the JSON response
    subdomains := strings.Fields(string(body))

    return subdomains, nil
}

func main() {
    targetDomain := "example.com"
    apiKey := "YOUR_CENSYS_API_KEY"

    subdomains, err := queryCensys(targetDomain, apiKey)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Discovered Subdomains:")
    for _, subdomain := range subdomains {
        fmt.Println(subdomain)
    }
}

In this example, the queryCensys function sends a query to the Censys API and extracts subdomains from the JSON response. Ensure to replace “YOUR_CENSYS_API_KEY” with your actual Censys API key.

Conclusion

Certificate Transparency logs provided by services like crt.sh and Censys.io offer a powerful avenue for subdomain enumeration, providing a wealth of information about a target domain. Leveraging Go, we’ve demonstrated practical examples for querying both crt.sh and Censys.io, showcasing how security professionals can harness the power of certificate records to enhance their reconnaissance efforts. As the cybersecurity landscape continues to evolve, incorporating certificate transparency into subdomain enumeration strategies becomes an invaluable tactic for comprehensive and non-intrusive discovery.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *