diff --git a/README.md b/README.md index 4b2064f..eb41372 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,11 @@ _(__ )_ / _ __/ [![Go Report Card](https://goreportcard.com/badge/github.com/dropalldatabases/sif)](https://goreportcard.com/report/github.com/dropalldatabases/sif) [![Version](https://img.shields.io/github/v/tag/dropalldatabases/sif)](https://github.com/dropalldatabases/sif/tags) [![Chat on Discord](https://img.shields.io/discord/1202922721969705010)](https://discord.gg/uzQv4YbJ8W) + ## Features + - 📂 Directory/file fuzzing/scanning - 📡 DNS subdomain enumeration - 🐾 Common Web scanning @@ -26,6 +28,7 @@ _(__ )_ / _ __/ - Metasploit emulation for execution - 🔎 Automated Google dorking - 💘 Shodan integration +- 📦 CMS detection ## Contributing and support diff --git a/pkg/config/config.go b/pkg/config/config.go index e8dadfd..b3c4aaf 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -25,6 +25,7 @@ type Settings struct { File string ApiMode bool Template string + CMS bool } const ( @@ -63,6 +64,7 @@ func Parse() *Settings { flagSet.BoolVar(&settings.NoScan, "noscan", false, "Do not perform base URL (robots.txt, etc) scanning"), flagSet.BoolVar(&settings.Whois, "whois", false, "Enable WHOIS lookup"), flagSet.BoolVar(&settings.JavaScript, "js", false, "Enable JavaScript scans"), + flagSet.BoolVar(&settings.CMS, "cms", false, "Enable CMS detection"), ) flagSet.CreateGroup("runtime", "Runtime", diff --git a/pkg/scan/cms.go b/pkg/scan/cms.go new file mode 100644 index 0000000..f9e7a4b --- /dev/null +++ b/pkg/scan/cms.go @@ -0,0 +1,112 @@ +package scan + +import ( + "fmt" + "io" + "net/http" + "strings" + "time" + "os" + + "github.com/charmbracelet/log" + "github.com/dropalldatabases/sif/internal/styles" + "github.com/dropalldatabases/sif/pkg/logger" +) + +type CMSResult struct { + Name string `json:"name"` + Version string `json:"version"` +} + +func CMS(url string, timeout time.Duration, logdir string) (*CMSResult, error) { + fmt.Println(styles.Separator.Render("🔍 Starting " + styles.Status.Render("CMS detection") + "...")) + + sanitizedURL := strings.Split(url, "://")[1] + + if logdir != "" { + if err := logger.WriteHeader(sanitizedURL, logdir, "CMS detection"); err != nil { + log.Errorf("Error creating log file: %v", err) + return nil, err + } + } + + cmslog := log.NewWithOptions(os.Stderr, log.Options{ + Prefix: "CMS 🔍", + }).With("url", url) + + + client := &http.Client{ + Timeout: timeout, + } + + resp, err := client.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + bodyString := string(body) + + // WordPress + if detectWordPress(url, client, bodyString) { + result := &CMSResult{Name: "WordPress", Version: "Unknown"} + cmslog.Infof("Detected CMS: %s", styles.Highlight.Render(result.Name)) + return result, nil + } + + // Drupal + if strings.Contains(resp.Header.Get("X-Drupal-Cache"), "HIT") || strings.Contains(bodyString, "Drupal.settings") { + result := &CMSResult{Name: "Drupal", Version: "Unknown"} + cmslog.Infof("Detected CMS: %s", styles.Highlight.Render(result.Name)) + return result, nil + } + + // Joomla + if strings.Contains(bodyString, "joomla") || strings.Contains(bodyString, "/media/system/js/core.js") { + result := &CMSResult{Name: "Joomla", Version: "Unknown"} + cmslog.Infof("Detected CMS: %s", styles.Highlight.Render(result.Name)) + return result, nil + } + + cmslog.Info("No CMS detected") + return nil, nil +} + +func detectWordPress(url string, client *http.Client, bodyString string) bool { + // Check for common WordPress indicators in the HTML + wpIndicators := []string{ + "wp-content", + "wp-includes", + "wp-json", + "wordpress", + } + + for _, indicator := range wpIndicators { + if strings.Contains(bodyString, indicator) { + return true + } + } + + // Check for WordPress-specific files + wpFiles := []string{ + "/wp-login.php", + "/wp-admin/", + "/wp-config.php", + } + + for _, file := range wpFiles { + resp, err := client.Get(url + file) + if err == nil { + defer resp.Body.Close() + if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusFound { + return true + } + } + } + + return false +} diff --git a/sif.go b/sif.go index 220404f..0ef5da0 100644 --- a/sif.go +++ b/sif.go @@ -166,6 +166,15 @@ func (app *App) Run() error { } } + if app.settings.CMS { + result, err := scan.CMS(url, app.settings.Timeout, app.settings.LogDir) + if err != nil { + log.Errorf("Error while running CMS detection: %s", err) + } else if result != nil { + moduleResults = append(moduleResults, ModuleResult{"cms", result}) + } + } + if app.settings.ApiMode { result := UrlResult{ Url: url, @@ -189,4 +198,4 @@ func (app *App) Run() error { } return nil -} +} \ No newline at end of file