1. Use better way to parse gateway on windows.

2. Fix untrusted cert on iOS 13 and up.
3. Allow to download cer/pem accessing to `mitm.it`.
This commit is contained in:
WangYi 2020-08-03 21:23:25 +08:00
commit 331d56c406
8 changed files with 132 additions and 39 deletions

View file

@ -24,6 +24,7 @@ type DNSSpoofer struct {
Hosts Hosts Hosts Hosts
TTL uint32 TTL uint32
All bool All bool
ResolveAll bool
waitGroup *sync.WaitGroup waitGroup *sync.WaitGroup
pktSourceChan chan gopacket.Packet pktSourceChan chan gopacket.Packet
} }
@ -33,6 +34,7 @@ func NewDNSSpoofer(s *session.Session) *DNSSpoofer {
SessionModule: session.NewSessionModule("dns.spoof", s), SessionModule: session.NewSessionModule("dns.spoof", s),
Handle: nil, Handle: nil,
All: false, All: false,
ResolveAll: true,
Hosts: Hosts{}, Hosts: Hosts{},
TTL: 1024, TTL: 1024,
waitGroup: &sync.WaitGroup{}, waitGroup: &sync.WaitGroup{},
@ -64,6 +66,10 @@ func NewDNSSpoofer(s *session.Session) *DNSSpoofer {
"^[0-9]+$", "^[0-9]+$",
"TTL of spoofed DNS replies.")) "TTL of spoofed DNS replies."))
mod.AddParam(session.NewBoolParameter("dns.spoof.resolve_all",
"true",
"If true the module will resolve every DNS request, even if it is not covered by spoof list."))
mod.AddHandler(session.NewModuleHandler("dns.spoof on", "", mod.AddHandler(session.NewModuleHandler("dns.spoof on", "",
"Start the DNS spoofer in the background.", "Start the DNS spoofer in the background.",
func(args []string) error { func(args []string) error {
@ -106,6 +112,8 @@ func (mod *DNSSpoofer) Configure() error {
return err return err
} else if err, mod.All = mod.BoolParam("dns.spoof.all"); err != nil { } else if err, mod.All = mod.BoolParam("dns.spoof.all"); err != nil {
return err return err
} else if err, mod.ResolveAll = mod.BoolParam("dns.spoof.resolve_all"); err != nil {
return err
} else if err, address = mod.IPParam("dns.spoof.address"); err != nil { } else if err, address = mod.IPParam("dns.spoof.address"); err != nil {
return err return err
} else if err, domains = mod.ListParam("dns.spoof.domains"); err != nil { } else if err, domains = mod.ListParam("dns.spoof.domains"); err != nil {
@ -287,6 +295,16 @@ func (mod *DNSSpoofer) onPacket(pkt gopacket.Packet) {
} }
break break
} else { } else {
if mod.ResolveAll {
ips, err := net.LookupIP(qName)
if err == nil && len(ips) > 0 {
redir, who := DnsReply(mod.Session, mod.TTL, pkt, eth, udp, qName, ips[0], dns, eth.SrcMAC)
if redir != "" && who != "" {
mod.Info("sending forward DNS reply for %s %s to %s.", tui.Red(qName), tui.Dim(redir), tui.Bold(who))
}
break
}
}
mod.Debug("skipping domain %s", qName) mod.Debug("skipping domain %s", qName)
} }
} }
@ -306,7 +324,7 @@ func (mod *DNSSpoofer) Start() error {
src := gopacket.NewPacketSource(mod.Handle, mod.Handle.LinkType()) src := gopacket.NewPacketSource(mod.Handle, mod.Handle.LinkType())
mod.pktSourceChan = src.Packets() mod.pktSourceChan = src.Packets()
for packet := range mod.pktSourceChan { for packet := range mod.pktSourceChan {
if !mod.Running() { if !mod.Running() || packet == nil {
break break
} }

View file

@ -2,6 +2,7 @@ package http_proxy
import ( import (
"github.com/bettercap/bettercap/session" "github.com/bettercap/bettercap/session"
"github.com/evilsocket/islazy/fs"
"github.com/evilsocket/islazy/str" "github.com/evilsocket/islazy/str"
) )
@ -44,6 +45,11 @@ func NewHttpProxy(s *session.Session) *HttpProxy {
"", "",
"URL, path or javascript code to inject into every HTML page.")) "URL, path or javascript code to inject into every HTML page."))
mod.AddParam(session.NewStringParameter("http.proxy.certificate",
"~/.bettercap-ca.cert.pem",
"",
"HTTP proxy certification authority TLS certificate file."))
mod.AddParam(session.NewStringParameter("http.proxy.blacklist", "", "", mod.AddParam(session.NewStringParameter("http.proxy.blacklist", "", "",
"Comma separated list of hostnames to skip while proxying (wildcard expressions can be used).")) "Comma separated list of hostnames to skip while proxying (wildcard expressions can be used)."))
@ -94,6 +100,7 @@ func (mod *HttpProxy) Configure() error {
var httpPort int var httpPort int
var doRedirect bool var doRedirect bool
var scriptPath string var scriptPath string
var certFile string
var stripSSL bool var stripSSL bool
var useIDN bool var useIDN bool
var jsToInject string var jsToInject string
@ -118,6 +125,10 @@ func (mod *HttpProxy) Configure() error {
return err return err
} else if err, jsToInject = mod.StringParam("http.proxy.injectjs"); err != nil { } else if err, jsToInject = mod.StringParam("http.proxy.injectjs"); err != nil {
return err return err
} else if err, certFile = mod.StringParam("http.proxy.certificate"); err != nil {
return err
} else if certFile, err = fs.Expand(certFile); err != nil {
return err
} else if err, blacklist = mod.StringParam("http.proxy.blacklist"); err != nil { } else if err, blacklist = mod.StringParam("http.proxy.blacklist"); err != nil {
return err return err
} else if err, whitelist = mod.StringParam("http.proxy.whitelist"); err != nil { } else if err, whitelist = mod.StringParam("http.proxy.whitelist"); err != nil {
@ -127,7 +138,7 @@ func (mod *HttpProxy) Configure() error {
mod.proxy.Blacklist = str.Comma(blacklist) mod.proxy.Blacklist = str.Comma(blacklist)
mod.proxy.Whitelist = str.Comma(whitelist) mod.proxy.Whitelist = str.Comma(whitelist)
error := mod.proxy.Configure(address, proxyPort, httpPort, doRedirect, scriptPath, jsToInject, stripSSL, useIDN) error := mod.proxy.Configure(address, proxyPort, httpPort, doRedirect, scriptPath, jsToInject, stripSSL, useIDN, certFile)
// save stripper to share it with other http(s) proxies // save stripper to share it with other http(s) proxies
mod.State.Store("stripper", mod.proxy.Stripper) mod.State.Store("stripper", mod.proxy.Stripper)

View file

@ -94,9 +94,14 @@ func NewHTTPProxy(s *session.Session, tag string) *HTTPProxy {
if !p.isTLS { if !p.isTLS {
req.URL.Scheme = "http" req.URL.Scheme = "http"
} }
// Clone it.
req.URL.Host = req.Host req.URL.Host = req.Host
if req.Host == "mitm.it" {
p.GetCert(w, req)
} else {
p.Proxy.ServeHTTP(w, req) p.Proxy.ServeHTTP(w, req)
} }
}
}) })
p.Proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) p.Proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
@ -106,6 +111,24 @@ func NewHTTPProxy(s *session.Session, tag string) *HTTPProxy {
return p return p
} }
func (p *HTTPProxy) GetCert(w http.ResponseWriter, req *http.Request) {
if p.CertFile != "" {
respHeader := w.Header()
respHeader.Set("Content-Type", "application/x-x509-ca-cert")
if strings.Index(req.UserAgent(), "Windows NT") != -1 {
// Return cer certificate format.
respHeader.Set("Content-Disposition", "inline; filename=ca-cert.cer")
} else {
// Return pem certificate format.
respHeader.Set("Content-Disposition", "inline; filename=ca-cert.pem")
}
rawCert, _ := ioutil.ReadFile(p.CertFile)
_, _ = w.Write(rawCert)
} else {
http.NotFound(w, req)
}
}
func (p *HTTPProxy) Debug(format string, args ...interface{}) { func (p *HTTPProxy) Debug(format string, args ...interface{}) {
p.Sess.Events.Log(log.DEBUG, p.tag+format, args...) p.Sess.Events.Log(log.DEBUG, p.tag+format, args...)
} }
@ -170,7 +193,7 @@ func (p *HTTPProxy) shouldProxy(req *http.Request) bool {
} }
func (p *HTTPProxy) Configure(address string, proxyPort int, httpPort int, doRedirect bool, scriptPath string, func (p *HTTPProxy) Configure(address string, proxyPort int, httpPort int, doRedirect bool, scriptPath string,
jsToInject string, stripSSL bool, useIDN bool) error { jsToInject string, stripSSL bool, useIDN bool, certFile string) error {
var err error var err error
// check if another http(s) proxy is using sslstrip and merge strippers // check if another http(s) proxy is using sslstrip and merge strippers
@ -197,6 +220,8 @@ func (p *HTTPProxy) Configure(address string, proxyPort int, httpPort int, doRed
p.doRedirect = doRedirect p.doRedirect = doRedirect
p.jsHook = "" p.jsHook = ""
p.CertFile = certFile
if strings.HasPrefix(jsToInject, "http://") || strings.HasPrefix(jsToInject, "https://") { if strings.HasPrefix(jsToInject, "http://") || strings.HasPrefix(jsToInject, "https://") {
p.jsHook = fmt.Sprintf("<script src=\"%s\" type=\"text/javascript\"></script></head>", jsToInject) p.jsHook = fmt.Sprintf("<script src=\"%s\" type=\"text/javascript\"></script></head>", jsToInject)
} else if fs.Exists(jsToInject) { } else if fs.Exists(jsToInject) {
@ -298,7 +323,7 @@ func (p *HTTPProxy) TLSConfigFromCA(ca *tls.Certificate) func(host string, ctx *
func (p *HTTPProxy) ConfigureTLS(address string, proxyPort int, httpPort int, doRedirect bool, scriptPath string, func (p *HTTPProxy) ConfigureTLS(address string, proxyPort int, httpPort int, doRedirect bool, scriptPath string,
certFile string, certFile string,
keyFile string, jsToInject string, stripSSL bool, useIDN bool) (err error) { keyFile string, jsToInject string, stripSSL bool, useIDN bool) (err error) {
if err = p.Configure(address, proxyPort, httpPort, doRedirect, scriptPath, jsToInject, stripSSL, useIDN); err != nil { if err = p.Configure(address, proxyPort, httpPort, doRedirect, scriptPath, jsToInject, stripSSL, useIDN, certFile); err != nil {
return err return err
} }

View file

@ -303,7 +303,7 @@ func SetInterfaceTxPower(name string, txpower int) error {
} }
func GatewayProvidedByUser(iface *Endpoint, gateway string) (*Endpoint, error) { func GatewayProvidedByUser(iface *Endpoint, gateway string) (*Endpoint, error) {
Debug("GatewayProvidedByUser(%s) [cmd=%v opts=%v parser=%v]", gateway, IPv4RouteCmd, IPv4RouteCmdOpts, IPv4RouteParser) Debug("GatewayProvidedByUser(%s) [cmd=%v opts=%v]", gateway, IPv4RouteCmd, IPv4RouteCmdOpts)
if IPv4Validator.MatchString(gateway) { if IPv4Validator.MatchString(gateway) {
Debug("valid gateway ip %s", gateway) Debug("valid gateway ip %s", gateway)
// we have the address, now we need its mac // we have the address, now we need its mac

View file

@ -3,17 +3,17 @@
package network package network
import ( import (
"bufio"
"fmt"
"strings" "strings"
"github.com/bettercap/bettercap/core" "github.com/bettercap/bettercap/core"
"github.com/evilsocket/islazy/str"
) )
func FindGateway(iface *Endpoint) (*Endpoint, error) { func FindGateway(iface *Endpoint) (*Endpoint, error) {
Debug("FindGateway(%s) [cmd=%v opts=%v parser=%v]", iface.Name(), IPv4RouteCmd, IPv4RouteCmdOpts, IPv4RouteParser) Debug("FindGateway(%s) [cmd=%v opts=%v]", iface.Name(), IPv4RouteCmd, IPv4RouteCmdOpts)
output, err := core.Exec(IPv4RouteCmd, IPv4RouteCmdOpts) output, err := core.ExecInEnglish(IPv4RouteCmd, append(IPv4RouteCmdOpts, fmt.Sprintf("%d", iface.Index)))
if err != nil { if err != nil {
Debug("FindGateway(%s): core.Exec failed with %s", err) Debug("FindGateway(%s): core.Exec failed with %s", err)
return nil, err return nil, err
@ -22,25 +22,24 @@ func FindGateway(iface *Endpoint) (*Endpoint, error) {
Debug("FindGateway(%s) output:\n%s", iface.Name(), output) Debug("FindGateway(%s) output:\n%s", iface.Name(), output)
ifName := iface.Name() ifName := iface.Name()
for _, line := range strings.Split(output, "\n") { scanner := bufio.NewScanner(strings.NewReader(output))
if line = str.Trim(line); strings.Contains(line, ifName) { for scanner.Scan() {
m := IPv4RouteParser.FindStringSubmatch(line) keyPair := strings.Split(scanner.Text(), ":")
if len(m) >= IPv4RouteTokens { if len(keyPair) != 2 {
Debug("FindGateway(%s) line '%s' matched with %v", iface.Name(), line, m) continue
return IPv4RouteIsGateway(ifName, m, func(gateway string) (*Endpoint, error) { }
if gateway == iface.IpAddress { key, value := strings.TrimSpace(keyPair[0]), strings.TrimSpace(keyPair[1])
Debug("gateway is the interface") if key == "Default Gateway" {
if value == iface.IpAddress {
return iface, nil return iface, nil
} else { } else {
// we have the address, now we need its mac // we have the address, now we need its mac
mac, err := ArpLookup(ifName, gateway, false) mac, err := ArpLookup(ifName, value, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
Debug("gateway is %s[%s]", gateway, mac) Debug("gateway is %s[%s]", value, mac)
return NewEndpoint(gateway, mac), nil return NewEndpoint(value, mac), nil
}
})
} }
} }
} }

View file

@ -3,17 +3,14 @@ package network
import ( import (
"fmt" "fmt"
"net" "net"
"regexp"
"strings" "strings"
"github.com/google/gopacket/pcap" "github.com/google/gopacket/pcap"
) )
// only matches gateway lines // only matches gateway lines
var IPv4RouteParser = regexp.MustCompile("^.+\\s+.+\\s+\\d+\\s+([0-9\\.]+/\\d+)\\s+\\d+\\s+([0-9\\.]+).*$")
var IPv4RouteTokens = 3
var IPv4RouteCmd = "netsh" var IPv4RouteCmd = "netsh"
var IPv4RouteCmdOpts = []string{"interface", "ipv4", "show", "route"} var IPv4RouteCmdOpts = []string{"interface", "ipv4", "show", "address"}
func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) (*Endpoint, error)) (*Endpoint, error) { func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) (*Endpoint, error)) (*Endpoint, error) {
// TODO check if the subnet is the same as iface ? // TODO check if the subnet is the same as iface ?

View file

@ -3,8 +3,10 @@ package tls
import ( import (
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha1"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1"
"encoding/pem" "encoding/pem"
"math/big" "math/big"
"os" "os"
@ -74,6 +76,17 @@ func CertConfigFromModule(prefix string, m session.SessionModule) (cfg CertConfi
return cfg, err return cfg, err
} }
var oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
var oidNetscapeCertType = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 1, 1}
var netscapeCetSSLCA = []byte{3, 2, 2, 4}
type publicKeyInfo struct {
Raw asn1.RawContent
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
func CreateCertificate(cfg CertConfig, ca bool) (*rsa.PrivateKey, []byte, error) { func CreateCertificate(cfg CertConfig, ca bool) (*rsa.PrivateKey, []byte, error) {
priv, err := rsa.GenerateKey(rand.Reader, cfg.Bits) priv, err := rsa.GenerateKey(rand.Reader, cfg.Bits)
if err != nil { if err != nil {
@ -100,11 +113,41 @@ func CreateCertificate(cfg CertConfig, ca bool) (*rsa.PrivateKey, []byte, error)
}, },
NotBefore: notBefore, NotBefore: notBefore,
NotAfter: notAfter, NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageEmailProtection, x509.ExtKeyUsageTimeStamping, x509.ExtKeyUsageMicrosoftCommercialCodeSigning, x509.ExtKeyUsageMicrosoftServerGatedCrypto, x509.ExtKeyUsageNetscapeServerGatedCrypto},
BasicConstraintsValid: true, BasicConstraintsValid: true,
IsCA: ca, IsCA: ca,
} }
// We can only remove this once we move to go 1.15.
if ca && len(template.SubjectKeyId) == 0 {
// SubjectKeyId generated using method 1 in RFC 5280, Section 4.2.1.2
publicKeyBytes, err := asn1.Marshal(priv.PublicKey)
if err != nil {
return nil, nil, err
}
// This is a NULL parameters value which is required by
// RFC 3279, Section 2.3.1.
publicKeyAlgorithm := pkix.AlgorithmIdentifier{
Algorithm: oidPublicKeyRSA,
Parameters: asn1.NullRawValue,
}
encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}
pki := publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey}
b, err := asn1.Marshal(pki)
if err != nil {
return nil, nil, err
}
h := sha1.Sum(b)
template.SubjectKeyId = h[:]
}
if ca {
template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{
Id: oidNetscapeCertType,
Critical: false,
Value: netscapeCetSSLCA,
})
}
cert, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) cert, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil { if err != nil {

View file

@ -63,7 +63,7 @@ func SignCertificateForHost(ca *tls.Certificate, host string, port int) (cert *t
}, },
NotBefore: notBefore, NotBefore: notBefore,
NotAfter: notAfter, NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true, BasicConstraintsValid: true,
} }
@ -90,7 +90,7 @@ func SignCertificateForHost(ca *tls.Certificate, host string, port int) (cert *t
} }
var certpriv *rsa.PrivateKey var certpriv *rsa.PrivateKey
if certpriv, err = rsa.GenerateKey(rand.Reader, 1024); err != nil { if certpriv, err = rsa.GenerateKey(rand.Reader, 2048); err != nil {
return return
} }