Skip to content

Commit

Permalink
projectdiscovery#408 Add fofa for SubFinder
Browse files Browse the repository at this point in the history
  • Loading branch information
Becivells committed May 17, 2021
1 parent 1c17b7f commit e85de39
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 0 deletions.
4 changes: 4 additions & 0 deletions v2/pkg/passive/sources.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/projectdiscovery/subfinder/v2/pkg/subscraping/sources/crtsh"
"github.com/projectdiscovery/subfinder/v2/pkg/subscraping/sources/dnsdb"
"github.com/projectdiscovery/subfinder/v2/pkg/subscraping/sources/dnsdumpster"
"github.com/projectdiscovery/subfinder/v2/pkg/subscraping/sources/fofa"
"github.com/projectdiscovery/subfinder/v2/pkg/subscraping/sources/github"
"github.com/projectdiscovery/subfinder/v2/pkg/subscraping/sources/hackertarget"
"github.com/projectdiscovery/subfinder/v2/pkg/subscraping/sources/intelx"
Expand Down Expand Up @@ -110,6 +111,7 @@ var DefaultAllSources = []string{
"virustotal",
"waybackarchive",
"zoomeye",
"fofa",
}

// Agent is a struct for running passive subdomain enumeration
Expand Down Expand Up @@ -198,6 +200,8 @@ func (a *Agent) addSources(sources []string) {
a.sources[source] = &waybackarchive.Source{}
case "zoomeye":
a.sources[source] = &zoomeye.Source{}
case "fofa":
a.sources[source] = &fofa.Source{}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions v2/pkg/runner/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type ConfigFile struct {
URLScan []string `yaml:"urlscan"`
Virustotal []string `yaml:"virustotal"`
ZoomEye []string `yaml:"zoomeye"`
Fofa []string `yaml:"fofa"`
// Version indicates the version of subfinder installed.
Version string `yaml:"subfinder-version"`
}
Expand Down Expand Up @@ -194,6 +195,14 @@ func (c *ConfigFile) GetKeys() subscraping.Keys {
keys.ZoomEyePassword = parts[1]
}
}
if len(c.Fofa) > 0 {
fofaKeys := c.Fofa[rand.Intn(len(c.Fofa))]
parts := strings.Split(fofaKeys, ":")
if len(parts) == MultipleKeyPartsLength {
keys.FofaUsername = parts[0]
keys.FofaSecret = parts[1]
}
}

return keys
}
74 changes: 74 additions & 0 deletions v2/pkg/subscraping/sources/fofa/fofa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Package fofa logic
package fofa

import (
"context"
"encoding/base64"
"fmt"
"strings"

jsoniter "github.com/json-iterator/go"
"github.com/projectdiscovery/subfinder/v2/pkg/subscraping"
)

type fofaResponse struct {
Error bool `json:"error"`
ErrMsg string `json:"errmsg"`
Size int `json:"size"`
Results []string `json:"results"`
}

// Source is the passive scraping agent
type Source struct{}

// Run function returns all subdomains found with the service
func (s *Source) Run(ctx context.Context, domain string, session *subscraping.Session) <-chan subscraping.Result {
results := make(chan subscraping.Result)

go func() {
defer close(results)

if session.Keys.FofaUsername == "" || session.Keys.FofaSecret == "" {
return
}

// fofa api doc https://fofa.so/static_pages/api_help
qbase64 := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("domain=\"%s\"", domain)))
resp, err := session.SimpleGet(ctx, fmt.Sprintf("https://fofa.so/api/v1/search/all?full=true&fields=host&page=1&size=10000&email=%s&key=%s&qbase64=%s", session.Keys.FofaUsername, session.Keys.FofaSecret, qbase64))
if err != nil && resp == nil {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
session.DiscardHTTPResponse(resp)
return
}

var response fofaResponse
err = jsoniter.NewDecoder(resp.Body).Decode(&response)
if err != nil {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
resp.Body.Close()
return
}
resp.Body.Close()

if response.Error {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: fmt.Errorf("%s", response.ErrMsg)}
return
}

if response.Size > 0 {
for _, subdomain := range response.Results {
if strings.HasPrefix(strings.ToLower(subdomain), "http://") || strings.HasPrefix(strings.ToLower(subdomain), "https://") {
subdomain = subdomain[strings.Index(subdomain, "//")+2:]
}
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Subdomain, Value: subdomain}
}
}
}()

return results
}

// Name returns the name of the source
func (s *Source) Name() string {
return "fofa"
}
2 changes: 2 additions & 0 deletions v2/pkg/subscraping/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type Keys struct {
Virustotal string `json:"virustotal"`
ZoomEyeUsername string `json:"zoomeye_username"`
ZoomEyePassword string `json:"zoomeye_password"`
FofaUsername string `json:"fofa_username"`
FofaSecret string `json:"fofa_password"`
}

// Result is a result structure returned by a source
Expand Down

0 comments on commit e85de39

Please sign in to comment.