-
-
Notifications
You must be signed in to change notification settings - Fork 66
/
Copy pathftp.go
114 lines (102 loc) · 2.67 KB
/
ftp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package tcp
import (
"bufio"
"context"
"fmt"
"io"
"log/slog"
"net"
"strconv"
"strings"
"github.com/mushorg/glutton/connection"
"github.com/mushorg/glutton/producer"
"github.com/mushorg/glutton/protocols/helpers"
"github.com/mushorg/glutton/protocols/interfaces"
)
type parsedFTP struct {
Direction string `json:"direction,omitempty"`
Payload []byte `json:"payload,omitempty"`
}
type ftpServer struct {
events []parsedFTP
conn net.Conn
}
func (s *ftpServer) read(_ interfaces.Logger, _ interfaces.Honeypot) (string, error) {
msg, err := bufio.NewReader(s.conn).ReadString('\n')
if err != nil {
return msg, err
}
s.events = append(s.events, parsedFTP{
Direction: "read",
Payload: []byte(msg),
})
return msg, nil
}
func (s *ftpServer) write(msg string) error {
_, err := s.conn.Write([]byte(msg))
if err != nil {
return err
}
s.events = append(s.events, parsedFTP{
Direction: "write",
Payload: []byte(msg),
})
return nil
}
// HandleFTP takes a net.Conn and does basic FTP communication
func HandleFTP(ctx context.Context, conn net.Conn, md connection.Metadata, logger interfaces.Logger, h interfaces.Honeypot) error {
server := ftpServer{
conn: conn,
}
defer func() {
if err := h.ProduceTCP("ftp", conn, md, helpers.FirstOrEmpty[parsedFTP](server.events).Payload, server.events); err != nil {
logger.Error("Failed to produce events", slog.String("protocol", "ftp"), producer.ErrAttr(err))
}
if err := conn.Close(); err != nil {
logger.Error("Failed to close FTP connection", slog.String("protocol", "ftp"), producer.ErrAttr(err))
}
}()
host, port, err := net.SplitHostPort(conn.RemoteAddr().String())
if err != nil {
return err
}
if _, err := conn.Write([]byte("220 Welcome!\r\n")); err != nil {
return err
}
for {
if err := h.UpdateConnectionTimeout(ctx, conn); err != nil {
logger.Debug("Failed to set connection timeout", slog.String("protocol", "ftp"), producer.ErrAttr(err))
return nil
}
msg, err := server.read(logger, h)
if err != nil || err != io.EOF {
logger.Debug("Failed to read data", slog.String("protocol", "ftp"), producer.ErrAttr(err))
break
}
if len(msg) < 4 {
continue
}
cmd := strings.ToUpper(msg[:4])
logger.Info(
"ftp payload received",
slog.String("dest_port", strconv.Itoa(int(md.TargetPort))),
slog.String("src_ip", host),
slog.String("src_port", port),
slog.String("message", fmt.Sprintf("%q", msg)),
slog.String("handler", "ftp"),
)
var resp string
switch cmd {
case "USER":
resp = "331 OK.\r\n"
case "PASS":
resp = "230 OK.\r\n"
default:
resp = "200 OK.\r\n"
}
if err := server.write(resp); err != nil {
return err
}
}
return nil
}