Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ _testmain.go
# Artifacts
glutton.log*
payloads/
samples/

# PoCs
poc/
Expand Down
2 changes: 1 addition & 1 deletion app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func main() {
fmt.Printf("%s %s\n\n", VERSION, BUILDDATE)

pflag.StringP("interface", "i", "eth0", "Bind to this interface")
pflag.IntP("ssh", "s", 0, "Override SSH port")
pflag.IntP("ssh", "s", 22, "Override SSH port")
pflag.StringP("logpath", "l", "/dev/null", "Log file path")
pflag.StringP("confpath", "c", "config/", "Configuration file path")
pflag.BoolP("debug", "d", false, "Enable debug mode")
Expand Down
18 changes: 17 additions & 1 deletion connection/connection.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package connection

import (
"context"
"errors"
"fmt"
"net"
Expand Down Expand Up @@ -55,10 +56,25 @@ type ConnTable struct {
mtx sync.RWMutex
}

func New() *ConnTable {
func New(ctx context.Context) *ConnTable {
ct := &ConnTable{
table: make(map[CKey]Metadata, 1024),
}
// every 5 minutes using a ticker, flush the table

go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
ct.FlushOlderThan(5 * time.Minute)
}
}
}()

return ct
}

Expand Down
9 changes: 5 additions & 4 deletions connection/connection_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package connection

