aitm

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 24, 2026 License: GPL-3.0 Imports: 18 Imported by: 0

Documentation

Overview

Package aitm defines the central domain model for the mirage AiTM proxy.

It contains no business logic of its own — the proxy pipeline, API, and storage layers all depend on this package, so keeping it free of outward dependencies prevents import cycles.

Phishlet types

Phishlet is the unified type representing a phishlet. It combines two groups of fields that have different lifecycles:

  • Compiled rules (ProxyHosts, SubFilters, AuthTokens, etc.) are populated by the phishlet loader from the YAML file. They are never persisted.

  • Operator config (Hostname, BaseDomain, Enabled, etc.) is persisted to the database and survives restarts.

Either group may be zero-valued. A freshly loaded YAML has no operator config; a record loaded from the database has no compiled rules. The daemon merges both at startup and on every YAML reload.

Lures

A Lure is a configured phishing URL tied to a phishlet. It carries per-lure settings (redirect URL, user-agent filter, OpenGraph tags, pause schedule) and a 32-byte AES-256-GCM key used to encrypt custom parameters embedded in the phishing URL's ?p= query value.

Session lifecycle

A Session is created when a victim first hits a lure. It progresses through three observable states, each of which publishes an event to the [eventBus]:

  1. Created — [EventSessionCreated] — victim loaded the phishing page
  2. Creds — [EventCredsCaptured] — username/password captured from a POST
  3. Completed — [EventSessionCompleted] — all required auth tokens captured; Session.CompletedAt is set and the post-capture redirect fires

The proxy pipeline calls SessionService.IsComplete after every response to check whether all non-always TokenRule entries in the Phishlet have been satisfied. When they have, SessionService.Complete marks the session done and publishes [EventSessionCompleted].

Event bus

[eventBus] is the publish/subscribe interface that decouples the proxy pipeline from other components. The WebSocket hub subscribes to [EventSessionCompleted] to fan the redirect URL out to waiting browsers. Publish must never block; if a subscriber's channel is full the event is silently dropped to avoid stalling the proxy hot path.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidToken  = errors.New("invalid invite token")
	ErrInvalidCSR    = errors.New("invalid certificate signing request")
	ErrSigningFailed = errors.New("certificate signing failed")
)
View Source
var (
	ErrHostnameRequired = errors.New("hostname is required")
	ErrHostnameConflict = errors.New("hostname is already in use by another phishlet")
)
View Source
var ErrConflict = errors.New("conflict")
View Source
var ErrInvalidFilter = errors.New("invalid filter")
View Source
var ErrNotFound = errors.New("not found")
View Source
var ErrRecordExists = errors.New("dns: record already exists")
View Source
var ErrRecordNotFound = errors.New("dns: record not found")

Functions

func MatchesDomain

func MatchesDomain(cookieDomain, ruleDomain string) bool

MatchesDomain reports whether cookieDomain satisfies ruleDomain using the standard phishlet convention: a leading dot on ruleDomain means "this domain and all subdomains". Leading dots are stripped from both sides before comparison so the form returned by Go's cookie parser (no leading dot) matches phishlet rules that conventionally include one.

func SubscribeAndHandle added in v0.5.3

func SubscribeAndHandle(bus eventBus, eventType sdk.EventType, fn func(Event)) (unsubscribe func())

SubscribeAndHandle subscribes to eventType on bus and runs fn for each event received. It returns an unsubscribe function that terminates the subscription and blocks until the dispatch goroutine has fully exited — so callers can safely tear down resources fn touches (e.g. close a downstream channel) immediately after unsubscribe returns.

fn is called sequentially — concurrent calls from a single SubscribeAndHandle are not possible. For slow handlers, spawn a goroutine inside fn.

Types

type BlacklistService

type BlacklistService struct {
	// contains filtered or unexported fields
}

BlacklistService manages IP-based access control using an in-memory set. Persistence will be layered on in a later phase.

func NewBlacklistService

