mirror of
https://github.com/andreicscs/HoneyWire
synced 2026-06-26 12:39:53 +00:00
205 lines
4.3 KiB
Go
205 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"os/signal"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/honeywire/sdk-go"
|
|
)
|
|
|
|
var (
|
|
decoyPorts = parsePorts(getEnv("HW_DECOY_PORTS", "2222,3306"))
|
|
tarpitMode = strings.ToLower(getEnv("HW_TARPIT_MODE", "hold"))
|
|
banner = parseBanner(getEnv("HW_TARPIT_BANNER", ""))
|
|
concurrency = 1000
|
|
maxBytes = 50 * 1024
|
|
maxLines = 10
|
|
maxDuration = 3600 * time.Second
|
|
)
|
|
|
|
func main() {
|
|
hw, err := sdk.NewSensor()
|
|
if err != nil {
|
|
log.Fatalf("[!] FATAL: %v", err)
|
|
}
|
|
|
|
hw.SetTestPayload(
|
|
"tcp_connection",
|
|
"Wizard Firedrill",
|
|
"Mock Tarpit Port",
|
|
sdk.EventDetails{
|
|
{Key: "test_message", Value: "Wizard triggered a synthetic event firedrill."},
|
|
{Key: "payload", Value: []string{"SSH-2.0-Firedrill-Test\r\n"}},
|
|
{Key: "duration_sec", Value: 5.2},
|
|
{Key: "action_taken", Value: "hold"},
|
|
},
|
|
)
|
|
|
|
if hw.TestMode {
|
|
if hw.RunTestMode() {
|
|
os.Exit(0)
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
|
|
var listeners []net.Listener
|
|
var lc net.ListenConfig
|
|
for _, port := range decoyPorts {
|
|
addr := fmt.Sprintf("0.0.0.0:%d", port)
|
|
l, err := lc.Listen(context.Background(), "tcp", addr)
|
|
if err != nil {
|
|
log.Fatalf("[!] FATAL: Failed to bind to port %d: %v", port, err)
|
|
}
|
|
listeners = append(listeners, l)
|
|
}
|
|
|
|
log.Printf("[*] HoneyWire Tarpit | Mode: %s", strings.ToUpper(tarpitMode))
|
|
|
|
semaphore := make(chan struct{}, concurrency)
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
defer stop()
|
|
|
|
for i, listener := range listeners {
|
|
go startListener(ctx, hw, decoyPorts[i], listener, semaphore)
|
|
}
|
|
|
|
if err := hw.Start(); err != nil {
|
|
log.Fatalf("[!] FATAL: %v", err)
|
|
}
|
|
defer hw.Stop()
|
|
|
|
<-ctx.Done()
|
|
log.Println("[*] Tarpit shutting down.")
|
|
}
|
|
|
|
func startListener(ctx context.Context, hw *sdk.Sensor, port int, listener net.Listener, semaphore chan struct{}) {
|
|
defer listener.Close()
|
|
|
|
log.Printf("[+] Tarpit listening on port %d", port)
|
|
|
|
for {
|
|
conn, err := listener.Accept()
|
|
if err != nil {
|
|
// Break if context was cancelled
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
log.Printf("[-] Accept error on port %d: %v", port, err)
|
|
continue
|
|
}
|
|
}
|
|
|
|
semaphore <- struct{}{}
|
|
go func(c net.Conn) {
|
|
defer func() { <-semaphore }()
|
|
handleConnection(hw, c, port)
|
|
}(conn)
|
|
}
|
|
}
|
|
|
|
func handleConnection(hw *sdk.Sensor, conn net.Conn, port int) {
|
|
defer conn.Close()
|
|
start := time.Now()
|
|
|
|
remoteAddr := conn.RemoteAddr().String()
|
|
srcIP, _, err := net.SplitHostPort(remoteAddr)
|
|
if err != nil {
|
|
srcIP = remoteAddr
|
|
}
|
|
|
|
var payload []string
|
|
consumedBytes := 0
|
|
|
|
if banner != "" && tarpitMode != "close" {
|
|
conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
|
|
conn.Write([]byte(banner))
|
|
}
|
|
|
|
if tarpitMode != "close" {
|
|
buf := make([]byte, 1024)
|
|
for consumedBytes < maxBytes && time.Since(start) < maxDuration {
|
|
conn.SetReadDeadline(time.Now().Add(300 * time.Second))
|
|
n, err := conn.Read(buf)
|
|
|
|
if err != nil {
|
|
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
|
conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
|
|
conn.Write([]byte{0})
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
|
|
if n > 0 {
|
|
consumedBytes += n
|
|
|
|
if len(payload) < maxLines {
|
|
text := strings.TrimSpace(strings.ToValidUTF8(string(buf[:n]), "?"))
|
|
if text != "" {
|
|
payload = append(payload, text)
|
|
}
|
|
}
|
|
|
|
if tarpitMode == "echo" {
|
|
conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
|
|
conn.Write(buf[:n])
|
|
}
|
|
|
|
time.Sleep(500 * time.Millisecond)
|
|
}
|
|
}
|
|
}
|
|
|
|
duration := time.Since(start).Seconds()
|
|
|
|
hw.ReportEvent(
|
|
"tcp_connection",
|
|
srcIP,
|
|
fmt.Sprintf("Port %d", port),
|
|
sdk.EventDetails{
|
|
{Key: "duration_sec", Value: duration},
|
|
{Key: "payload", Value: payload},
|
|
{Key: "action_taken", Value: tarpitMode},
|
|
},
|
|
)
|
|
}
|
|
|
|
func parsePorts(raw string) []int {
|
|
var ports []int
|
|
for _, p := range strings.Split(raw, ",") {
|
|
p = strings.TrimSpace(p)
|
|
if p == "" {
|
|
continue
|
|
}
|
|
if val, err := strconv.Atoi(p); err == nil {
|
|
ports = append(ports, val)
|
|
}
|
|
}
|
|
if len(ports) == 0 {
|
|
return []int{2222, 3306}
|
|
}
|
|
return ports
|
|
}
|
|
|
|
func parseBanner(raw string) string {
|
|
raw = strings.ReplaceAll(raw, "\\r", "\r")
|
|
raw = strings.ReplaceAll(raw, "\\n", "\n")
|
|
return raw
|
|
}
|
|
|
|
func getEnv(key, fallback string) string {
|
|
if val, exists := os.LookupEnv(key); exists {
|
|
return val
|
|
}
|
|
return fallback
|
|
}
|