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
TTL uint32
All bool
ResolveAll bool
waitGroup *sync.WaitGroup
pktSourceChan chan gopacket.Packet
}
@ -33,6 +34,7 @@ func NewDNSSpoofer(s *session.Session) *DNSSpoofer {
SessionModule: session.NewSessionModule("dns.spoof", s),
Handle: nil,
All: false,
ResolveAll: true,
Hosts: Hosts{},
TTL: 1024,
waitGroup: &sync.WaitGroup{},
@ -64,6 +66,10 @@ func NewDNSSpoofer(s *session.Session) *DNSSpoofer {
"^[0-9]+$",
"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", "",
"Start the DNS spoofer in the background.",
func(args []string) error {
@ -106,6 +112,8 @@ func (mod *DNSSpoofer) Configure() error {
return err
} else if err, mod.All = mod.BoolParam("dns.spoof.all"); err != nil {
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 {
return err
} else if err, domains = mod.ListParam("dns.spoof.domains"); err != nil {
@ -287,6 +295,16 @@ func (mod *DNSSpoofer) onPacket(pkt gopacket.Packet) {
}
break
} 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)
}
}
@ -306,7 +324,7 @@ func (mod *DNSSpoofer) Start() error {
src := gopacket.NewPacketSource(mod.Handle, mod.Handle.LinkType())
mod.pktSourceChan = src.Packets()
for packet := range mod.pktSourceChan {
if !mod.Running() {
if !mod.Running() || packet == nil {
break
}

View file

@ -2,6 +2,7 @@ package http_proxy
import (
"github.com/bettercap/bettercap/session"
"github.com/evilsocket/islazy/fs"
"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."))
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", "", "",
"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 doRedirect bool
var scriptPath string
var certFile string
var stripSSL bool
var useIDN bool
var jsToInject string
@ -118,6 +125,10 @@ func (mod *HttpProxy) Configure() error {
return err
} else if err, jsToInject = mod.StringParam("http.proxy.injectjs"); err != nil {
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 {
return err
} 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.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
mod.State.Store("stripper", mod.proxy.Stripper)

View file

@ -94,9 +94,14 @@ func NewHTTPProxy(s *session.Session, tag string) *HTTPProxy {
if !p.isTLS {
req.URL.Scheme = "http"
}
// Clone it.
req.URL.Host = req.Host
if req.Host == "mitm.it" {
p.GetCert(w, req)
} else {
p.Proxy.ServeHTTP(w, req)
}
}
})
p.Proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
@ -106,6 +111,24 @@ func NewHTTPProxy(s *session.Session, tag string) *HTTPProxy {
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{}) {
p.Sess.Events.Log(log.DEBUG, p.tag+format, args...)
}
@ -170,12 +193,12 @@ func (p *HTTPProxy) shouldProxy(req *http.Request) bool {
}
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
// check if another http(s) proxy is using sslstrip and merge strippers
if stripSSL {
for _, mname := range []string{"http.proxy", "https.proxy"}{
for _, mname := range []string{"http.proxy", "https.proxy"} {
err, m := p.Sess.Module(mname)
if err == nil && m.Running() {
var mextra interface{}
@ -197,6 +220,8 @@ func (p *HTTPProxy) Configure(address string, proxyPort int, httpPort int, doRed
p.doRedirect = doRedirect
p.jsHook = ""
p.CertFile = certFile
if strings.HasPrefix(jsToInject, "http://") || strings.HasPrefix(jsToInject, "https://") {
p.jsHook = fmt.Sprintf("<script src=\"%s\" type=\"text/javascript\"></script></head>", 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,
certFile string,
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
}

View file

@ -303,7 +303,7 @@ func SetInterfaceTxPower(name string, txpower int) 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) {
Debug("valid gateway ip %s", gateway)
// we have the address, now we need its mac

View file

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

View file

@ -3,17 +3,14 @@ package network
import (
"fmt"
"net"
"regexp"
"strings"
"github.com/google/gopacket/pcap"
)
// 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 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) {
// TODO check if the subnet is the same as iface ?

View file

@ -3,8 +3,10 @@ package tls
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"math/big"
"os"
@ -74,6 +76,17 @@ func CertConfigFromModule(prefix string, m session.SessionModule) (cfg CertConfi
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) {
priv, err := rsa.GenerateKey(rand.Reader, cfg.Bits)
if err != nil {
@ -100,11 +113,41 @@ func CreateCertificate(cfg CertConfig, ca bool) (*rsa.PrivateKey, []byte, error)
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageEmailProtection, x509.ExtKeyUsageTimeStamping, x509.ExtKeyUsageMicrosoftCommercialCodeSigning, x509.ExtKeyUsageMicrosoftServerGatedCrypto, x509.ExtKeyUsageNetscapeServerGatedCrypto},
BasicConstraintsValid: true,
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)
if err != nil {

View file

@ -63,7 +63,7 @@ func SignCertificateForHost(ca *tls.Certificate, host string, port int) (cert *t
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
@ -90,7 +90,7 @@ func SignCertificateForHost(ca *tls.Certificate, host string, port int) (cert *t
}
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
}