func NewBlacklistService() *BlacklistService

func (*BlacklistService) Block

func (s *BlacklistService) Block(ip string)

func (*BlacklistService) IsBlocked

func (s *BlacklistService) IsBlocked(ip string) bool

func (*BlacklistService) List

func (s *BlacklistService) List() []string

List returns blocked IPs in unspecified order.

func (*BlacklistService) Unblock

func (s *BlacklistService) Unblock(ip string)

func (*BlacklistService) WhitelistTemporary

func (s *BlacklistService) WhitelistTemporary(ip string, dur time.Duration)

WhitelistTemporary temporarily exempts ip from blocking. Called after token capture so the victim's browser isn't blocked during the post-auth redirect.

type BotDetectedPayload

type BotDetectedPayload struct {
	SessionID  string
	RemoteAddr string
	JA4Hash    string
	BotScore   float64
	Verdict    string // "spoof" or "block"
	Reason     string // e.g. "JA4 match: zgrab2"
}

BotDetectedPayload is the payload for EventBotDetected.

type BotGuardService

type BotGuardService struct {
	Scorer         botScorer
	Store          botTelemetryStore
	SignatureStore botSignatureStore
	Bus            eventBus
	// contains filtered or unexported fields
}

BotGuardService evaluates connections for bot/scanner signatures. L1 (JA4 signature lookup) is performed in-memory via signatures sync.Map. L2 (telemetry heuristic) is delegated to Scorer.

func (*BotGuardService) AddSignature

func (s *BotGuardService) AddSignature(sig BotSignature) error

AddSignature persists a new bot signature and adds it to the in-memory set.

func (*BotGuardService) Evaluate

func (s *BotGuardService) Evaluate(ja4 string, telemetry *BotTelemetry) BotVerdict

Evaluate checks L1 (in-memory JA4 signature) then L2 (telemetry heuristic).

func (*BotGuardService) ListSignatures

func (s *BotGuardService) ListSignatures() ([]BotSignature, error)

ListSignatures returns all persisted bot signatures.

func (*BotGuardService) LoadSignaturesFromDB

func (s *BotGuardService) LoadSignaturesFromDB() error

LoadSignaturesFromDB populates the in-memory signature set from the database. Call once at startup before serving requests.

func (*BotGuardService) RemoveSignature

func (s *BotGuardService) RemoveSignature(ja4Hash string) error

RemoveSignature deletes a bot signature from the database and in-memory set. Returns aitm.ErrNotFound if no signature with the given hash exists.

func (*BotGuardService) ScoreSession

func (s *BotGuardService) ScoreSession(sessionID string) float64

ScoreSession loads stored telemetry for sessionID and returns a [0.0, 1.0] bot probability score. Returns 1.0 if any telemetry record scores as non-allow.

func (*BotGuardService) StoreTelemetry

func (s *BotGuardService) StoreTelemetry(t *BotTelemetry) error

StoreTelemetry persists a telemetry record so ScoreSession can evaluate it later.

type BotSignature

type BotSignature struct {
	JA4Hash     string
	Description string
	AddedAt     time.Time
}

type BotTelemetry

type BotTelemetry struct {
	ID          string
	SessionID   string
	CollectedAt time.Time
	Raw         map[string]any
}

type BotVerdict

type BotVerdict int
const (
	VerdictAllow BotVerdict = iota
	VerdictSpoof
	VerdictBlock
)

type CertSource

type CertSource interface {
	GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error)
}

type ConfiguredPhishlet added in v0.3.0

type ConfiguredPhishlet struct {
	Definition *Phishlet
	Config     *PhishletConfig
}

ConfiguredPhishlet pairs a compiled phishlet with operator config. This is what the resolver stores and the proxy uses during request handling.

type CookieExport

type CookieExport struct {
	Name     string `json:"name"`
	Value    string `json:"value"`
	Domain   string `json:"domain"`
	Path     string `json:"path"`
	Expires  int64  `json:"expirationDate"`
	HttpOnly bool   `json:"httpOnly"`
	Secure   bool   `json:"secure"`
	SameSite string `json:"sameSite"`
}

