Skip to content

Commit 7f6b35b

Browse files
Merge pull request #11 from shadowy-pycoder/arp_basic
added setup to handle ARP spoofed traffic
2 parents 8b6cbe2 + 7a8d380 commit 7f6b35b

File tree

6 files changed

+115
-4
lines changed

6 files changed

+115
-4
lines changed

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- [Transparent proxy](#transparent-proxy)
2121
- [redirect (via NAT and SO_ORIGINAL_DST)](#redirect-via-nat-and-so_original_dst)
2222
- [tproxy (via MANGLE and IP_TRANSPARENT)](#tproxy-via-mangle-and-ip_transparent)
23+
- [ARP spoofing](#arp-spoofing)
2324
- [Traffic sniffing](#traffic-sniffing)
2425
- [JSON format](#json-format)
2526
- [Colored format](#colored-format)
@@ -97,7 +98,7 @@ You can download the binary for your platform from [Releases](https://github.com
9798
Example:
9899

99100
```shell
100-
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
101+
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
101102
```
102103

103104
Alternatively, you can install it using `go install` command (requires Go [1.24](https://go.dev/doc/install) or later):
@@ -164,6 +165,7 @@ Options:
164165
-T Address of transparent proxy server (no HTTP)
165166
-M Transparent proxy mode: (redirect, tproxy)
166167
-auto Automatically setup iptables for transparent proxy (requires elevated privileges)
168+
-arp Automatically setup iptables to proxy ARP spoofed traffic (use tools like bettercap to perform actual attack)
167169
-mark Set mark for each packet sent through transparent proxy (Default: redirect 0, tproxy 100)
168170
```
169171
@@ -479,6 +481,25 @@ else
479481
fi
480482
```
481483
484+
### ARP spoofing
485+
486+
`GoHPTS` can be used with tools like [Bettercap](https://github.com/bettercap/bettercap) to proxy ARP spoofed traffic.
487+
488+
Run the proxy with `-arp` flag
489+
490+
```shell
491+
ssh remote -D 1080 -Nf
492+
sudo env PATH=$PATH gohpts -d -T 8888 -M tproxy -sniff -body -auto -mark 100 -arp
493+
```
494+
495+
Run `bettercap`
496+
497+
```shell
498+
sudo bettercap -eval "set net.probe on;set net.recon on;arp.spoof on"
499+
```
500+
501+
Check proxy logs for traffic from other devices from your LAN
502+
482503
## Traffic sniffing
483504
484505
[[Back]](#table-of-contents)

cmd/gohpts/cli.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const usageTproxy string = `
6161
-T Address of transparent proxy server (no HTTP)
6262
-M Transparent proxy mode: (redirect, tproxy)
6363
-auto Automatically setup iptables for transparent proxy (requires elevated privileges)
64+
-arp Automatically setup iptables to proxy ARP spoofed traffic (use tools like bettercap to perform actual attack)
6465
-mark Set mark for each packet sent through transparent proxy (Default: redirect 0, tproxy 100)
6566
`
6667

@@ -113,6 +114,12 @@ func root(args []string) error {
113114
0,
114115
"Set mark for each packet sent through transparent proxy (Default: redirect 0, tproxy 100)",
115116
)
117+
flags.BoolVar(
118+
&conf.ARP,
119+
"arp",
120+
false,
121+
"Automatically setup iptables to proxy ARP spoofed traffic (use tools like bettercap to perform actual attack)",
122+
)
116123
}
117124
flags.StringVar(&conf.LogFilePath, "logfile", "", "Log file path (Default: stdout)")
118125
flags.BoolVar(&conf.Debug, "d", false, "Show logs in DEBUG mode")
@@ -172,6 +179,11 @@ func root(args []string) error {
172179
return fmt.Errorf("-mark requires -t or -T flag")
173180
}
174181
}
182+
if seen["arp"] {
183+
if !seen["auto"] {
184+
return fmt.Errorf("-arp requires -auto flag")
185+
}
186+
}
175187
if seen["f"] {
176188
for _, da := range []string{"s", "u", "U", "c", "k", "l"} {
177189
if seen[da] {

gohpts.go

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ type Config struct {
9797
TProxyMode string
9898
Auto bool
9999
Mark uint
100+
ARP bool
100101
LogFilePath string
101102
Debug bool
102103
JSON bool
@@ -120,6 +121,7 @@ type proxyapp struct {
120121
tproxyMode string
121122
auto bool
122123
mark uint
124+
arp bool
123125
user string
124126
pass string
125127
proxychain chain
@@ -1361,10 +1363,53 @@ func (p *proxyapp) applyRedirectRules() string {
13611363
cmdForward.Stdout = os.Stdout
13621364
cmdForward.Stderr = os.Stderr
13631365
_ = cmdForward.Run()
1366+
if p.arp {
1367+
cmdClear := exec.Command("bash", "-c", `
1368+
set -ex
1369+
iptables -t filter -F GOHPTS 2>/dev/null || true
1370+
iptables -t filter -D FORWARD -j GOHPTS 2>/dev/null || true
1371+
iptables -t filter -X GOHPTS 2>/dev/null || true
1372+
`)
1373+
cmdClear.Stdout = os.Stdout
1374+
cmdClear.Stderr = os.Stderr
1375+
if err := cmdClear.Run(); err != nil {
1376+
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
1377+
}
1378+
iface, err := getDefaultInterface()
1379+
if err != nil {
1380+
p.logger.Fatal().Err(err).Msg("failed getting default network interface")
1381+
}
1382+
cmdForward := exec.Command("bash", "-c", fmt.Sprintf(`
1383+
set -ex
1384+
iptables -t filter -N GOHPTS 2>/dev/null
1385+
iptables -t filter -F GOHPTS
1386+
iptables -t filter -A FORWARD -j GOHPTS
1387+
iptables -t filter -A GOHPTS -i %s -j ACCEPT
1388+
iptables -t filter -A GOHPTS -o %s -j ACCEPT
1389+
`, iface.Name, iface.Name))
1390+
cmdForward.Stdout = os.Stdout
1391+
cmdForward.Stderr = os.Stderr
1392+
if err := cmdForward.Run(); err != nil {
1393+
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
1394+
}
1395+
}
13641396
return string(output)
13651397
}
13661398

13671399
func (p *proxyapp) clearRedirectRules(output string) error {
1400+
if p.arp {
1401+
cmdClear := exec.Command("bash", "-c", `
1402+
set -ex
1403+
iptables -t filter -F GOHPTS 2>/dev/null || true
1404+
iptables -t filter -D FORWARD -j GOHPTS 2>/dev/null || true
1405+
iptables -t filter -X GOHPTS 2>/dev/null || true
1406+
`)
1407+
cmdClear.Stdout = os.Stdout
1408+
cmdClear.Stderr = os.Stderr
1409+
if err := cmdClear.Run(); err != nil {
1410+
p.logger.Fatal().Err(err).Msg("Failed while configuring iptables. Are you root?")
1411+
}
1412+
}
13681413
var cmd *exec.Cmd
13691414
switch p.tproxyMode {
13701415
case "redirect":
@@ -1701,7 +1746,7 @@ func New(conf *Config) *proxyapp {
17011746
p.tproxyMode = conf.TProxyMode
17021747
tproxyonly := conf.TProxyOnly != ""
17031748
if tproxyonly {
1704-
if p.tproxyMode == "tproxy" {
1749+
if p.tproxyMode != "" {
17051750
p.tproxyAddr, err = getFullAddress(conf.TProxyOnly, true)
17061751
if err != nil {
17071752
p.logger.Fatal().Err(err).Msg("")
@@ -1713,7 +1758,7 @@ func New(conf *Config) *proxyapp {
17131758
}
17141759
}
17151760
} else {
1716-
if p.tproxyMode == "tproxy" {
1761+
if p.tproxyMode != "" {
17171762
p.tproxyAddr, err = getFullAddress(conf.TProxy, true)
17181763
if err != nil {
17191764
p.logger.Fatal().Err(err).Msg("")
@@ -1739,6 +1784,12 @@ func New(conf *Config) *proxyapp {
17391784
if p.mark == 0 && p.tproxyMode == "tproxy" {
17401785
p.mark = 100
17411786
}
1787+
p.arp = conf.ARP
1788+
if p.arp && runtime.GOOS != "linux" {
1789+
p.logger.Fatal().Msg("ARP setup is available only for linux system")
1790+
} else if p.arp && !p.auto {
1791+
p.logger.Fatal().Msg("ARP setup requires auto configuration")
1792+
}
17421793
var addrHTTP, addrSOCKS, certFile, keyFile string
17431794
if conf.ServerConfPath != "" {
17441795
var sconf serverConfig

tproxy_linux.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
package gohpts
55

66
import (
7+
"bufio"
78
"context"
89
"errors"
910
"fmt"
1011
"net"
1112
"net/netip"
13+
"os"
1214
"strings"
1315
"sync"
1416
"syscall"
@@ -257,3 +259,23 @@ func getBaseDialer(timeout time.Duration, mark uint) *net.Dialer {
257259
}
258260
return dialer
259261
}
262+
263+
func getDefaultInterface() (*net.Interface, error) {
264+
f, err := os.Open("/proc/net/route")
265+
if err != nil {
266+
return nil, err
267+
}
268+
defer f.Close()
269+
270+
defaultInterface := ""
271+
scanner := bufio.NewScanner(f)
272+
for scanner.Scan() {
273+
line := scanner.Text()
274+
fields := strings.Fields(line)
275+
if len(fields) >= 2 && fields[1] == "00000000" {
276+
defaultInterface = fields[0]
277+
break
278+
}
279+
}
280+
return net.InterfaceByName(defaultInterface)
281+
}

tproxy_nonlinux.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package gohpts
55

66
import (
7+
"fmt"
78
"net"
89
"sync"
910
"syscall"
@@ -46,3 +47,7 @@ func getBaseDialer(timeout time.Duration, mark uint) *net.Dialer {
4647
_ = mark
4748
return &net.Dialer{Timeout: timeout}
4849
}
50+
51+
func getDefaultInterface() (*net.Interface, error) {
52+
return nil, fmt.Errorf("not implemented")
53+
}

version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
package gohpts
22

3-
const Version string = "gohpts v1.8.3"
3+
const Version string = "gohpts v1.8.4"

0 commit comments

Comments
 (0)