http_proxy_base.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. package http_proxy
  2. import (
  3. "bufio"
  4. "bytes"
  5. "context"
  6. "crypto/tls"
  7. "crypto/x509"
  8. "fmt"
  9. "io/ioutil"
  10. "net"
  11. "net/http"
  12. "net/url"
  13. "path/filepath"
  14. "strconv"
  15. "strings"
  16. "time"
  17. "github.com/bettercap/bettercap/firewall"
  18. "github.com/bettercap/bettercap/session"
  19. btls "github.com/bettercap/bettercap/tls"
  20. "github.com/elazarl/goproxy"
  21. "github.com/inconshreveable/go-vhost"
  22. "github.com/evilsocket/islazy/fs"
  23. "github.com/evilsocket/islazy/log"
  24. "github.com/evilsocket/islazy/str"
  25. "github.com/evilsocket/islazy/tui"
  26. )
  27. const (
  28. httpReadTimeout = 5 * time.Second
  29. httpWriteTimeout = 10 * time.Second
  30. )
  31. type HTTPProxy struct {
  32. Name string
  33. Address string
  34. Server *http.Server
  35. Redirection *firewall.Redirection
  36. Proxy *goproxy.ProxyHttpServer
  37. Script *HttpProxyScript
  38. CertFile string
  39. KeyFile string
  40. Blacklist []string
  41. Whitelist []string
  42. Sess *session.Session
  43. Stripper *SSLStripper
  44. jsHook string
  45. isTLS bool
  46. isRunning bool
  47. doRedirect bool
  48. sniListener net.Listener
  49. tag string
  50. }
  51. func stripPort(s string) string {
  52. ix := strings.IndexRune(s, ':')
  53. if ix == -1 {
  54. return s
  55. }
  56. return s[:ix]
  57. }
  58. type dummyLogger struct {
  59. p *HTTPProxy
  60. }
  61. func (l dummyLogger) Printf(format string, v ...interface{}) {
  62. l.p.Debug("[goproxy.log] %s", str.Trim(fmt.Sprintf(format, v...)))
  63. }
  64. func NewHTTPProxy(s *session.Session, tag string) *HTTPProxy {
  65. p := &HTTPProxy{
  66. Name: "http.proxy",
  67. Proxy: goproxy.NewProxyHttpServer(),
  68. Sess: s,
  69. Stripper: NewSSLStripper(s, false),
  70. isTLS: false,
  71. doRedirect: true,
  72. Server: nil,
  73. Blacklist: make([]string, 0),
  74. Whitelist: make([]string, 0),
  75. tag: session.AsTag(tag),
  76. }
  77. p.Proxy.Verbose = false
  78. p.Proxy.Logger = dummyLogger{p}
  79. p.Proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  80. if p.doProxy(req) {
  81. if !p.isTLS {
  82. req.URL.Scheme = "http"
  83. }
  84. req.URL.Host = req.Host
  85. p.Proxy.ServeHTTP(w, req)
  86. }
  87. })
  88. p.Proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
  89. p.Proxy.OnRequest().DoFunc(p.onRequestFilter)
  90. p.Proxy.OnResponse().DoFunc(p.onResponseFilter)
  91. return p
  92. }
  93. func (p *HTTPProxy) Debug(format string, args ...interface{}) {
  94. p.Sess.Events.Log(log.DEBUG, p.tag+format, args...)
  95. }
  96. func (p *HTTPProxy) Info(format string, args ...interface{}) {
  97. p.Sess.Events.Log(log.INFO, p.tag+format, args...)
  98. }
  99. func (p *HTTPProxy) Warning(format string, args ...interface{}) {
  100. p.Sess.Events.Log(log.WARNING, p.tag+format, args...)
  101. }
  102. func (p *HTTPProxy) Error(format string, args ...interface{}) {
  103. p.Sess.Events.Log(log.ERROR, p.tag+format, args...)
  104. }
  105. func (p *HTTPProxy) Fatal(format string, args ...interface{}) {
  106. p.Sess.Events.Log(log.FATAL, p.tag+format, args...)
  107. }
  108. func (p *HTTPProxy) doProxy(req *http.Request) bool {
  109. if req.Host == "" {
  110. p.Error("got request with empty host: %v", req)
  111. return false
  112. }
  113. hostname := strings.Split(req.Host, ":")[0]
  114. for _, local := range []string{"localhost", "127.0.0.1"} {
  115. if hostname == local {
  116. p.Error("got request with localed host: %s", req.Host)
  117. return false
  118. }
  119. }
  120. return true
  121. }
  122. func (p *HTTPProxy) shouldProxy(req *http.Request) bool {
  123. hostname := strings.Split(req.Host, ":")[0]
  124. // check for the whitelist
  125. for _, expr := range p.Whitelist {
  126. if matched, err := filepath.Match(expr, hostname); err != nil {
  127. p.Error("error while using proxy whitelist expression '%s': %v", expr, err)
  128. } else if matched {
  129. p.Debug("hostname '%s' matched whitelisted element '%s'", hostname, expr)
  130. return true
  131. }
  132. }
  133. // then the blacklist
  134. for _, expr := range p.Blacklist {
  135. if matched, err := filepath.Match(expr, hostname); err != nil {
  136. p.Error("error while using proxy blacklist expression '%s': %v", expr, err)
  137. } else if matched {
  138. p.Debug("hostname '%s' matched blacklisted element '%s'", hostname, expr)
  139. return false
  140. }
  141. }
  142. return true
  143. }
  144. func (p *HTTPProxy) Configure(address string, proxyPort int, httpPort int, doRedirect bool, scriptPath string,
  145. jsToInject string, stripSSL bool) error {
  146. var err error
  147. // check if another http(s) proxy is using sslstrip and merge strippers
  148. if stripSSL {
  149. for _, mname := range []string{"http.proxy", "https.proxy"}{
  150. err, m := p.Sess.Module(mname)
  151. if err == nil && m.Running() {
  152. var mextra interface{}
  153. var mstripper *SSLStripper
  154. mextra = m.Extra()
  155. mextramap := mextra.(map[string]interface{})
  156. mstripper = mextramap["stripper"].(*SSLStripper)
  157. if mstripper != nil && mstripper.Enabled() {
  158. p.Info("found another proxy using sslstrip -> merging strippers...")
  159. p.Stripper = mstripper
  160. break
  161. }
  162. }
  163. }
  164. }
  165. p.Stripper.Enable(stripSSL)
  166. p.Address = address
  167. p.doRedirect = doRedirect
  168. p.jsHook = ""
  169. if strings.HasPrefix(jsToInject, "http://") || strings.HasPrefix(jsToInject, "https://") {
  170. p.jsHook = fmt.Sprintf("<script src=\"%s\" type=\"text/javascript\"></script></head>", jsToInject)
  171. } else if fs.Exists(jsToInject) {
  172. if data, err := ioutil.ReadFile(jsToInject); err != nil {
  173. return err
  174. } else {
  175. jsToInject = string(data)
  176. }
  177. }
  178. if p.jsHook == "" && jsToInject != "" {
  179. if !strings.HasPrefix(jsToInject, "<script ") {
  180. jsToInject = fmt.Sprintf("<script type=\"text/javascript\">%s</script>", jsToInject)
  181. }
  182. p.jsHook = fmt.Sprintf("%s</head>", jsToInject)
  183. }
  184. if scriptPath != "" {
  185. if err, p.Script = LoadHttpProxyScript(scriptPath, p.Sess); err != nil {
  186. return err
  187. } else {
  188. p.Debug("proxy script %s loaded.", scriptPath)
  189. }
  190. }
  191. p.Server = &http.Server{
  192. Addr: fmt.Sprintf("%s:%d", p.Address, proxyPort),
  193. Handler: p.Proxy,
  194. ReadTimeout: httpReadTimeout,
  195. WriteTimeout: httpWriteTimeout,
  196. }
  197. if p.doRedirect {
  198. if !p.Sess.Firewall.IsForwardingEnabled() {
  199. p.Info("enabling forwarding.")
  200. p.Sess.Firewall.EnableForwarding(true)
  201. }
  202. p.Redirection = firewall.NewRedirection(p.Sess.Interface.Name(),
  203. "TCP",
  204. httpPort,
  205. p.Address,
  206. proxyPort)
  207. if err := p.Sess.Firewall.EnableRedirection(p.Redirection, true); err != nil {
  208. return err
  209. }
  210. p.Debug("applied redirection %s", p.Redirection.String())
  211. } else {
  212. p.Warning("port redirection disabled, the proxy must be set manually to work")
  213. }
  214. p.Sess.UnkCmdCallback = func(cmd string) bool {
  215. if p.Script != nil {
  216. return p.Script.OnCommand(cmd)
  217. }
  218. return false
  219. }
  220. return nil
  221. }
  222. func (p *HTTPProxy) TLSConfigFromCA(ca *tls.Certificate) func(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) {
  223. return func(host string, ctx *goproxy.ProxyCtx) (c *tls.Config, err error) {
  224. parts := strings.SplitN(host, ":", 2)
  225. hostname := parts[0]
  226. port := 443
  227. if len(parts) > 1 {
  228. port, err = strconv.Atoi(parts[1])
  229. if err != nil {
  230. port = 443
  231. }
  232. }
  233. cert := getCachedCert(hostname, port)
  234. if cert == nil {
  235. p.Info("creating spoofed certificate for %s:%d", tui.Yellow(hostname), port)
  236. cert, err = btls.SignCertificateForHost(ca, hostname, port)
  237. if err != nil {
  238. p.Warning("cannot sign host certificate with provided CA: %s", err)
  239. return nil, err
  240. }
  241. setCachedCert(hostname, port, cert)
  242. } else {
  243. p.Debug("serving spoofed certificate for %s:%d", tui.Yellow(hostname), port)
  244. }
  245. config := tls.Config{
  246. InsecureSkipVerify: true,
  247. Certificates: []tls.Certificate{*cert},
  248. }
  249. return &config, nil
  250. }
  251. }
  252. func (p *HTTPProxy) ConfigureTLS(address string, proxyPort int, httpPort int, doRedirect bool, scriptPath string,
  253. certFile string,
  254. keyFile string, jsToInject string, stripSSL bool) (err error) {
  255. if err = p.Configure(address, proxyPort, httpPort, doRedirect, scriptPath, jsToInject, stripSSL); err != nil {
  256. return err
  257. }
  258. p.isTLS = true
  259. p.Name = "https.proxy"
  260. p.CertFile = certFile
  261. p.KeyFile = keyFile
  262. rawCert, _ := ioutil.ReadFile(p.CertFile)
  263. rawKey, _ := ioutil.ReadFile(p.KeyFile)
  264. ourCa, err := tls.X509KeyPair(rawCert, rawKey)
  265. if err != nil {
  266. return err
  267. }
  268. if ourCa.Leaf, err = x509.ParseCertificate(ourCa.Certificate[0]); err != nil {
  269. return err
  270. }
  271. goproxy.GoproxyCa = ourCa
  272. goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectAccept, TLSConfig: p.TLSConfigFromCA(&ourCa)}
  273. goproxy.MitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: p.TLSConfigFromCA(&ourCa)}
  274. goproxy.HTTPMitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectHTTPMitm, TLSConfig: p.TLSConfigFromCA(&ourCa)}
  275. goproxy.RejectConnect = &goproxy.ConnectAction{Action: goproxy.ConnectReject, TLSConfig: p.TLSConfigFromCA(&ourCa)}
  276. return nil
  277. }
  278. func (p *HTTPProxy) httpWorker() error {
  279. p.isRunning = true
  280. return p.Server.ListenAndServe()
  281. }
  282. type dumbResponseWriter struct {
  283. net.Conn
  284. }
  285. func (dumb dumbResponseWriter) Header() http.Header {
  286. panic("Header() should not be called on this ResponseWriter")
  287. }
  288. func (dumb dumbResponseWriter) Write(buf []byte) (int, error) {
  289. if bytes.Equal(buf, []byte("HTTP/1.0 200 OK\r\n\r\n")) {
  290. return len(buf), nil // throw away the HTTP OK response from the faux CONNECT request
  291. }
  292. return dumb.Conn.Write(buf)
  293. }
  294. func (dumb dumbResponseWriter) WriteHeader(code int) {
  295. panic("WriteHeader() should not be called on this ResponseWriter")
  296. }
  297. func (dumb dumbResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  298. return dumb, bufio.NewReadWriter(bufio.NewReader(dumb), bufio.NewWriter(dumb)), nil
  299. }
  300. func (p *HTTPProxy) httpsWorker() error {
  301. var err error
  302. // listen to the TLS ClientHello but make it a CONNECT request instead
  303. p.sniListener, err = net.Listen("tcp", p.Server.Addr)
  304. if err != nil {
  305. return err
  306. }
  307. p.isRunning = true
  308. for p.isRunning {
  309. c, err := p.sniListener.Accept()
  310. if err != nil {
  311. p.Warning("error accepting connection: %s.", err)
  312. continue
  313. }
  314. go func(c net.Conn) {
  315. now := time.Now()
  316. c.SetReadDeadline(now.Add(httpReadTimeout))
  317. c.SetWriteDeadline(now.Add(httpWriteTimeout))
  318. tlsConn, err := vhost.TLS(c)
  319. if err != nil {
  320. p.Warning("error reading SNI: %s.", err)
  321. return
  322. }
  323. hostname := tlsConn.Host()
  324. if hostname == "" {
  325. p.Warning("client does not support SNI.")
  326. return
  327. }
  328. p.Debug("proxying connection from %s to %s", tui.Bold(stripPort(c.RemoteAddr().String())), tui.Yellow(hostname))
  329. req := &http.Request{
  330. Method: "CONNECT",
  331. URL: &url.URL{
  332. Opaque: hostname,
  333. Host: net.JoinHostPort(hostname, "443"),
  334. },
  335. Host: hostname,
  336. Header: make(http.Header),
  337. RemoteAddr: c.RemoteAddr().String(),
  338. }
  339. p.Proxy.ServeHTTP(dumbResponseWriter{tlsConn}, req)
  340. }(c)
  341. }
  342. return nil
  343. }
  344. func (p *HTTPProxy) Start() {
  345. go func() {
  346. var err error
  347. strip := tui.Yellow("enabled")
  348. if !p.Stripper.Enabled() {
  349. strip = tui.Dim("disabled")
  350. }
  351. p.Info("started on %s (sslstrip %s)", p.Server.Addr, strip)
  352. if p.isTLS {
  353. err = p.httpsWorker()
  354. } else {
  355. err = p.httpWorker()
  356. }
  357. if err != nil && err.Error() != "http: Server closed" {
  358. p.Fatal("%s", err)
  359. }
  360. }()
  361. }
  362. func (p *HTTPProxy) Stop() error {
  363. if p.doRedirect && p.Redirection != nil {
  364. p.Debug("disabling redirection %s", p.Redirection.String())
  365. if err := p.Sess.Firewall.EnableRedirection(p.Redirection, false); err != nil {
  366. return err
  367. }
  368. p.Redirection = nil
  369. }
  370. p.Sess.UnkCmdCallback = nil
  371. if p.isTLS {
  372. p.isRunning = false
  373. p.sniListener.Close()
  374. return nil
  375. } else {
  376. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  377. defer cancel()
  378. return p.Server.Shutdown(ctx)
  379. }
  380. }