CookieExport is the wire format for the StorageAce browser import extension.

type CredentialRule

type CredentialRule struct {
	Key    *regexp.Regexp
	Search *regexp.Regexp
	Type   string // "post" or "json"
}

CredentialRule is a key+search regex pair that extracts one field from POST/JSON.

type CredentialRules

type CredentialRules struct {
	Username CredentialRule
	Password CredentialRule
	Custom   []CustomCredentialRule
}

CredentialRules groups the extraction rules for username, password, and custom fields.

type CustomCredentialRule

type CustomCredentialRule struct {
	Name string
	CredentialRule
}

CustomCredentialRule is like CredentialRule but stores into Session.Custom[Name].

type DNSProvider

type DNSProvider interface {
	CreateRecord(ctx context.Context, zone, name, typ, value string, ttl int) error
	UpdateRecord(ctx context.Context, zone, name, typ, value string, ttl int) error
	DeleteRecord(ctx context.Context, zone, name, typ string) error

	// ACME DNS-01 challenge support.
	Present(ctx context.Context, domain, token, keyAuth string) error
	CleanUp(ctx context.Context, domain, token, keyAuth string) error

	Ping(ctx context.Context) error

	// Name returns a short provider identifier, e.g. "builtin" or "cloudflare".
	Name() string
}

DNSProvider is the interface that dns provider implementations satisfy implicitly. It covers record management, ACME DNS-01 challenge support, and health checking. Implementations must be safe for concurrent use.

type DNSService

type DNSService struct {
	// contains filtered or unexported fields
}

DNSService routes DNS record operations to the appropriate provider for each configured zone, and reconciles the desired record state for active phishlets.

func NewDNSService

func NewDNSService(providers map[string]DNSProvider, zones map[string]ZoneConfig, bus eventBus, logger *slog.Logger) *DNSService

func (*DNSService) CleanUp

func (s *DNSService) CleanUp(ctx context.Context, domain, token, keyAuth string) error

func (*DNSService) CreateRecord

func (s *DNSService) CreateRecord(ctx context.Context, zone, name, typ, value string, ttl int) error

func (*DNSService) DeleteRecord

func (s *DNSService) DeleteRecord(ctx context.Context, zone, name, typ string) error

func (*DNSService) ListProviders added in v0.4.0

func (s *DNSService) ListProviders() []string

ListProviders returns the aliases of all configured DNS providers.

func (*DNSService) ListZones added in v0.4.0

func (s *DNSService) ListZones() []ZoneConfig

ListZones returns all configured DNS zones.

func (*DNSService) Present

func (s *DNSService) Present(ctx context.Context, domain, token, keyAuth string) error

func (*DNSService) Reconcile

func (s *DNSService) Reconcile(ctx context.Context, desiredRecords []PhishletRecord) error

Reconcile creates or updates DNS records to match the desired state. It is idempotent — calling it twice with the same desired state is safe.

func (*DNSService) RemoveRecords

func (s *DNSService) RemoveRecords(ctx context.Context, records []PhishletRecord) error

RemoveRecords cleans up DNS records when a phishlet is disabled.

func (*DNSService) UpdateRecord

func (s *DNSService) UpdateRecord(ctx context.Context, zone, name, typ, value string, ttl int) error

type DNSSyncAction added in v0.5.3

type DNSSyncAction string

DNSSyncAction names the DNS record change reported by a DNSSyncPayload.

const (
	DNSActionCreate DNSSyncAction = "create"
	DNSActionUpdate DNSSyncAction = "update"
	DNSActionDelete DNSSyncAction = "delete"
)

type DNSSyncPayload

type DNSSyncPayload struct {
	Zone     string
	Name     string
	Type     string // "A", "AAAA", "TXT", etc.
	Value    string
	Action   DNSSyncAction
	Provider string // provider alias from config
}