import (
"context"
"net"
"testing"
"time"
Expand Down Expand Up @@ -32,12 +33,12 @@ func TestNewConnKeyFromNetConn(t *testing.T) {
}

func TestNewConnTable(t *testing.T) {
table := New()
table := New(context.Background())
require.NotNil(t, table)
}

func TestRegister(t *testing.T) {
table := New()
table := New(context.Background())
targetPort := 4321
m1, err := table.Register("127.0.0.1", "1234", uint16(targetPort), &rules.Rule{})
require.NoError(t, err)
Expand All @@ -57,7 +58,7 @@ func TestRegisterConn(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, conn)
defer conn.Close()
table := New()
table := New(context.Background())
md, err := table.RegisterConn(conn, &rules.Rule{Target: "tcp"})
require.NoError(t, err)
require.NotNil(t, md)
Expand All @@ -67,7 +68,7 @@ func TestRegisterConn(t *testing.T) {
}

func TestFlushOlderThan(t *testing.T) {
table := New()
table := New(context.Background())
targetPort := 4321
md, err := table.Register("127.0.0.1", "1234", uint16(targetPort), &rules.Rule{})
require.NoError(t, err)
Expand Down
26 changes: 10 additions & 16 deletions glutton.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,43 +43,37 @@ type Glutton struct {
//go:embed config/rules.yaml
var defaultRules []byte

//go:embed config/config.yaml
var defaultConfig []byte

func (g *Glutton) initConfig() error {
viper.SetConfigName("config")
viper.AddConfigPath(viper.GetString("confpath"))
if _, err := os.Stat(viper.GetString("confpath")); !os.IsNotExist(err) {
if err := viper.ReadInConfig(); err != nil {
return err
}
g.Logger.Info("Using configuration file", slog.String("path", viper.GetString("confpath")), slog.String("reporter", "glutton"))
return viper.ReadInConfig()
}
// If no config is found, use the defaults
viper.SetDefault("ports.tcp", 5000)
viper.SetDefault("ports.udp", 5001)
viper.SetDefault("ports.ssh", 22)
viper.SetDefault("max_tcp_payload", 4096)
viper.SetDefault("conn_timeout", 45)
viper.SetDefault("rules_path", "rules/rules.yaml")
viper.SetDefault("interface", "eth0") // Default interface name

g.Logger.Debug("configuration set successfully", slog.String("reporter", "glutton"))
return nil

g.Logger.Info("No configuration file found, using default configuration", slog.String("reporter", "glutton"))
return viper.ReadConfig(bytes.NewBuffer(defaultConfig))
}

// New creates a new Glutton instance
func New(ctx context.Context) (*Glutton, error) {
g := &Glutton{
tcpProtocolHandlers: make(map[string]protocols.TCPHandlerFunc),
udpProtocolHandlers: make(map[string]protocols.UDPHandlerFunc),
connTable: connection.New(),
}
g.ctx, g.cancel = context.WithCancel(ctx)

g.connTable = connection.New(ctx)
if err := g.makeID(); err != nil {
return nil, err
}
g.Logger = producer.NewLogger(g.id.String())

// Loading the configuration
g.Logger.Info("Loading configurations from: config/config.yaml", slog.String("reporter", "glutton"))
g.Logger.Info("Loading configurations", slog.String("reporter", "glutton"))
if err := g.initConfig(); err != nil {
return nil, err
}
Expand Down
6 changes: 3 additions & 3 deletions protocols/helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ func FirstOrEmpty[T any](s []T) T {
return t
}

func StorePayload(data []byte) (string, error) {
func Store(data []byte, folder string) (string, error) {
sum := sha256.Sum256(data)
if err := os.MkdirAll("payloads", os.ModePerm); err != nil {
if err := os.MkdirAll(folder, os.ModePerm); err != nil {
return "", err
}
sha256Hash := hex.EncodeToString(sum[:])
path := filepath.Join("payloads", sha256Hash)
path := filepath.Join(folder, sha256Hash)
if _, err := os.Stat(path); err == nil {
// file already exists
return "", nil
Expand Down
2 changes: 1 addition & 1 deletion protocols/tcp/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func HandleTCP(ctx context.Context, conn net.Conn, md connection.Metadata, logge

defer func() {
if msgLength > 0 {
payloadHash, err := helpers.StorePayload(data)
payloadHash, err := helpers.Store(data, "payloads")
if err != nil {
logger.Error("Failed to store payload", slog.String("handler", "tcp"), producer.ErrAttr(err))
}
Expand Down
47 changes: 20 additions & 27 deletions protocols/tcp/telnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@ import (
"bufio"
"context"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"io"
"log/slog"
"math/big"
"net"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"time"
Expand Down Expand Up @@ -95,46 +91,38 @@ func (s *telnetServer) read(conn net.Conn) (string, error) {
func (s *telnetServer) getSample(cmd string, logger interfaces.Logger) error {
url := cmd[strings.Index(cmd, "http"):]
url = strings.Split(url, " ")[0]
url = strings.TrimSpace(url)
logger.Debug("Fetching sample", slog.String("url", url), slog.String("handler", "telnet"))
resp, err := s.client.Get(url)
if err != nil {
return err
}
if resp.StatusCode != 200 {
return errors.New("getSample read http: error: Non 200 status code on getSample")
return errors.New("failed to fetch sample: " + resp.Status)
}
defer resp.Body.Close()
if resp.ContentLength <= 0 {
return errors.New("getSample read http: error: Empty response body")
return errors.New("content length is 0")
}
bodyBuffer, err := io.ReadAll(resp.Body)

data, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
sum := sha256.Sum256(bodyBuffer)
// Ignoring errors for if the folder already exists
if err = os.MkdirAll("samples", os.ModePerm); err != nil {
return err
}
sha256Hash := hex.EncodeToString(sum[:])
path := filepath.Join("samples", sha256Hash)
if _, err = os.Stat(path); err == nil {
logger.Debug("getSample already known", slog.String("sha", sha256Hash), slog.String("handler", "telnet"))
return nil
}
out, err := os.Create(path)
if err != nil {
return err

if len(data) == 0 {
return errors.New("empty response body")
}
defer out.Close()
_, err = out.Write(bodyBuffer)

sha256Hash, err := helpers.Store(data, "samples")
if err != nil {
return err
}

logger.Info(
"new sample fetched from telnet",
"New sample fetched",
slog.String("handler", "telnet"),
slog.String("sha256", sha256Hash),
slog.String("sample_hash", sha256Hash),
slog.String("source", url),
)
return nil
Expand Down Expand Up @@ -197,7 +185,12 @@ func HandleTelnet(ctx context.Context, conn net.Conn, md connection.Metadata, lo
}
for _, cmd := range strings.Split(msg, ";") {
if strings.Contains(strings.Trim(cmd, " "), "wget http") {
go s.getSample(strings.Trim(cmd, " "), logger)
go func() {
err := s.getSample(strings.Trim(cmd, " "), logger)
if err != nil {
logger.Error("Failed to get sample", slog.String("handler", "telnet"), producer.ErrAttr(err))
}
}()
}
if strings.TrimRight(cmd, "") == " rm /dev/.t" {
continue
Expand Down Expand Up @@ -226,7 +219,7 @@ func HandleTelnet(ctx context.Context, conn net.Conn, md connection.Metadata, lo
}
} else {
// /bin/busybox YDKBI
re := regexp.MustCompile(`\/bin\/busybox (?P<applet>[A-Z]+)`)
re := regexp.MustCompile(`\/bin\/busybox (?P<applet>[A-Za-z]+)`)
match := re.FindStringSubmatch(cmd)
if len(match) > 1 {
if err := s.write(conn, match[1]+": applet not found\r\n"); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion protocols/udp/udp.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

func HandleUDP(ctx context.Context, srcAddr, dstAddr *net.UDPAddr, data []byte, md connection.Metadata, log interfaces.Logger, h interfaces.Honeypot) error {
log.Info(fmt.Sprintf("UDP payload:\n%s", hex.Dump(data[:len(data)%1024])))
if _, err := helpers.StorePayload(data[:len(data)%1024]); err != nil {
if _, err := helpers.Store(data[:len(data)%1024], "payloads"); err != nil {
log.Error("failed to store UDP payload", producer.ErrAttr(err))
}
if err := h.ProduceUDP("udp", srcAddr, dstAddr, md, data[:len(data)%1024], nil); err != nil {
Comment thread
glaslos marked this conversation as resolved.
Outdated
Expand Down