Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- [Transparent proxy](#transparent-proxy)
- [redirect (via NAT and SO_ORIGINAL_DST)](#redirect-via-nat-and-so_original_dst)
- [tproxy (via MANGLE and IP_TRANSPARENT)](#tproxy-via-mangle-and-ip_transparent)
- [ARP spoofing](#arp-spoofing)
- [Traffic sniffing](#traffic-sniffing)
- [JSON format](#json-format)
- [Colored format](#colored-format)
Expand Down Expand Up @@ -97,7 +98,7 @@ You can download the binary for your platform from [Releases](https://github.com
Example:

```shell
HPTS_RELEASE=v1.8.3; wget -v https://github.com/shadowy-pycoder/go-http-proxy-to-socks/releases/download/$HPTS_RELEASE/gohpts-$HPTS_RELEASE-linux-amd64.tar.gz -O gohpts && tar xvzf gohpts && mv -f gohpts-$HPTS_RELEASE-linux-amd64 gohpts && ./gohpts -h
HPTS_RELEASE=v1.8.4; wget -v https://github.com/shadowy-pycoder/go-http-proxy-to-socks/releases/download/$HPTS_RELEASE/gohpts-$HPTS_RELEASE-linux-amd64.tar.gz -O gohpts && tar xvzf gohpts && mv -f gohpts-$HPTS_RELEASE-linux-amd64 gohpts && ./gohpts -h
```

Alternatively, you can install it using `go install` command (requires Go [1.24](https://go.dev/doc/install) or later):
Expand Down Expand Up @@ -164,6 +165,7 @@ Options:
-T Address of transparent proxy server (no HTTP)
-M Transparent proxy mode: (redirect, tproxy)
-auto Automatically setup iptables for transparent proxy (requires elevated privileges)
-arp Automatically setup iptables to proxy ARP spoofed traffic (use tools like bettercap to perform actual attack)
-mark Set mark for each packet sent through transparent proxy (Default: redirect 0, tproxy 100)
```

Expand Down Expand Up @@ -479,6 +481,25 @@ else
fi
```

### ARP spoofing

`GoHPTS` can be used with tools like [Bettercap](https://github.com/bettercap/bettercap) to proxy ARP spoofed traffic.

Run the proxy with `-arp` flag

```shell
ssh remote -D 1080 -Nf
sudo env PATH=$PATH gohpts -d -T 8888 -M tproxy -sniff -body -auto -mark 100 -arp
```

Run `bettercap`

```shell
sudo bettercap -eval "set net.probe on;set net.recon on;arp.spoof on"
```

Check proxy logs for traffic from other devices from your LAN

## Traffic sniffing

[[Back]](#table-of-contents)
Expand Down
12 changes: 12 additions & 0 deletions cmd/gohpts/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const usageTproxy string = `
-T Address of transparent proxy server (no HTTP)
-M Transparent proxy mode: (redirect, tproxy)
-auto Automatically setup iptables for transparent proxy (requires elevated privileges)
-arp Automatically setup iptables to proxy ARP spoofed traffic (use tools like bettercap to perform actual attack)
-mark Set mark for each packet sent through transparent proxy (Default: redirect 0, tproxy 100)
`

Expand Down Expand Up @@ -113,6 +114,12 @@ func root(args []string) error {
0,
"Set mark for each packet sent through transparent proxy (Default: redirect 0, tproxy 100)",
)
flags.BoolVar(
&conf.ARP,
"arp",
false,
"Automatically setup iptables to proxy ARP spoofed traffic (use tools like bettercap to perform actual attack)",
)
}
flags.StringVar(&conf.LogFilePath, "logfile", "", "Log file path (Default: stdout)")
flags.BoolVar(&conf.Debug, "d", false, "Show logs in DEBUG mode")
Expand Down Expand Up @@ -172,6 +179,11 @@ func root(args []string) error {
return fmt.Errorf("-mark requires -t or -T flag")
}
}
if seen["arp"] {
if !seen["auto"] {
return fmt.Errorf("-arp requires -auto flag")
}
}
if seen["f"] {
for _, da := range []string{"s", "u", "U", "c", "k", "l"} {
if seen[da] {
Expand Down
55 changes: 53 additions & 2 deletions gohpts.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ type Config struct {
TProxyMode string
Auto bool
Mark uint
ARP bool
LogFilePath string
Debug bool
JSON bool
Expand All @@ -120,6 +121,7 @@ type proxyapp struct {
tproxyMode string
auto bool
mark uint
arp bool
user string
pass string
proxychain chain
Expand Down Expand Up @@ -1361,10 +1363,53 @@ func (p *proxyapp) applyRedirectRules() string {
cmdForward.Stdout = os.Stdout
cmdForward.Stderr = os.Stderr
_ = cmdForward.Run()
if p.arp {
cmdClear := exec.Command("bash", "-c", `
set -ex
iptables -t filter -F GOHPTS 2>/dev/null || true
iptables -t filter -D FORWARD -j GOHPTS 2>/dev/null || true
iptables -t filter -X GOHPTS 2>/dev/null || true
`)
cmdClear.Stdout = os.Stdout
cmdClear.Stderr = os.Stderr
if err := cmdClear.Run(); err != nil {
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
}
iface, err := getDefaultInterface()
if err != nil {
p.logger.Fatal().Err(err).Msg("failed getting default network interface")
}
cmdForward := exec.Command("bash", "-c", fmt.Sprintf(`
set -ex
iptables -t filter -N GOHPTS 2>/dev/null
iptables -t filter -F GOHPTS
iptables -t filter -A FORWARD -j GOHPTS
iptables -t filter -A GOHPTS -i %s -j ACCEPT
iptables -t filter -A GOHPTS -o %s -j ACCEPT
`, iface.Name, iface.Name))
cmdForward.Stdout = os.Stdout
cmdForward.Stderr = os.Stderr
if err := cmdForward.Run(); err != nil {
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
}
}
return string(output)
}

func (p *proxyapp) clearRedirectRules(output string) error {
if p.arp {
cmdClear := exec.Command("bash", "-c", `
set -ex
iptables -t filter -F GOHPTS 2>/dev/null || true
iptables -t filter -D FORWARD -j GOHPTS 2>/dev/null || true
iptables -t filter -X GOHPTS 2>/dev/null || true
`)
cmdClear.Stdout = os.Stdout
cmdClear.Stderr = os.Stderr
if err := cmdClear.Run(); err != nil {
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
}
}
var cmd *exec.Cmd
switch p.tproxyMode {
case "redirect":
Expand Down Expand Up @@ -1701,7 +1746,7 @@ func New(conf *Config) *proxyapp {
p.tproxyMode = conf.TProxyMode
tproxyonly := conf.TProxyOnly != ""
if tproxyonly {
if p.tproxyMode == "tproxy" {
if p.tproxyMode != "" {
p.tproxyAddr, err = getFullAddress(conf.TProxyOnly, true)
if err != nil {
p.logger.Fatal().Err(err).Msg("")
Expand All @@ -1713,7 +1758,7 @@ func New(conf *Config) *proxyapp {
}
}
} else {
if p.tproxyMode == "tproxy" {
if p.tproxyMode != "" {
p.tproxyAddr, err = getFullAddress(conf.TProxy, true)
if err != nil {
p.logger.Fatal().Err(err).Msg("")
Expand All @@ -1739,6 +1784,12 @@ func New(conf *Config) *proxyapp {
if p.mark == 0 && p.tproxyMode == "tproxy" {
p.mark = 100
}
p.arp = conf.ARP
if p.arp && runtime.GOOS != "linux" {
p.logger.Fatal().Msg("ARP setup is available only for linux system")
} else if p.arp && !p.auto {
p.logger.Fatal().Msg("ARP setup requires auto configuration")
}
var addrHTTP, addrSOCKS, certFile, keyFile string
if conf.ServerConfPath != "" {
var sconf serverConfig
Expand Down
22 changes: 22 additions & 0 deletions tproxy_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
package gohpts

import (
"bufio"
"context"
"errors"
"fmt"
"net"
"net/netip"
"os"
"strings"
"sync"
"syscall"
Expand Down Expand Up @@ -257,3 +259,23 @@ func getBaseDialer(timeout time.Duration, mark uint) *net.Dialer {
}
return dialer
}

func getDefaultInterface() (*net.Interface, error) {
f, err := os.Open("/proc/net/route")
if err != nil {
return nil, err
}
defer f.Close()

defaultInterface := ""
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
fields := strings.Fields(line)
if len(fields) >= 2 && fields[1] == "00000000" {
defaultInterface = fields[0]
break
}
}
return net.InterfaceByName(defaultInterface)
}
5 changes: 5 additions & 0 deletions tproxy_nonlinux.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package gohpts

import (
"fmt"
"net"
"sync"
"syscall"
Expand Down Expand Up @@ -46,3 +47,7 @@ func getBaseDialer(timeout time.Duration, mark uint) *net.Dialer {
_ = mark
return &net.Dialer{Timeout: timeout}
}

func getDefaultInterface() (*net.Interface, error) {
return nil, fmt.Errorf("not implemented")
}
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package gohpts

const Version string = "gohpts v1.8.3"
const Version string = "gohpts v1.8.4"