DNSSyncPayload is the payload for EventDNSRecordSynced.

type Event

type Event struct {
	Type       sdk.EventType
	OccurredAt time.Time
	Payload    any
}

Event is a domain event published to the bus.

func NewBotDetectedEvent added in v0.5.3

func NewBotDetectedEvent(p BotDetectedPayload) Event

func NewCredsCapturedEvent added in v0.5.3

func NewCredsCapturedEvent(s *Session) Event

func NewDNSSyncEvent added in v0.5.3

func NewDNSSyncEvent(p DNSSyncPayload) Event

func NewPhishletEnabledEvent added in v0.5.3

func NewPhishletEnabledEvent(cp *ConfiguredPhishlet) Event

func NewPhishletPushedEvent added in v0.5.3

func NewPhishletPushedEvent(p *Phishlet) Event

func NewSessionCompletedEvent added in v0.5.3

func NewSessionCompletedEvent(s *Session) Event

func NewSessionCreatedEvent added in v0.5.3

func NewSessionCreatedEvent(s *Session) Event

func NewTokensCapturedEvent added in v0.5.3

func NewTokensCapturedEvent(s *Session) Event

type ForcePost

type ForcePost struct {
	Path       *regexp.Regexp
	Conditions []ForcePostCondition
	Params     []ForcePostParam
}

ForcePost describes a POST parameter to inject or override.

type ForcePostCondition

type ForcePostCondition struct {
	Key    *regexp.Regexp
	Search *regexp.Regexp
}

type ForcePostParam

type ForcePostParam struct {
	Key   string
	Value string
}

type InterceptRule

type InterceptRule struct {
	Path        *regexp.Regexp
	BodySearch  *regexp.Regexp
	StatusCode  int
	ContentType string
	Body        string
}

InterceptRule matches a request and returns a fully custom response.

type JSInject

type JSInject struct {
	TriggerDomain string
	TriggerPath   *regexp.Regexp
	Script        string
}

JSInject defines a JavaScript snippet to inject into matching pages.

type LoginSpec

type LoginSpec struct {
	Domain string
	Path   string
}

LoginSpec identifies where the login form lives.

type Lure

type Lure struct {
	ID          string
	Phishlet    string
	Hostname    string
	Path        string
	RedirectURL string
	SpoofURL    string
	UAFilter    string // raw regex; empty means accept all

	PausedUntil time.Time
	ParamsKey   []byte // 32-byte AES-256-GCM key, generated at creation
	// contains filtered or unexported fields
}

Lure is a configured phishing URL instance tied to a specific phishlet.

func (*Lure) CompileUA

func (l *Lure) CompileUA() error

CompileUA compiles the UAFilter regex. Must be called after loading from storage.

func (*Lure) IsPaused

func (l *Lure) IsPaused() bool

func (*Lure) MatchesUA

func (l *Lure) MatchesUA(ua string) bool

func (*Lure) PausedUntilPtr

func (l *Lure) PausedUntilPtr() *time.Time

PausedUntilPtr returns a pointer to PausedUntil, or nil if not paused.

func (*Lure) URL

func (l *Lure) URL(httpsPort int) string

URL returns the base phishing URL for this lure.

type LureService

type LureService struct {
	Store       lureStore
	Invalidator lureInvalidator
	Cipher      paramCipher
}

LureService owns all business logic for lure management.

func (*LureService) Create

func (s *LureService) Create(lure *Lure) error

func (*LureService) DecryptParams

func (s *LureService) DecryptParams(lure *Lure, token string) (map[string]string, error)

DecryptParams decodes and decrypts the ?p= query value from a lure URL.

func (*LureService) Delete

func (s *LureService) Delete(id string) error

func (*LureService) Get

func (s *LureService) Get(id string) (*Lure, error)

func (*LureService) List

func (s *LureService) List() ([]*Lure, error)

func (*LureService) Pause

func (s *LureService) Pause(id string, d time.Duration) (*Lure, error)

