events_view_http.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. package events_stream
  2. import (
  3. "bytes"
  4. "compress/gzip"
  5. "encoding/hex"
  6. "encoding/json"
  7. "fmt"
  8. "io"
  9. "net/url"
  10. "regexp"
  11. "strings"
  12. "github.com/bettercap/bettercap/modules/net_sniff"
  13. "github.com/bettercap/bettercap/session"
  14. "github.com/evilsocket/islazy/tui"
  15. )
  16. var (
  17. reJsonKey = regexp.MustCompile(`("[^"]+"):`)
  18. )
  19. func (mod *EventsStream) shouldDumpHttpRequest(req net_sniff.HTTPRequest) bool {
  20. if mod.dumpHttpReqs {
  21. // dump all
  22. return true
  23. } else if req.Method != "GET" {
  24. // dump if it's not just a GET
  25. return true
  26. }
  27. // search for interesting headers and cookies
  28. for name := range req.Headers {
  29. headerName := strings.ToLower(name)
  30. if strings.Contains(headerName, "auth") || strings.Contains(headerName, "token") {
  31. return true
  32. }
  33. }
  34. return false
  35. }
  36. func (mod *EventsStream) shouldDumpHttpResponse(res net_sniff.HTTPResponse) bool {
  37. if mod.dumpHttpResp {
  38. return true
  39. } else if strings.Contains(res.ContentType, "text/plain") {
  40. return true
  41. } else if strings.Contains(res.ContentType, "application/json") {
  42. return true
  43. } else if strings.Contains(res.ContentType, "text/xml") {
  44. return true
  45. }
  46. // search for interesting headers
  47. for name := range res.Headers {
  48. headerName := strings.ToLower(name)
  49. if strings.Contains(headerName, "auth") || strings.Contains(headerName, "token") || strings.Contains(headerName, "cookie") {
  50. return true
  51. }
  52. }
  53. return false
  54. }
  55. func (mod *EventsStream) dumpForm(body []byte) string {
  56. form := []string{}
  57. for _, v := range strings.Split(string(body), "&") {
  58. if strings.Contains(v, "=") {
  59. parts := strings.SplitN(v, "=", 2)
  60. name := parts[0]
  61. value, err := url.QueryUnescape(parts[1])
  62. if err != nil {
  63. value = parts[1]
  64. }
  65. form = append(form, fmt.Sprintf("%s=%s", tui.Green(name), tui.Bold(tui.Red(value))))
  66. } else {
  67. value, err := url.QueryUnescape(v)
  68. if err != nil {
  69. value = v
  70. }
  71. form = append(form, tui.Bold(tui.Red(value)))
  72. }
  73. }
  74. return "\n" + strings.Join(form, "&") + "\n"
  75. }
  76. func (mod *EventsStream) dumpText(body []byte) string {
  77. return "\n" + tui.Bold(tui.Red(string(body))) + "\n"
  78. }
  79. func (mod *EventsStream) dumpGZIP(body []byte) string {
  80. buffer := bytes.NewBuffer(body)
  81. uncompressed := bytes.Buffer{}
  82. reader, err := gzip.NewReader(buffer)
  83. if mod.dumpFormatHex {
  84. if err != nil {
  85. return mod.dumpRaw(body)
  86. } else if _, err = uncompressed.ReadFrom(reader); err != nil {
  87. return mod.dumpRaw(body)
  88. }
  89. return mod.dumpRaw(uncompressed.Bytes())
  90. } else {
  91. if err != nil {
  92. return mod.dumpText(body)
  93. } else if _, err = uncompressed.ReadFrom(reader); err != nil {
  94. return mod.dumpText(body)
  95. }
  96. return mod.dumpText(uncompressed.Bytes())
  97. }
  98. }
  99. func (mod *EventsStream) dumpJSON(body []byte) string {
  100. var buf bytes.Buffer
  101. var pretty string
  102. if err := json.Indent(&buf, body, "", " "); err != nil {
  103. pretty = string(body)
  104. } else {
  105. pretty = buf.String()
  106. }
  107. return "\n" + reJsonKey.ReplaceAllString(pretty, tui.Green(`$1:`)) + "\n"
  108. }
  109. func (mod *EventsStream) dumpXML(body []byte) string {
  110. // TODO: indent xml
  111. return "\n" + string(body) + "\n"
  112. }
  113. func (mod *EventsStream) dumpRaw(body []byte) string {
  114. return "\n" + hex.Dump(body) + "\n"
  115. }
  116. func (mod *EventsStream) viewHttpRequest(output io.Writer, e session.Event) {
  117. se := e.Data.(net_sniff.SnifferEvent)
  118. req := se.Data.(net_sniff.HTTPRequest)
  119. fmt.Fprintf(output, "[%s] [%s] %s\n",
  120. e.Time.Format(mod.timeFormat),
  121. tui.Green(e.Tag),
  122. se.Message)
  123. if mod.shouldDumpHttpRequest(req) {
  124. dump := fmt.Sprintf("%s %s %s\n", tui.Bold(req.Method), req.URL, tui.Dim(req.Proto))
  125. dump += fmt.Sprintf("%s: %s\n", tui.Blue("Host"), tui.Yellow(req.Host))
  126. for name, values := range req.Headers {
  127. for _, value := range values {
  128. dump += fmt.Sprintf("%s: %s\n", tui.Blue(name), tui.Yellow(value))
  129. }
  130. }
  131. if req.Body != nil {
  132. if req.IsType("application/x-www-form-urlencoded") {
  133. dump += mod.dumpForm(req.Body)
  134. } else if req.IsType("text/plain") {
  135. dump += mod.dumpText(req.Body)
  136. } else if req.IsType("text/xml") {
  137. dump += mod.dumpXML(req.Body)
  138. } else if req.IsType("gzip") {
  139. dump += mod.dumpGZIP(req.Body)
  140. } else if req.IsType("application/json") {
  141. dump += mod.dumpJSON(req.Body)
  142. } else {
  143. if mod.dumpFormatHex {
  144. dump += mod.dumpRaw(req.Body)
  145. } else {
  146. dump += mod.dumpText(req.Body)
  147. }
  148. }
  149. }
  150. fmt.Fprintf(output, "\n%s\n", dump)
  151. }
  152. }
  153. func (mod *EventsStream) viewHttpResponse(output io.Writer, e session.Event) {
  154. se := e.Data.(net_sniff.SnifferEvent)
  155. res := se.Data.(net_sniff.HTTPResponse)
  156. fmt.Fprintf(output, "[%s] [%s] %s\n",
  157. e.Time.Format(mod.timeFormat),
  158. tui.Green(e.Tag),
  159. se.Message)
  160. if mod.shouldDumpHttpResponse(res) {
  161. dump := fmt.Sprintf("%s %s\n", tui.Dim(res.Protocol), res.Status)
  162. for name, values := range res.Headers {
  163. for _, value := range values {
  164. dump += fmt.Sprintf("%s: %s\n", tui.Blue(name), tui.Yellow(value))
  165. }
  166. }
  167. if res.Body != nil {
  168. // TODO: add more interesting response types
  169. if res.IsType("text/plain") {
  170. dump += mod.dumpText(res.Body)
  171. } else if res.IsType("application/json") {
  172. dump += mod.dumpJSON(res.Body)
  173. } else if res.IsType("text/xml") {
  174. dump += mod.dumpXML(res.Body)
  175. }
  176. }
  177. fmt.Fprintf(output, "\n%s\n", dump)
  178. }
  179. }
  180. func (mod *EventsStream) viewHttpEvent(output io.Writer, e session.Event) {
  181. if e.Tag == "net.sniff.http.request" {
  182. mod.viewHttpRequest(output, e)
  183. } else if e.Tag == "net.sniff.http.response" {
  184. mod.viewHttpResponse(output, e)
  185. }
  186. }