diff --git a/modules/http_proxy_base.go b/modules/http_proxy_base.go index 1d03efa3..8b9cf219 100644 --- a/modules/http_proxy_base.go +++ b/modules/http_proxy_base.go @@ -77,9 +77,12 @@ func NewHTTPProxy(s *session.Session) *HTTPProxy { p.Proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { log.Debug("(%s) < %s %s %s%s", core.Green(p.Name), req.RemoteAddr, req.Method, req.Host, req.URL.Path) if p.Script != nil { - jsres := p.Script.OnRequest(req) - if jsres != nil { - p.logAction(req, jsres) + jsreq, jsres := p.Script.OnRequest(req) + if jsreq != nil { + p.logRequestAction(req, jsreq) + return jsreq.ToRequest(), nil + } else if jsres != nil { + p.logResponseAction(req, jsres) return req, jsres.ToResponse(req) } } @@ -91,9 +94,9 @@ func NewHTTPProxy(s *session.Session) *HTTPProxy { req := res.Request log.Debug("(%s) > %s %s %s%s", core.Green(p.Name), req.RemoteAddr, req.Method, req.Host, req.URL.Path) if p.Script != nil { - jsres := p.Script.OnResponse(res) + _, jsres := p.Script.OnResponse(res) if jsres != nil { - p.logAction(res.Request, jsres) + p.logResponseAction(res.Request, jsres) return jsres.ToResponse(res.Request) } } @@ -104,7 +107,23 @@ func NewHTTPProxy(s *session.Session) *HTTPProxy { return p } -func (p *HTTPProxy) logAction(req *http.Request, jsres *JSResponse) { +func (p *HTTPProxy) logRequestAction(req *http.Request, jsreq *JSRequest) { + p.sess.Events.Add(p.Name+".spoofed-request", struct { + To string + Method string + Host string + Path string + Size int + }{ + strings.Split(req.RemoteAddr, ":")[0], + jsreq.Method, + jsreq.Hostname, + jsreq.Path, + len(jsreq.Body), + }) +} + +func (p *HTTPProxy) logResponseAction(req *http.Request, jsres *JSResponse) { p.sess.Events.Add(p.Name+".spoofed-response", struct { To string Method string diff --git a/modules/http_proxy_js_request.go b/modules/http_proxy_js_request.go index 6b98fa17..f389db29 100644 --- a/modules/http_proxy_js_request.go +++ b/modules/http_proxy_js_request.go @@ -24,7 +24,10 @@ type JSRequest struct { ContentType string Headers []JSHeader Body string - req *http.Request + + req *http.Request + refHash string + bodyRead bool } func NewJSRequest(req *http.Request) *JSRequest { @@ -41,7 +44,7 @@ func NewJSRequest(req *http.Request) *JSRequest { } } - return &JSRequest{ + jreq := &JSRequest{ Client: strings.Split(req.RemoteAddr, ":")[0], Method: req.Method, Version: fmt.Sprintf("%d.%d", req.ProtoMajor, req.ProtoMinor), @@ -51,8 +54,38 @@ func NewJSRequest(req *http.Request) *JSRequest { ContentType: cType, Headers: headers, - req: req, + req: req, + bodyRead: false, } + jreq.UpdateHash() + + return jreq +} + +func (j *JSRequest) NewHash() string { + hash := fmt.Sprintf("%s.%s.%s.%s.%s.%s.%s", j.Client, j.Method, j.Version, j.Hostname, j.Path, j.Query, j.ContentType) + for _, h := range j.Headers { + hash += fmt.Sprintf(".%s-%s", h.Name, h.Value) + } + hash += "." + j.Body + return hash +} + +func (j *JSRequest) UpdateHash() { + j.refHash = j.NewHash() +} + +func (j *JSRequest) WasModified() bool { + // body was read + if j.bodyRead == true { + return true + } + // check if any of the fields has been changed + newHash := j.NewHash() + if newHash != j.refHash { + return true + } + return false } func (j *JSRequest) ReadBody() string { @@ -62,6 +95,7 @@ func (j *JSRequest) ReadBody() string { } j.Body = string(raw) + j.bodyRead = true // reset the request body to the original unread state j.req.Body = ioutil.NopCloser(bytes.NewBuffer(raw)) @@ -90,3 +124,26 @@ func (j *JSRequest) ParseForm() map[string]string { return form } + +func (j *JSRequest) ToRequest() (req *http.Request) { + url := fmt.Sprintf("%s://%s:%s%s?%s", j.req.URL.Scheme, j.Hostname, j.req.URL.Port(), j.Path, j.Query) + if j.Body == "" { + req, _ = http.NewRequest(j.Method, url, j.req.Body) + } else { + req, _ = http.NewRequest(j.Method, url, strings.NewReader(j.Body)) + } + + hadType := false + for _, h := range j.Headers { + req.Header.Set(h.Name, h.Value) + if h.Name == "Content-Type" { + hadType = true + } + } + + if hadType == false && j.ContentType != "" { + req.Header.Set("Content-Type", j.ContentType) + } + + return +} diff --git a/modules/http_proxy_script.go b/modules/http_proxy_script.go index ec29620d..48f1b821 100644 --- a/modules/http_proxy_script.go +++ b/modules/http_proxy_script.go @@ -60,9 +60,8 @@ func LoadHttpProxyScript(path string, sess *session.Session) (err error, s *Http return LoadHttpProxyScriptSource(path, string(raw), sess) } -func (s *HttpProxyScript) doRequestDefines(req *http.Request) (err error, jsres *JSResponse) { - // convert request and define empty response to be optionally filled - jsreq := NewJSRequest(req) +func (s *HttpProxyScript) doRequestDefines(req *http.Request) (err error, jsreq *JSRequest, jsres *JSResponse) { + jsreq = NewJSRequest(req) if err = s.VM.Set("req", jsreq); err != nil { log.Error("Error while defining request: %s", err) return @@ -76,9 +75,8 @@ func (s *HttpProxyScript) doRequestDefines(req *http.Request) (err error, jsres return } -func (s *HttpProxyScript) doResponseDefines(res *http.Response) (err error, jsres *JSResponse) { - // convert both request and response - jsreq := NewJSRequest(res.Request) +func (s *HttpProxyScript) doResponseDefines(res *http.Response) (err error, jsreq *JSRequest, jsres *JSResponse) { + jsreq = NewJSRequest(res.Request) if err = s.VM.Set("req", jsreq); err != nil { log.Error("Error while defining request: %s", err) return @@ -93,54 +91,57 @@ func (s *HttpProxyScript) doResponseDefines(res *http.Response) (err error, jsre return } -func (s *HttpProxyScript) OnRequest(req *http.Request) *JSResponse { +func (s *HttpProxyScript) OnRequest(original *http.Request) (jsreq *JSRequest, jsres *JSResponse) { + var err error + if s.onRequestScript != nil { s.Lock() defer s.Unlock() - err, jsres := s.doRequestDefines(req) - if err != nil { + if err, jsreq, jsres = s.doRequestDefines(original); err != nil { log.Error("Error while running bootstrap definitions: %s", err) - return nil + return nil, nil } - _, err = s.VM.Run(s.onRequestScript) - if err != nil { + if _, err = s.VM.Run(s.onRequestScript); err != nil { log.Error("Error while executing onRequest callback: %s", err) - return nil + return nil, nil } - if jsres.WasModified() { + if jsreq.WasModified() { + jsreq.UpdateHash() + return jsreq, nil + } else if jsres.WasModified() { jsres.UpdateHash() - return jsres + return nil, jsres } } - return nil + return nil, nil } -func (s *HttpProxyScript) OnResponse(res *http.Response) *JSResponse { +func (s *HttpProxyScript) OnResponse(res *http.Response) (jsreq *JSRequest, jsres *JSResponse) { + var err error + if s.onResponseScript != nil { s.Lock() defer s.Unlock() - err, jsres := s.doResponseDefines(res) - if err != nil { + if err, jsreq, jsres = s.doResponseDefines(res); err != nil { log.Error("Error while running bootstrap definitions: %s", err) - return nil + return nil, nil } - _, err = s.VM.Run(s.onResponseScript) - if err != nil { + if _, err = s.VM.Run(s.onResponseScript); err != nil { log.Error("Error while executing onRequest callback: %s", err) - return nil + return nil, nil } if jsres.WasModified() { jsres.UpdateHash() - return jsres + return nil, jsres } } - return nil + return nil, nil }