func (*LureService) URLWithParams

func (s *LureService) URLWithParams(lure *Lure, httpsPort int, params map[string]string) (string, error)

URLWithParams returns the phishing URL with encrypted custom parameters embedded as a URL-safe base64 ?p= query value.

func (*LureService) Unpause

func (s *LureService) Unpause(id string) (*Lure, error)

func (*LureService) Update

func (s *LureService) Update(lure *Lure) error

type NopDNSReconciler

type NopDNSReconciler struct{}

NopDNSReconciler is a DNS reconciler that does nothing. Used when no DNS providers are configured (e.g., local testing with /etc/hosts).

func (NopDNSReconciler) Reconcile

func (NopDNSReconciler) RemoveRecords

func (NopDNSReconciler) RemoveRecords(_ context.Context, _ []PhishletRecord) error

type NotificationChannel

type NotificationChannel struct {
	ID         string
	Type       sdk.ChannelType
	URL        string
	AuthHeader string          // optional Authorization header (webhook only)
	Filter     []sdk.EventType // event type filter; empty = all events
	Enabled    bool
	CreatedAt  time.Time
}

NotificationChannel is a configured notification destination.

func (*NotificationChannel) Accepts

func (ch *NotificationChannel) Accepts(eventType sdk.EventType) bool

Accepts reports whether the channel should receive the given event type. An empty filter means all events are accepted.

type NotificationService

type NotificationService struct {
	Store      notificationStore
	Dispatcher notificationDispatcher
}

NotificationService owns notification channel lifecycle — CRUD, dispatcher reload, and test delivery. The API handler delegates to this service.

func (*NotificationService) Create

func (s *NotificationService) Create(channel *NotificationChannel) error

func (*NotificationService) Delete

func (m *NotificationService) Delete(id string) error

func (*NotificationService) Get

func (*NotificationService) List

func (*NotificationService) Shutdown

func (s *NotificationService) Shutdown(ctx context.Context) error

Shutdown stops the dispatcher and waits for in-flight deliveries.

func (*NotificationService) Start

func (s *NotificationService) Start(ctx context.Context)

Start loads channels from the store and starts the dispatcher.

func (*NotificationService) Test

func (m *NotificationService) Test(ctx context.Context, id string) error

type Operator added in v0.2.0

type Operator struct {
	Name      string
	CreatedAt time.Time
}

Operator represents a registered operator with API access.

type OperatorInvite added in v0.2.0

type OperatorInvite struct {
	Token string
	Name  string
}

OperatorInvite is a single-use token for enrolling a new operator.

type OperatorService added in v0.2.0

type OperatorService struct {
	Store  operatorStore
	Signer certSigner
}

OperatorService manages operator lifecycle: invites, enrollment, and listing.

func (*OperatorService) Delete added in v0.2.0

func (s *OperatorService) Delete(name string) error

func (*OperatorService) Enroll added in v0.2.0

func (s *OperatorService) Enroll(token string, csrPEM []byte) (certPEM, caCertPEM []byte, err error)

Enroll validates the invite token, signs the CSR, registers the operator, and returns the signed cert + CA cert.

func (*OperatorService) Invite added in v0.2.0

func (s *OperatorService) Invite(name string) (*OperatorInvite, error)

Invite creates a single-use invite token for the named operator.

func (*OperatorService) List added in v0.2.0

func (s *OperatorService) List() ([]*Operator, error)

type ParseError added in v0.3.0

type ParseError struct {
	// File is the path of the YAML file being parsed.
	File string

	// Field is the dot-separated path to the problematic field,
	// e.g. "sub_filters[0].search" or "proxy_hosts[1].phish_sub".
	Field string

	// Message describes the problem in human-readable form.
	Message string
}

ParseError is a single field-level problem found while validating or compiling a phishlet YAML file.

func (ParseError) Error added in v0.3.0

func (e ParseError) Error() string

type ParseErrors added in v0.3.0

type ParseErrors []ParseError

