123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- package events_stream
- import (
- "bytes"
- "compress/gzip"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "io"
- "net/url"
- "regexp"
- "strings"
- "github.com/bettercap/bettercap/modules/net_sniff"
- "github.com/bettercap/bettercap/session"
- "github.com/evilsocket/islazy/tui"
- )
- var (
- reJsonKey = regexp.MustCompile(`("[^"]+"):`)
- )
- func (mod *EventsStream) shouldDumpHttpRequest(req net_sniff.HTTPRequest) bool {
- if mod.dumpHttpReqs {
- // dump all
- return true
- } else if req.Method != "GET" {
- // dump if it's not just a GET
- return true
- }
- // search for interesting headers and cookies
- for name := range req.Headers {
- headerName := strings.ToLower(name)
- if strings.Contains(headerName, "auth") || strings.Contains(headerName, "token") {
- return true
- }
- }
- return false
- }
- func (mod *EventsStream) shouldDumpHttpResponse(res net_sniff.HTTPResponse) bool {
- if mod.dumpHttpResp {
- return true
- } else if strings.Contains(res.ContentType, "text/plain") {
- return true
- } else if strings.Contains(res.ContentType, "application/json") {
- return true
- } else if strings.Contains(res.ContentType, "text/xml") {
- return true
- }
- // search for interesting headers
- for name := range res.Headers {
- headerName := strings.ToLower(name)
- if strings.Contains(headerName, "auth") || strings.Contains(headerName, "token") || strings.Contains(headerName, "cookie") {
- return true
- }
- }
- return false
- }
- func (mod *EventsStream) dumpForm(body []byte) string {
- form := []string{}
- for _, v := range strings.Split(string(body), "&") {
- if strings.Contains(v, "=") {
- parts := strings.SplitN(v, "=", 2)
- name := parts[0]
- value, err := url.QueryUnescape(parts[1])
- if err != nil {
- value = parts[1]
- }
- form = append(form, fmt.Sprintf("%s=%s", tui.Green(name), tui.Bold(tui.Red(value))))
- } else {
- value, err := url.QueryUnescape(v)
- if err != nil {
- value = v
- }
- form = append(form, tui.Bold(tui.Red(value)))
- }
- }
- return "\n" + strings.Join(form, "&") + "\n"
- }
- func (mod *EventsStream) dumpText(body []byte) string {
- return "\n" + tui.Bold(tui.Red(string(body))) + "\n"
- }
- func (mod *EventsStream) dumpGZIP(body []byte) string {
- buffer := bytes.NewBuffer(body)
- uncompressed := bytes.Buffer{}
- reader, err := gzip.NewReader(buffer)
- if mod.dumpFormatHex {
- if err != nil {
- return mod.dumpRaw(body)
- } else if _, err = uncompressed.ReadFrom(reader); err != nil {
- return mod.dumpRaw(body)
- }
- return mod.dumpRaw(uncompressed.Bytes())
- } else {
- if err != nil {
- return mod.dumpText(body)
- } else if _, err = uncompressed.ReadFrom(reader); err != nil {
- return mod.dumpText(body)
- }
- return mod.dumpText(uncompressed.Bytes())
- }
- }
- func (mod *EventsStream) dumpJSON(body []byte) string {
- var buf bytes.Buffer
- var pretty string
- if err := json.Indent(&buf, body, "", " "); err != nil {
- pretty = string(body)
- } else {
- pretty = buf.String()
- }
- return "\n" + reJsonKey.ReplaceAllString(pretty, tui.Green(`$1:`)) + "\n"
- }
- func (mod *EventsStream) dumpXML(body []byte) string {
- // TODO: indent xml
- return "\n" + string(body) + "\n"
- }
- func (mod *EventsStream) dumpRaw(body []byte) string {
- return "\n" + hex.Dump(body) + "\n"
- }
- func (mod *EventsStream) viewHttpRequest(output io.Writer, e session.Event) {
- se := e.Data.(net_sniff.SnifferEvent)
- req := se.Data.(net_sniff.HTTPRequest)
- fmt.Fprintf(output, "[%s] [%s] %s\n",
- e.Time.Format(mod.timeFormat),
- tui.Green(e.Tag),
- se.Message)
- if mod.shouldDumpHttpRequest(req) {
- dump := fmt.Sprintf("%s %s %s\n", tui.Bold(req.Method), req.URL, tui.Dim(req.Proto))
- dump += fmt.Sprintf("%s: %s\n", tui.Blue("Host"), tui.Yellow(req.Host))
- for name, values := range req.Headers {
- for _, value := range values {
- dump += fmt.Sprintf("%s: %s\n", tui.Blue(name), tui.Yellow(value))
- }
- }
- if req.Body != nil {
- if req.IsType("application/x-www-form-urlencoded") {
- dump += mod.dumpForm(req.Body)
- } else if req.IsType("text/plain") {
- dump += mod.dumpText(req.Body)
- } else if req.IsType("text/xml") {
- dump += mod.dumpXML(req.Body)
- } else if req.IsType("gzip") {
- dump += mod.dumpGZIP(req.Body)
- } else if req.IsType("application/json") {
- dump += mod.dumpJSON(req.Body)
- } else {
- if mod.dumpFormatHex {
- dump += mod.dumpRaw(req.Body)
- } else {
- dump += mod.dumpText(req.Body)
- }
- }
- }
- fmt.Fprintf(output, "\n%s\n", dump)
- }
- }
- func (mod *EventsStream) viewHttpResponse(output io.Writer, e session.Event) {
- se := e.Data.(net_sniff.SnifferEvent)
- res := se.Data.(net_sniff.HTTPResponse)
- fmt.Fprintf(output, "[%s] [%s] %s\n",
- e.Time.Format(mod.timeFormat),
- tui.Green(e.Tag),
- se.Message)
- if mod.shouldDumpHttpResponse(res) {
- dump := fmt.Sprintf("%s %s\n", tui.Dim(res.Protocol), res.Status)
- for name, values := range res.Headers {
- for _, value := range values {
- dump += fmt.Sprintf("%s: %s\n", tui.Blue(name), tui.Yellow(value))
- }
- }
- if res.Body != nil {
- // TODO: add more interesting response types
- if res.IsType("text/plain") {
- dump += mod.dumpText(res.Body)
- } else if res.IsType("application/json") {
- dump += mod.dumpJSON(res.Body)
- } else if res.IsType("text/xml") {
- dump += mod.dumpXML(res.Body)
- }
- }
- fmt.Fprintf(output, "\n%s\n", dump)
- }
- }
- func (mod *EventsStream) viewHttpEvent(output io.Writer, e session.Event) {
- if e.Tag == "net.sniff.http.request" {
- mod.viewHttpRequest(output, e)
- } else if e.Tag == "net.sniff.http.response" {
- mod.viewHttpResponse(output, e)
- }
- }
|