Skip to content

Commit

Permalink
Merge pull request #16 from Escape-Technologies/feat/addresses-metadata
Browse files Browse the repository at this point in the history
Feat/addresses metadata
  • Loading branch information
nohehf authored Apr 24, 2023
2 parents 2672957 + db35acc commit ada629e
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 19 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ testers
/goctopus

dist/
.env
.env

# root go files (often used for testing / prototyping)
/*.go
28 changes: 24 additions & 4 deletions pkg/address/address.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package address

import "strings"

type Addr struct {
Address string
Source string
Address string
Source string
Metadata map[string]string
}

func New(address string) *Addr {
return &Addr{
Address: address,
Source: address,
Address: address,
Source: address,
Metadata: map[string]string{},
}
}

Expand All @@ -19,3 +23,19 @@ func NewSourced(address, source string) *Addr {
Source: source,
}
}

func (a *Addr) AddMetadata(key, value string) {
a.Metadata[key] = value
}

func (a *Addr) Copy() *Addr {
metadataCopy := make(map[string]string)
for k, v := range a.Metadata {
metadataCopy[k] = v
}
return &Addr{
Address: strings.Clone(a.Address),
Source: strings.Clone(a.Source),
Metadata: metadataCopy,
}
}
11 changes: 7 additions & 4 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func LoadFromArgs() {
log.SetLevel(log.ErrorLevel)
}

if err := ValidateConfig(&config); err != nil {
if err := validateConfig(&config, true); err != nil {
log.Error(err)
flag.PrintDefaults()
os.Exit(1)
Expand All @@ -85,7 +85,10 @@ func LoadFromArgs() {
c = &config
}

func ValidateConfig(conf *Config) error {
// Validates the config.
// `cli` is used to determine if the config is loaded from the CLI or from a file.
// If cli is false, then the addresses check is skipped.
func validateConfig(conf *Config, isCli bool) error {
if conf.MaxWorkers < 1 {
return errors.New("[Invalid config] Max workers must be greater than 0")
}
Expand All @@ -98,15 +101,15 @@ func ValidateConfig(conf *Config) error {
return errors.New("[Invalid config] Introspection has to be enabled to use field suggestion fingerprinting")
}

if conf.InputFile == "" && len(conf.Addresses) == 0 {
if isCli && c.InputFile == "" && len(c.Addresses) == 0 {
return errors.New("[Invalid config] Please specify an input file or a list of addresses")
}

return nil
}

func Load(config *Config) {
if err := ValidateConfig(config); err != nil {
if err := validateConfig(config, false); err != nil {
log.Error(err)
flag.PrintDefaults()
os.Exit(1)
Expand Down
4 changes: 3 additions & 1 deletion pkg/domain/enumeration.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import (

func makeCallback(domain *address.Addr, subDomains chan *address.Addr) func(s *resolve.HostEntry) {
return func(s *resolve.HostEntry) {
subDomains <- address.NewSourced(s.Host, domain.Source)
addr := domain.Copy()
addr.Address = s.Host
subDomains <- addr
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/domain/fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func FingerprintSubDomain(domain *address.Addr) (*output.FingerprintOutput, erro
}
output.Domain = domain.Address
output.Source = domain.Source
output.Metadata = domain.Metadata
return output, nil
}
return nil, errors.New("no graphql endpoint found")
Expand Down
9 changes: 6 additions & 3 deletions pkg/goctopus/fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func worker(addresses chan *address.Addr, output chan *output.FingerprintOutput,
log.Debugf("Worker %d instantiated", workerId)
for address := range addresses {
log.Debugf("Worker %d started on: %v", workerId, address)
res, err := FingerprintAddress(address)
res, err := fingerprintAddress(address)
if err == nil {
log.Debugf("Worker %d found endpoint: %v", workerId, res)
output <- res
Expand All @@ -26,7 +26,10 @@ func worker(addresses chan *address.Addr, output chan *output.FingerprintOutput,
log.Debugf("Worker %d finished", workerId)
}

func FingerprintAddress(address *address.Addr) (*output.FingerprintOutput, error) {
/**
* Fingerprint an address, without subdomain enumeration
*/
func fingerprintAddress(address *address.Addr) (*output.FingerprintOutput, error) {
// If the domain is a url, we don't need to crawl it
if utils.IsUrl(address.Address) {
return endpoint.FingerprintEndpoint(address)
Expand Down Expand Up @@ -65,6 +68,6 @@ func FingerprintAddresses(addresses chan *address.Addr, output chan *output.Fing
close(enumeratedAddresses)
log.Debugf("Waiting for workers to finish...")
workersWg.Wait()
close(output)
log.Debugf("All workers finished")
close(output)
}
20 changes: 20 additions & 0 deletions pkg/goctopus/single_address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package goctopus

import (
"github.com/Escape-Technologies/goctopus/pkg/address"
out "github.com/Escape-Technologies/goctopus/pkg/output"
)

// Fingerprints a single address and outputs a slice of FingerprintOutput.
func FingerprintAddress(addr *address.Addr) []*out.FingerprintOutput {
outputSlice := make([]*out.FingerprintOutput, 0)
output := make(chan *out.FingerprintOutput, 1)
addresses := make(chan *address.Addr, 1)
addresses <- addr
close(addresses)
go FingerprintAddresses(addresses, output)
for fingerprintOutput := range output {
outputSlice = append(outputSlice, fingerprintOutput)
}
return outputSlice
}
8 changes: 4 additions & 4 deletions pkg/output/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ func openOutputFile(config *config.Config) (*os.File, error) {
}

func handleSingleOutput(output *FingerprintOutput, outputFile *os.File, wg *sync.WaitGroup, config *config.Config) {
isOutputFile := config.OutputFile != ""
isWebhook := config.WebhookUrl != ""
hasOutputFile := config.OutputFile != ""
hasWebhook := config.WebhookUrl != ""

jsonOutput, err := json.Marshal(output)
log.Infof("Found: %+v\n", string(jsonOutput))
if err != nil {
log.Error(err)
}

if isOutputFile {
if hasOutputFile {
content := append(jsonOutput, []byte("\n")...)
if _, err := outputFile.Write(content); err != nil {
log.Error(err)
}
}

if isWebhook {
if hasWebhook {
wg.Add(1)
go func() {
if err := http.SendToWebhook(config.WebhookUrl, jsonOutput, wg); err != nil {
Expand Down
8 changes: 6 additions & 2 deletions pkg/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ type FingerprintResult string
const (
ResultOpenGraphql FingerprintResult = "OPEN_GRAPHQL"
ResultAuthentifiedGraphql FingerprintResult = "AUTHENTIFIED_GRAPHQL"
// ResultMaybeGraphql FingerprintResult = "MAYBE_GRAPHQL"
)

type FingerprintOutput struct {
Expand All @@ -21,7 +20,8 @@ type FingerprintOutput struct {
Url string `json:"url"`
Introspection bool `json:"introspection"`
FieldSuggestion bool `json:"field_suggestion"`
Source string `json:"source"` // the original address used to fingerprint the endpoint
Source string `json:"source"` // the original address used to fingerprint the endpoint
Metadata map[string]string `json:"metadata"` // optional metadata
}

func (o *FingerprintOutput) MarshalJSON() ([]byte, error) {
Expand Down Expand Up @@ -56,5 +56,9 @@ func marshalOutput(o *FingerprintOutput, c *config.Config) ([]byte, error) {
outputMap["domain"] = utils.DomainFromUrl(o.Url)
}

if o.Metadata == nil || len(o.Metadata) == 0 {
delete(outputMap, "metadata")
}

return json.Marshal(outputMap)
}

0 comments on commit ada629e

Please sign in to comment.