ParseErrors is a collection of ParseError values returned when multiple problems are found in a single phishlet file.

func (ParseErrors) Error added in v0.3.0

func (p ParseErrors) Error() string

type Phishlet

type Phishlet struct {
	Name    string
	Author  string
	Version string

	ProxyHosts  []ProxyHost
	SubFilters  []SubFilter
	AuthTokens  []TokenRule
	Credentials CredentialRules
	Login       LoginSpec
	ForcePosts  []ForcePost
	Intercepts  []InterceptRule
	JSInjects   []JSInject
}

Phishlet is a compiled phishlet definition. It is the shareable artifact produced by parsing and compiling a phishlet YAML file.

func (*Phishlet) FindLanding

func (p *Phishlet) FindLanding() *ProxyHost

FindLanding returns the proxy host marked is_landing, or nil if none.

func (*Phishlet) FindProxyHost

func (p *Phishlet) FindProxyHost(phishHost, baseDomain string) *ProxyHost

FindProxyHost returns the proxy host whose phishing FQDN matches phishHost, stripping any port and normalising case. Returns nil if no host matches.

func (*Phishlet) MatchesHost

func (p *Phishlet) MatchesHost(hostname, baseDomain string) bool

MatchesHost reports whether hostname belongs to this phishlet under baseDomain.

type PhishletConfig added in v0.3.0

type PhishletConfig struct {
	Name        string
	BaseDomain  string
	DNSProvider string
	Hostname    string
	SpoofURL    string
	Enabled     bool
}

PhishletConfig holds the operator's runtime settings for a phishlet. Persisted to SQLite and survives daemon restarts.

type PhishletFilter added in v0.3.0

type PhishletFilter struct {
	Enabled *bool
}

PhishletFilter controls which configs are returned by ListConfigs.

type PhishletRecord

type PhishletRecord struct {
	Zone string
	Name string // FQDN, e.g. "login.phish.com"
	IP   string // overrides ZoneConfig.ExternalIP when non-empty
}

PhishletRecord is the set of DNS records that must exist for one proxy host inside an active phishlet.

type PhishletService

type PhishletService struct {
	Store    phishletStore
	Bus      eventBus
	DNS      dnsReconciler
	Resolver phishletResolver
	Compiler phishletCompiler
}

PhishletService owns all business logic for phishlet lifecycle. Every enable/disable writes to the store AND updates the in-memory resolver, so the proxy router never falls out of sync with the database.

func (*PhishletService) Disable

func (s *PhishletService) Disable(ctx context.Context, name string) (*ConfiguredPhishlet, error)

Disable marks a phishlet as inactive and removes its DNS records.

func (*PhishletService) Enable

func (s *PhishletService) Enable(ctx context.Context, name, hostname, dnsProvider string) (*ConfiguredPhishlet, error)

Enable marks a phishlet as active, optionally updating its hostname, base domain, and DNS provider. The resolver is updated so routing takes effect immediately. If a DNS reconciler is configured, A records are created for each proxy host's phishing FQDN.

func (*PhishletService) Get

func (s *PhishletService) Get(name string) (*PhishletConfig, error)

Get returns the phishlet config by name.

func (*PhishletService) InvalidateLures

func (s *PhishletService) InvalidateLures()

InvalidateLures reloads the lure cache after any lure mutation. Satisfies the lureInvalidator interface so LureService can notify the routing index.

func (*PhishletService) List

func (s *PhishletService) List() ([]*PhishletConfig, error)

List returns all phishlet configs.

func (*PhishletService) LoadFromDB

func (s *PhishletService) LoadFromDB() error

LoadFromDB compiles stored definitions, applies operator config, and registers enabled phishlets in the resolver. Call once at startup.

func (*PhishletService) Push added in v0.3.0

func (s *PhishletService) Push(yaml string) (*Phishlet, error)

Push validates phishlet YAML and stores the definition.

func (*PhishletService) ReconcileAll added in v0.4.0

