Added Project Files
parent
088e59fb0c
commit
f95da15c7f
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// misc.go : contains misc helper function
|
||||
// Written By : @ice3man (Nizamul Rana)
|
||||
//
|
||||
// Distributed Under MIT License
|
||||
// Copyrights (C) 2018 Ice3man
|
||||
//
|
||||
|
||||
package helper
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
)
|
||||
//
|
||||
// ReadConfigFile : Reads a config file from disk
|
||||
//
|
||||
// @return config : configuration structure
|
||||
// @return err : if no error nil, else error
|
||||
//
|
||||
func ReadConfigFile() (configuration *Config, err error) {
|
||||
|
||||
var config Config
|
||||
|
||||
// Read the file
|
||||
raw, err := ioutil.ReadFile("./config.json")
|
||||
if err != nil {
|
||||
return &config, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(raw, &config)
|
||||
if (err != nil) {
|
||||
return &config, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
//
|
||||
// Returns unique items in a slice
|
||||
// Adapted from http://www.golangprograms.com/remove-duplicate-values-from-slice.html
|
||||
//
|
||||
func Unique(strSlice []string) []string {
|
||||
keys := make(map[string]bool)
|
||||
list := []string{}
|
||||
for _, entry := range strSlice {
|
||||
if _, value := keys[entry]; !value {
|
||||
keys[entry] = true
|
||||
list = append(list, entry)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// helper.go : Main sources driver. Contains helper functions for other sources.
|
||||
// Written By : @ice3man (Nizamul Rana)
|
||||
//
|
||||
// Distributed Under MIT License
|
||||
// Copyrights (C) 2018 Ice3man
|
||||
//
|
||||
|
||||
package helper
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"crypto/tls"
|
||||
"time"
|
||||
)
|
||||
|
||||
//
|
||||
// GetHTTPResponse : Returns a HTTP Response object
|
||||
// @param url : URL To Visit (Note, It needs full url with scheme)
|
||||
// @param timeout : Seconds to wait for response until timeout
|
||||
//
|
||||
// @return resp : HTTP Response object
|
||||
// @return err : nil if successfull else error
|
||||
//
|
||||
func GetHTTPResponse(url string, timeout int) (resp *http.Response, err error) {
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: tr,
|
||||
Timeout: time.Duration(timeout) * time.Second,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// TODO : Figure out a way to handle user agents as per user intention
|
||||
// @codingo, I don't think it's correct to spam services by making requests with fake user agent
|
||||
// What do you think
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008071615 Fedora/3.0.1-1.fc9 Firefox/3.0.1")
|
||||
req.Header.Add("Connection", "close")
|
||||
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// state.go : Contains current program state
|
||||
// Written By : @codingo (Michael)
|
||||
// @ice3man (Nizamul Rana)
|
||||
//
|
||||
// Distributed Under MIT License
|
||||
// Copyrights (C) 2018 Ice3man
|
||||
//
|
||||
|
||||
package helper
|
||||
|
||||
// Holds the State read in from the CLI
|
||||
type State struct {
|
||||
Color bool // Whether to use color or not
|
||||
Threads int // Number of threads to use
|
||||
Verbose bool // Show verbose information
|
||||
Domain string // Domain name to find subdomains for
|
||||
Recursive bool // Whether perform recursive subdomain discovery or not
|
||||
|
||||
ConfigState Config // Current configuration file state
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
VirustotalAPIKey string `json:"virustotalapikey"` // Virustotal API Key
|
||||
}
|
||||
|
||||
func InitState() (state State, err error) {
|
||||
|
||||
// Read the configuration file
|
||||
config, err := ReadConfigFile()
|
||||
if err != nil {
|
||||
return state, err
|
||||
}
|
||||
|
||||
return State{true, 10, false, "", false, *config}, nil
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// certspotter.go : A Golang based client for Certspotter Parsing
|
||||
// Written By : @ice3man (Nizamul Rana)
|
||||
//
|
||||
// Distributed Under MIT License
|
||||
// Copyrights (C) 2018 Ice3man
|
||||
//
|
||||
|
||||
package certspotter
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"fmt"
|
||||
|
||||
"subfinder/libsubfinder/helper"
|
||||
)
|
||||
|
||||
// Structure of a single dictionary of output by crt.sh
|
||||
type certspotter_object struct {
|
||||
Dns_names []string `json:"dns_names"`
|
||||
}
|
||||
|
||||
// array of all results returned
|
||||
var certspotter_data []certspotter_object
|
||||
|
||||
//
|
||||
// Query : Queries awesome Certspotter service for subdomains
|
||||
// @param state : current application state, holds all information found
|
||||
//
|
||||
// @return subdomain : String array containing subdomains found
|
||||
// @return err : nil if successfull and error if failed
|
||||
//
|
||||
func Query(state *helper.State) (subdomains []string, err error) {
|
||||
|
||||
// Make a http request to Certspotter
|
||||
resp, err := helper.GetHTTPResponse("https://certspotter.com/api/v0/certs?domain="+state.Domain, 3000)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Get the response body
|
||||
resp_body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Decode the json format
|
||||
err = json.Unmarshal([]byte(resp_body), &certspotter_data)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Append each subdomain found to subdomains array
|
||||
for _, block := range certspotter_data {
|
||||
for _, dns_name := range block.Dns_names {
|
||||
|
||||
// Fix Wildcard subdomains containg asterisk before them
|
||||
if strings.Contains(dns_name, "*.") {
|
||||
dns_name = strings.Split(dns_name, "*.")[1]
|
||||
}
|
||||
|
||||
if state.Verbose == true {
|
||||
if state.Color == true {
|
||||
fmt.Printf("\n[\033[31;1;4mCERTSPOTTER\033[0m] %s", dns_name)
|
||||
} else {
|
||||
fmt.Printf("\n[CERTSPOTTER] %s", dns_name)
|
||||
}
|
||||
}
|
||||
|
||||
subdomains = append(subdomains, dns_name)
|
||||
}
|
||||
}
|
||||
|
||||
return subdomains, nil
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// crtsh.go : A Golang based client for CRT.SH Parsing
|
||||
// Written By : @ice3man (Nizamul Rana)
|
||||
//
|
||||
// Distributed Under MIT License
|
||||
// Copyrights (C) 2018 Ice3man
|
||||
//
|
||||
|
||||
package crtsh
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"subfinder/libsubfinder/helper"
|
||||
)
|
||||
|
||||
// Structure of a single dictionary of output by crt.sh
|
||||
// We only need name_value object hence this :-)
|
||||
type crtsh_object struct {
|
||||
Name_value string `json:"name_value"`
|
||||
}
|
||||
|
||||
// array of all results returned
|
||||
var crtsh_data []crtsh_object
|
||||
|
||||
//
|
||||
// Query : Queries awesome crt.sh service by comodo
|
||||
// @param state : current application state, holds all information found
|
||||
//
|
||||
// @return subdomain : String array containing subdomains found
|
||||
// @return err : nil if successfull and error if failed
|
||||
//
|
||||
func Query(state *helper.State) (subdomains []string, err error) {
|
||||
|
||||
// Make a http request to CRT.SH server and request output in JSON
|
||||
// format.
|
||||
// I Think 5 minutes would be more than enough for CRT.SH :-)
|
||||
resp, err := helper.GetHTTPResponse("https://crt.sh/?q=%25."+state.Domain+"&output=json", 3000)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Get the response body
|
||||
resp_body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Convert Response Body to string and then replace }{ to },{
|
||||
// This is done in order to enable parsing of JSON format employed by
|
||||
// crt.sh
|
||||
correct_format := strings.Replace(string(resp_body), "}{", "},{", -1)
|
||||
|
||||
// Now convert it into a json array like this
|
||||
// [
|
||||
// {abc},
|
||||
// {abc}
|
||||
// ]
|
||||
json_output := "["+correct_format+"]"
|
||||
|
||||
// Decode the json format
|
||||
err = json.Unmarshal([]byte(json_output), &crtsh_data)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Append each subdomain found to subdomains array
|
||||
for _, subdomain := range crtsh_data {
|
||||
|
||||
// Fix Wildcard subdomains containg asterisk before them
|
||||
if strings.Contains(subdomain.Name_value, "*.") {
|
||||
subdomain.Name_value = strings.Split(subdomain.Name_value, "*.")[1]
|
||||
}
|
||||
|
||||
if state.Verbose == true {
|
||||
if state.Color == true {
|
||||
fmt.Printf("\n[\033[31;1;4mCRT.SH\033[0m] %s", subdomain.Name_value)
|
||||
} else {
|
||||
fmt.Printf("\n[CRT.SH] %s", subdomain.Name_value)
|
||||
}
|
||||
}
|
||||
|
||||
subdomains = append(subdomains, subdomain.Name_value)
|
||||
}
|
||||
|
||||
return subdomains, nil
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// netcraft.go : Netcraft Scraping Engine in Golang
|
||||
// Written By : @ice3man (Nizamul Rana)
|
||||
//
|
||||
// Distributed Under MIT License
|
||||
// Copyrights (C) 2018 Ice3man
|
||||
//
|
||||
|
||||
package threatcrowd
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"fmt"
|
||||
|
||||
"subfinder/libsubfinder/helper"
|
||||
)
|
||||
|
||||
// Struct containing json data we actually need
|
||||
type netcraft_object struct {
|
||||
Subdomains []string `json:"subdomains"`
|
||||
}
|
||||
|
||||
// array of all results returned
|
||||
var netcraft_data netcraft_object
|
||||
|
||||
//
|
||||
// Query : Queries awesome Netcraft service for subdomains
|
||||
// @param state : current application state, holds all information found
|
||||
//
|
||||
// @return subdomain : String array containing subdomains found
|
||||
// @return err : nil if successfull and error if failed
|
||||
//
|
||||
func Query(state *helper.State) (subdomains []string, err error) {
|
||||
|
||||
// Make a http request to Netcraft
|
||||
resp, err := helper.GetHTTPResponse("https://searchdns.netcraft.com/?restriction=site+ends+with&host="+state.Domain+"&lookup=wait..&position=limited", 3000)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Get the response body
|
||||
resp_body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Decode the json format
|
||||
err = json.Unmarshal([]byte(resp_body), &threatcrowd_data)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Append each subdomain found to subdomains array
|
||||
for _, subdomain := range threatcrowd_data.Subdomains {
|
||||
|
||||
// Fix Wildcard subdomains containg asterisk before them
|
||||
if strings.Contains(subdomain, "*.") {
|
||||
subdomain = strings.Split(subdomain, "*.")[1]
|
||||
}
|
||||
|
||||
if state.Verbose == true {
|
||||
if state.Color == true {
|
||||
fmt.Printf("\n[\033[31;1;4mTHREATCROWD\033[0m] %s", subdomain)
|
||||
} else {
|
||||
fmt.Printf("\n[THREATCROWD] %s", subdomain)
|
||||
}
|
||||
}
|
||||
|
||||
subdomains = append(subdomains, subdomain)
|
||||
}
|
||||
|
||||
return subdomains, nil
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// threatcrowd.go : A Golang based client for Threatcrowd API
|
||||
// Written By : @ice3man (Nizamul Rana)
|
||||
//
|
||||
// Distributed Under MIT License
|
||||
// Copyrights (C) 2018 Ice3man
|
||||
//
|
||||
|
||||
package threatcrowd
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"fmt"
|
||||
|
||||
"subfinder/libsubfinder/helper"
|
||||
)
|
||||
|
||||
// Struct containing json data we actually need
|
||||
type threatcrowd_object struct {
|
||||
Subdomains []string `json:"subdomains"`
|
||||
}
|
||||
|
||||
// array of all results returned
|
||||
var threatcrowd_data threatcrowd_object
|
||||
|
||||
//
|
||||
// Query : Queries awesome ThreatCrowd service for subdomains
|
||||
// @param state : current application state, holds all information found
|
||||
//
|
||||
// @return subdomain : String array containing subdomains found
|
||||
// @return err : nil if successfull and error if failed
|
||||
//
|
||||
func Query(state *helper.State) (subdomains []string, err error) {
|
||||
|
||||
// Make a http request to Threatcrowd
|
||||
resp, err := helper.GetHTTPResponse("https://www.threatcrowd.org/searchApi/v2/domain/report/?domain="+state.Domain, 3000)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Get the response body
|
||||
resp_body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Decode the json format
|
||||
err = json.Unmarshal([]byte(resp_body), &threatcrowd_data)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Append each subdomain found to subdomains array
|
||||
for _, subdomain := range threatcrowd_data.Subdomains {
|
||||
|
||||
// Fix Wildcard subdomains containg asterisk before them
|
||||
if strings.Contains(subdomain, "*.") {
|
||||
subdomain = strings.Split(subdomain, "*.")[1]
|
||||
}
|
||||
|
||||
if state.Verbose == true {
|
||||
if state.Color == true {
|
||||
fmt.Printf("\n[\033[31;1;4mTHREATCROWD\033[0m] %s", subdomain)
|
||||
} else {
|
||||
fmt.Printf("\n[THREATCROWD] %s", subdomain)
|
||||
}
|
||||
}
|
||||
|
||||
subdomains = append(subdomains, subdomain)
|
||||
}
|
||||
|
||||
return subdomains, nil
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// virustotal.go : A Virustotal Client for Subdomain Enumeration
|
||||
// Written By : @ice3man (Nizamul Rana)
|
||||
//
|
||||
// Distributed Under MIT License
|
||||
// Copyrights (C) 2018 Ice3man
|
||||
//
|
||||
|
||||
// NOTE : We are using Virustotal API here Since we wanted to eliminate the
|
||||
// rate limiting performed by Virustotal on scraping.
|
||||
// Direct queries and parsing can be also done :-)
|
||||
|
||||
package virustotal
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"subfinder/libsubfinder/helper"
|
||||
)
|
||||
|
||||
type virustotalapi_object struct {
|
||||
Subdomains []string `json:"subdomains"`
|
||||
}
|
||||
|
||||
var virustotalapi_data virustotalapi_object
|
||||
|
||||
//
|
||||
// Local function to query virustotal API
|
||||
// Requires an API key
|
||||
//
|
||||
// @note : If the user specifies an API key in config.json, we use API
|
||||
// If not, we try to scrape pages though it is highly discouraged
|
||||
//
|
||||
func queryVirustotalApi(state *helper.State) (subdomains []string, err error) {
|
||||
|
||||
// Make a search for a domain name and get HTTP Response
|
||||
resp, err := helper.GetHTTPResponse("https://www.virustotal.com/vtapi/v2/domain/report?apikey="+state.ConfigState.VirustotalAPIKey+"&domain="+state.Domain, 3000)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Get the response body
|
||||
resp_body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Decode the json format
|
||||
err = json.Unmarshal([]byte(resp_body), &virustotalapi_data)
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
|
||||
// Append each subdomain found to subdomains array
|
||||
for _, subdomain := range virustotalapi_data.Subdomains {
|
||||
|
||||
// Fix Wildcard subdomains containg asterisk before them
|
||||
if strings.Contains(subdomain, "*.") {
|
||||
subdomain = strings.Split(subdomain, "*.")[1]
|
||||
}
|
||||
|
||||
if state.Verbose == true {
|
||||
if state.Color == true {
|
||||
fmt.Printf("\n[\033[31;1;4mVIRUSTOTAL\033[0m] %s", subdomain)
|
||||
} else {
|
||||
fmt.Printf("\n[VIRUSTOTAL] %s", subdomain)
|
||||
}
|
||||
}
|
||||
|
||||
subdomains = append(subdomains, subdomain)
|
||||
}
|
||||
|
||||
return subdomains, nil
|
||||
}
|
||||
|
||||
//
|
||||
// Query : Queries awesome Virustotal Service for Subdomains
|
||||
// @param state : Current application state
|
||||
//
|
||||
// @return subdomain : String array containing subdomains found
|
||||
// @return err : nil if successfull and error if failed
|
||||
//
|
||||
func Query(state *helper.State) (subdomains []string, err error) {
|
||||
|
||||
// We have recieved an API Key
|
||||
// Now, we will use Virustotal API key to fetch subdomain info
|
||||
if state.ConfigState.VirustotalAPIKey != "" {
|
||||
|
||||
// Get subdomains via API
|
||||
subdomains, err = queryVirustotalApi(state)
|
||||
|
||||
if err != nil {
|
||||
return subdomains, err
|
||||
}
|
||||
}
|
||||
|
||||
return subdomains, nil
|
||||
}
|
Loading…
Reference in New Issue