mirror of
https://github.com/anotherhadi/iknowyou.git
synced 2026-04-12 00:47:26 +02:00
134 lines
3.9 KiB
Go
134 lines
3.9 KiB
Go
package ghunt
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/anotherhadi/iknowyou/internal/tools"
|
|
)
|
|
|
|
var ansiRe = regexp.MustCompile(`\x1b[\x5b-\x5f][0-9;]*[A-Za-z]|\x1b[^[\x5b-\x5f]`)
|
|
|
|
const (
|
|
name = "ghunt"
|
|
description = "Google account OSINT via GHunt. Extracts profile info, linked services, and activity from a Google email address."
|
|
link = "https://github.com/mxrch/GHunt"
|
|
icon = "google"
|
|
)
|
|
|
|
type Config struct {
|
|
Creds string `yaml:"creds" iky:"desc=GHunt credentials (content of ~/.malfrats/ghunt/creds.m). To obtain: (1) install GHunt and run 'ghunt login' on your machine, (2) copy the full content of ~/.malfrats/ghunt/creds.m, (3) paste it here.;required=true"`
|
|
}
|
|
|
|
type Runner struct {
|
|
cfg Config
|
|
}
|
|
|
|
func New() tools.ToolRunner {
|
|
cfg := Config{}
|
|
tools.ApplyDefaults(&cfg)
|
|
return &Runner{cfg: cfg}
|
|
}
|
|
|
|
func (r *Runner) Name() string { return name }
|
|
func (r *Runner) Description() string { return description }
|
|
func (r *Runner) Link() string { return link }
|
|
func (r *Runner) Icon() string { return icon }
|
|
|
|
func (r *Runner) InputTypes() []tools.InputType {
|
|
return []tools.InputType{
|
|
tools.InputTypeEmail,
|
|
}
|
|
}
|
|
|
|
func (r *Runner) ConfigPtr() interface{} { return &r.cfg }
|
|
|
|
func (r *Runner) ConfigFields() []tools.ConfigField {
|
|
return tools.ReflectConfigFields(r.cfg)
|
|
}
|
|
|
|
func (r *Runner) Available() (bool, string) {
|
|
if _, err := exec.LookPath("ghunt"); err != nil {
|
|
return false, "ghunt binary not found in PATH"
|
|
}
|
|
return true, ""
|
|
}
|
|
|
|
func (r *Runner) Dependencies() []string { return []string{"ghunt"} }
|
|
|
|
func (r *Runner) writeCreds() error {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return fmt.Errorf("cannot determine home directory: %w", err)
|
|
}
|
|
dir := filepath.Join(home, ".malfrats", "ghunt")
|
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
|
return fmt.Errorf("cannot create ghunt dir: %w", err)
|
|
}
|
|
return os.WriteFile(filepath.Join(dir, "creds.m"), []byte(r.cfg.Creds), 0600)
|
|
}
|
|
|
|
func (r *Runner) Run(ctx context.Context, target string, _ tools.InputType, out chan<- tools.Event) error {
|
|
defer close(out)
|
|
|
|
if err := r.writeCreds(); err != nil {
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeError, Payload: err.Error()}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeDone}
|
|
return nil
|
|
}
|
|
|
|
cmd := exec.CommandContext(ctx, "ghunt", "email", target)
|
|
output, err := tools.RunWithPTY(ctx, cmd)
|
|
|
|
output = strings.ReplaceAll(output, "\r\n", "\n")
|
|
output = strings.ReplaceAll(output, "\r", "\n")
|
|
|
|
lines := strings.Split(output, "\n")
|
|
parsed := make([]string, len(lines))
|
|
for i, l := range lines {
|
|
parsed[i] = ansiRe.ReplaceAllString(l, "")
|
|
}
|
|
|
|
start := -1
|
|
for i, l := range parsed {
|
|
if strings.Contains(l, "[+] Authenticated !") {
|
|
start = i + 1
|
|
break
|
|
}
|
|
}
|
|
|
|
if start == -1 {
|
|
// Banner printed but auth line never appeared — bad/expired credentials.
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeError, Payload: "GHunt authentication failed — credentials may be missing or expired (run 'ghunt login' and update your creds in Settings)"}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeCount, Payload: 0}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeDone}
|
|
return nil
|
|
}
|
|
|
|
end := len(lines)
|
|
for i := start; i < len(parsed); i++ {
|
|
if strings.Contains(parsed[i], "Traceback (most recent call last)") {
|
|
end = i
|
|
break
|
|
}
|
|
}
|
|
|
|
output = strings.TrimSpace(strings.Join(lines[start:end], "\n"))
|
|
|
|
if err != nil && ctx.Err() != nil {
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeError, Payload: "scan cancelled"}
|
|
} else if output != "" {
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeOutput, Payload: output}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeCount, Payload: 1}
|
|
} else {
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeCount, Payload: 0}
|
|
}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeDone}
|
|
return nil
|
|
}
|