func (s *PhishletService) ReconcileAll(ctx context.Context) error

ReconcileAll reconciles DNS records for all enabled phishlets.

func (*PhishletService) ResolveHostname

func (s *PhishletService) ResolveHostname(hostname, urlPath string) (*ConfiguredPhishlet, *Lure, error)

ResolveHostname returns the configured phishlet and best-matching lure for a request hostname. Returns nil, nil, nil when no active phishlet owns the hostname.

type ProxyHost

type ProxyHost struct {
	PhishSubdomain string
	OrigSubdomain  string
	Domain         string
	IsLanding      bool
	AutoFilter     bool
	UpstreamScheme string // "http" or "https"
}

ProxyHost maps a phishing subdomain to a real upstream host.

func (*ProxyHost) OriginHost

func (h *ProxyHost) OriginHost() string

OriginHost returns the fully qualified upstream hostname (e.g. "login.microsoftonline.com").

func (*ProxyHost) PhishHost

func (h *ProxyHost) PhishHost(baseDomain string) string

PhishHost returns the fully qualified phishing hostname (e.g. "login.phish.example.com").

type PuppetService

type PuppetService struct {
	// contains filtered or unexported fields
}

PuppetService manages headless browser interactions for bot telemetry collection. It collects telemetry from target sites and caches JS override strings keyed by phishlet name for injection into victim responses.

func NewPuppetService

func NewPuppetService(puppet puppet, builder overrideBuilder, bus eventBus, cfg PuppetServiceConfig, logger *slog.Logger) *PuppetService

func (*PuppetService) CollectAndCache

func (s *PuppetService) CollectAndCache(ctx context.Context, phishletName, targetURL string) error

CollectAndCache runs the puppet browser against targetURL, builds the JS override, and caches it under phishletName.

func (*PuppetService) GetOverride

func (s *PuppetService) GetOverride(phishletName string) string

GetOverride returns the cached JS override for a phishlet, or "" if none.

func (*PuppetService) Shutdown

func (s *PuppetService) Shutdown(ctx context.Context) error

Shutdown cancels in-flight collections, unsubscribes from events, and shuts down the puppet backend.

func (*PuppetService) Start

func (s *PuppetService) Start(ctx context.Context)

Start subscribes to phishlet-enabled events and triggers async collection. The provided context is used as the parent for all puppet collection goroutines; cancelling it (e.g. on daemon shutdown) cancels any in-flight collections.

type PuppetServiceConfig

type PuppetServiceConfig struct {
	CacheTTL      time.Duration
	NavTimeout    time.Duration
	MaxConcurrent int
}

PuppetServiceConfig holds the parameters for constructing a PuppetService.

type Session

type Session struct {
	ID           string
	Phishlet     string
	LureID       string
	RemoteAddr   string
	UserAgent    string
	JA4Hash      string
	BotScore     float64
	Username     string
	Password     string
	Custom       map[string]string
	LureParams   map[string]string                  // decrypted params from the ?p= lure URL token
	CookieTokens map[string]map[string]*http.Cookie // domain → name → cookie
	BodyTokens   map[string]string
	HTTPTokens   map[string]string
	PuppetID     string
	StartedAt    time.Time
	CompletedAt  *time.Time // nil until all auth_tokens are captured
}

Session is the server-side record for a single victim visit.

func (*Session) AddCookie

func (s *Session) AddCookie(cookie *http.Cookie)

AddCookie stores a captured upstream cookie for token extraction and replay. The cookie is stored keyed by its Domain and Name fields.

func (*Session) Complete

func (s *Session) Complete()

func (*Session) ExportCookies

func (s *Session) ExportCookies() []CookieExport

ExportCookies returns captured cookies in StorageAce import format.

func (*Session) HasCredentials

func (s *Session) HasCredentials() bool

func (*Session) HasRequiredTokens

func (s *Session) HasRequiredTokens(def *Phishlet) bool

