mirror of
https://github.com/bettercap/bettercap.git
synced 2025-03-12 04:36:03 -07:00
178 lines
3.5 KiB
Go
178 lines
3.5 KiB
Go
package firewall
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/bettercap/bettercap/core"
|
|
"github.com/bettercap/bettercap/network"
|
|
|
|
"github.com/evilsocket/islazy/str"
|
|
)
|
|
|
|
var (
|
|
sysCtlParser = regexp.MustCompile(`([^:]+):\s*(.+)`)
|
|
pfFilePath = fmt.Sprintf("/tmp/bcap_pf_%d.conf", os.Getpid())
|
|
)
|
|
|
|
type PfFirewall struct {
|
|
iface *network.Endpoint
|
|
filename string
|
|
forwarding bool
|
|
enabled bool
|
|
}
|
|
|
|
func Make(iface *network.Endpoint) FirewallManager {
|
|
firewall := &PfFirewall{
|
|
iface: iface,
|
|
filename: pfFilePath,
|
|
forwarding: false,
|
|
enabled: false,
|
|
}
|
|
|
|
firewall.forwarding = firewall.IsForwardingEnabled()
|
|
|
|
return firewall
|
|
}
|
|
|
|
func (f PfFirewall) sysCtlRead(param string) (string, error) {
|
|
if out, err := core.Exec("sysctl", []string{param}); err != nil {
|
|
return "", err
|
|
} else if m := sysCtlParser.FindStringSubmatch(out); len(m) == 3 && m[1] == param {
|
|
return m[2], nil
|
|
} else {
|
|
return "", fmt.Errorf("Unexpected sysctl output: %s", out)
|
|
}
|
|
}
|
|
|
|
func (f PfFirewall) sysCtlWrite(param string, value string) (string, error) {
|
|
args := []string{"-w", fmt.Sprintf("%s=%s", param, value)}
|
|
_, err := core.Exec("sysctl", args)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// make sure we actually wrote the value
|
|
if out, err := f.sysCtlRead(param); err != nil {
|
|
return "", err
|
|
} else if out != value {
|
|
return "", fmt.Errorf("Expected value for '%s' is %s, found %s", param, value, out)
|
|
} else {
|
|
return out, nil
|
|
}
|
|
}
|
|
|
|
func (f PfFirewall) IsForwardingEnabled() bool {
|
|
out, err := f.sysCtlRead("net.inet.ip.forwarding")
|
|
if err != nil {
|
|
log.Printf("ERROR: %s", err)
|
|
return false
|
|
}
|
|
|
|
return strings.HasSuffix(out, ": 1")
|
|
}
|
|
|
|
func (f PfFirewall) enableParam(param string, enabled bool) error {
|
|
var value string
|
|
if enabled {
|
|
value = "1"
|
|
} else {
|
|
value = "0"
|
|
}
|
|
|
|
if _, err := f.sysCtlWrite(param, value); err != nil {
|
|
return err
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (f PfFirewall) EnableForwarding(enabled bool) error {
|
|
return f.enableParam("net.inet.ip.forwarding", enabled)
|
|
}
|
|
|
|
func (f PfFirewall) generateRule(r *Redirection) string {
|
|
src_a := "any"
|
|
dst_a := "any"
|
|
|
|
if r.SrcAddress != "" {
|
|
src_a = r.SrcAddress
|
|
}
|
|
|
|
if r.DstAddress != "" {
|
|
dst_a = r.DstAddress
|
|
}
|
|
|
|
return fmt.Sprintf("rdr pass on %s proto %s from any to %s port %d -> %s port %d",
|
|
r.Interface, r.Protocol, src_a, r.SrcPort, dst_a, r.DstPort)
|
|
}
|
|
|
|
func (f *PfFirewall) enable(enabled bool) {
|
|
f.enabled = enabled
|
|
if enabled {
|
|
core.Exec("pfctl", []string{"-e"})
|
|
} else {
|
|
core.Exec("pfctl", []string{"-d"})
|
|
}
|
|
}
|
|
|
|
func (f PfFirewall) EnableRedirection(r *Redirection, enabled bool) error {
|
|
rule := f.generateRule(r)
|
|
|
|
if enabled {
|
|
fd, err := os.OpenFile(f.filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fd.Close()
|
|
|
|
if _, err = fd.WriteString(rule + "\n"); err != nil {
|
|
return err
|
|
}
|
|
|
|
// enable pf
|
|
f.enable(true)
|
|
|
|
// load the rule
|
|
if _, err := core.Exec("pfctl", []string{"-f", f.filename}); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
fd, err := os.Open(f.filename)
|
|
if err == nil {
|
|
defer fd.Close()
|
|
|
|
lines := ""
|
|
scanner := bufio.NewScanner(fd)
|
|
for scanner.Scan() {
|
|
line := str.Trim(scanner.Text())
|
|
if line != rule {
|
|
lines += line + "\n"
|
|
}
|
|
}
|
|
|
|
if str.Trim(lines) == "" {
|
|
os.Remove(f.filename)
|
|
f.enable(false)
|
|
} else {
|
|
ioutil.WriteFile(f.filename, []byte(lines), 0600)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f PfFirewall) Restore() {
|
|
f.EnableForwarding(f.forwarding)
|
|
if f.enabled {
|
|
f.enable(false)
|
|
}
|
|
os.Remove(f.filename)
|
|
}
|