5.4 KiB
SubFinder Documentation
👍🎉 First off, Many Thanks for taking the time to contribute! 🎉👍
The follwing is a document describing the project structure of SubFinder subdomain enumeration tool and teaching you how to contribute to it.
Project Structure
SubFinder is written in golang. The author has tried to keep the code as simple and modular as possible. There can be flaws in the golang implementation or there may be many things which can be done better. Please create an issue with the details and we would be more than happy to implement your suggestions. Pull Requests are more appreciated 👍.
Comments can be found throughout the majority of the code base explaining functionality of usage. The codebase is pretty simple and modular and you can easily understand the structure and contribute to the codebase.
Adding new Passive Sources
Passive Sources are data sources which can be used to find subdomains without directly connecting to the site. It can easily fetch many subdomains which cannot be found through plain bruteforcing and alterations. SubFinder uses multiple passive sources which are organized in github.com/ice3man543/subfinder/libsubfinder/sources
. Each passive source has it's own directory which contains a subpackage under the package sources. Each passive source must export a query function. This allows the code to retain a similar structure throughout the tool.
- Create a directory
github.com/ice3man543/subfinder/libsubfinder/sources/example
- Create a file called example.go in
example
directory.
Here is a boilerplate code for a passive package called example.
package example // The first line contains package name, like example
import (
"github.com/ice3man543/subfinder/libsubfinder/helper" // Obligatory, Contains helper class
)
// Contains all subdomains found
var subdomains []string
//
// Query : Each passive package exports a Query function
// which returns all results found.
//
// @param state : Current application state
// @param ch : Channel for our result structure
func Query(state *helper.State, ch chan helper.Result) {
// Perform all the operations here
...
// In case of error handling, return error
if err != nil {
result.Error = err
ch <- result
return
}
for _, found := range block.Dns_names {
if state.Verbose == true {
if state.Color == true {
fmt.Printf("\n[%sEXAMPLE%s] %s", helper.Red, helper.Reset, found)
} else {
fmt.Printf("\n[EXAMPLE] %s", found)
}
}
subdomains = append(subdomains, found)
}
}
// Return success result
result.Subdomains = subdomains
result.Error = nil
ch <-result
}
Some notes :
- A Package can have as many functions as you want. But all functionality must be exported by the Query function itself.
- The helper class exports a function
helper.GetHTTPResponse("https://example.com?domain="+state.Domain, state.Timeout)
which can help with HTTP requests. - We have been keeping Package count almost to none. Please use
Regexp
package or native Golang HTML parser.
After you have created your package, it's time to import it. All logic for passive sources is in github.com/ice3man543/subfinder/libsubfinder/engines/passive/passive.go
.
- Import your custom package in passive.go.
package passive
import (
...
// Load different Passive data sources
...
"github.com/ice3man543/subfinder/libsubfinder/sources/example"
)
- Add your data source to Source struct at the last of all sources before NoOfSources.
type Source struct {
...
Example bool // Your example data source
NoOfSources int
}
func PassiveDiscovery(state *helper.State) (finalPassiveSubdomains []string) {
- Add a false statement to your sourceConfig object at the last before NoOfSources.
func PassiveDiscovery(state *helper.State) (finalPassiveSubdomains []string) {
sourceConfig := Source{... false, 0}
- Add a print statement to all data sources if-block. Also add a true statement to sourceConfig assignment and also increment NoOfSources field by 1.
if state.Sources == "all" {
// Search all data sources
...
fmt.Printf("\n[-] Searching For Subdomains in Example")
...
sourceConfig = Source{... true, 13}
- Add an else-if block to the custom source config block.
else {
// Check data sources and create a source configuration structure
dataSources := strings.Split(state.Sources, ",")
for _, source := range dataSources {
if source == "crtsh" {
fmt.Printf("\n[-] Searching For Subdomains in Crt.sh")
sourceConfig.Crtsh = true
sourceConfig.NoOfSources = sourceConfig.NoOfSources + 1
...
} else if source == "example" {
fmt.Printf("\n[-] Searching For Subdomains in Example")
sourceConfig.Example = true
sourceConfig.NoOfSources = sourceConfig.NoOfSources + 1
}
}
- Add an if-block for main goroutine execution.
if sourceConfig.Virustotal == true { go virustotal.Query(state, ch) }
if sourceConfig.Example == true { go example.Query(state, ch) }
And finally, you have created your own custom passive source.
Notes
This document is currently in development like this project. If there are any mistakes or questions, feel free to create an issue or contact me directly on twitter or email. Have fun developing it. If you do create something awesome, do share it and we will add your name to the Acknowledgements section.
Written by : @Ice3man