HasRequiredTokens returns true when all non-always auth tokens defined by def have been captured in this session. Used by TokenExtractor to determine when to fire EventSessionCompleted.

func (*Session) IsDone

func (s *Session) IsDone() bool

IsDone returns true when all required auth tokens have been captured.

func (*Session) Snapshot added in v0.5.0

func (s *Session) Snapshot() Session

Snapshot returns a shallow copy of the session, safe to read from another goroutine without holding a lock. Map fields are shared (not deep-copied) but are only appended to during the session lifecycle, not mutated in place.

type SessionFilter

type SessionFilter struct {
	Phishlet       string
	CompletedOnly  bool
	IncompleteOnly bool
	After          time.Time // zero means no lower bound
	Before         time.Time // zero means no upper bound
	Limit          int       // 0 means no limit
	Offset         int
}

SessionFilter scopes a ListSessions call.

type SessionService

type SessionService struct {
	Store sessionStore
	Bus   eventBus
	// contains filtered or unexported fields
}

SessionService owns all business logic for session lifecycle. Sessions are persisted on creation (one write per victim) and on credential capture / completion. The in-memory cache eliminates database reads from the proxy hot path — Get checks the cache first and only falls back to the store for completed sessions from previous daemon runs.

func (*SessionService) CaptureCredentials

func (s *SessionService) CaptureCredentials(session *Session) error

func (*SessionService) CaptureTokens added in v0.5.0

func (s *SessionService) CaptureTokens(session *Session) error

func (*SessionService) Complete

func (s *SessionService) Complete(session *Session) error

func (*SessionService) Count

func (s *SessionService) Count(f SessionFilter) (int, error)

func (*SessionService) Delete

func (s *SessionService) Delete(id string) error

func (*SessionService) ExportCookiesJSON

func (s *SessionService) ExportCookiesJSON(id string) ([]byte, error)

func (*SessionService) Get

func (s *SessionService) Get(id string) (*Session, error)

func (*SessionService) IsComplete

func (s *SessionService) IsComplete(sess *Session, def *Phishlet) bool

IsComplete satisfies the response.SessionCompleter interface.

func (*SessionService) List

func (s *SessionService) List(f SessionFilter) ([]*Session, error)

func (*SessionService) NewSession

func (s *SessionService) NewSession(clientIP, ja4Hash, userAgent, lureID, phishletName string, lureParams map[string]string) (*Session, error)

NewSession creates a new session and persists it to the store. lureParams are optional key-value pairs decrypted from the lure URL's ?p= token.

func (*SessionService) Stream added in v0.5.3

func (s *SessionService) Stream(ctx context.Context) <-chan Event

Stream returns a channel that receives every session-lifecycle event published on the bus. The channel is closed and all bus subscriptions are released when ctx is cancelled, so callers need only range over it.

Delivery is best-effort: if the caller drains slowly and the buffer fills, events are dropped for that subscriber, matching the bus's policy.

func (*SessionService) Update

func (s *SessionService) Update(session *Session) error

type SubFilter

type SubFilter struct {
	Hostname  string
	MimeTypes []string
	Search    *regexp.Regexp
	Replace   string
}

SubFilter is a compiled search/replace rule applied to proxied response bodies.

func (*SubFilter) MatchesMIME

func (s *SubFilter) MatchesMIME(mimeType string) bool

type TokenRule

type TokenRule struct {
	Type     TokenType
	Domain   string
	Name     *regexp.Regexp
	Search   *regexp.Regexp
	HTTPOnly bool
	Always   bool
}

TokenRule describes how to capture one auth token from a response.

type TokenType

type TokenType int

TokenType classifies how an auth token is extracted.

const (
	TokenTypeCookie TokenType = iota
	TokenTypeBody
	TokenTypeHTTPHeader
)

type ZoneConfig

type ZoneConfig struct {
	Zone         string
	ProviderName string
	ExternalIP   string
}

ZoneConfig pairs a zone (base domain) with its provider alias and the external IP to use for A records.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL