Files
honeydany/app/service_handlers.go
T
2025-09-28 06:48:03 +01:00

620 lines
16 KiB
Go

package app
import (
"bufio"
"encoding/base64"
"fmt"
"net"
"regexp"
"strconv"
"strings"
"time"
)
// FTP Handler - implements basic FTP protocol with authentication logging
func (a *App) ftpHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("ftp_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "ftp", "connection_start", nil)
// Send FTP welcome banner
_, _ = conn.Write([]byte("220 Welcome to FTP Server\r\n"))
conn.SetDeadline(time.Now().Add(5 * time.Minute))
scanner := bufio.NewScanner(conn)
var username, password string
authenticated := false
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
parts := strings.SplitN(line, " ", 2)
if len(parts) < 1 {
continue
}
cmd := strings.ToUpper(parts[0])
arg := ""
if len(parts) > 1 {
arg = parts[1]
}
switch cmd {
case "USER":
username = arg
a.logServiceEvent(sessionID, remote, "ftp", "username_attempt", map[string]string{"username": username})
_, _ = conn.Write([]byte("331 Password required for " + username + "\r\n"))
case "PASS":
password = arg
a.logServiceEvent(sessionID, remote, "ftp", "password_attempt", map[string]string{
"username": username,
"password": password,
})
_, _ = conn.Write([]byte("530 Login incorrect\r\n"))
case "QUIT":
_, _ = conn.Write([]byte("221 Goodbye\r\n"))
return
default:
if !authenticated {
_, _ = conn.Write([]byte("530 Please login with USER and PASS\r\n"))
} else {
_, _ = conn.Write([]byte("502 Command not implemented\r\n"))
}
}
}
}
// SMTP Handler - implements basic SMTP protocol
func (a *App) smtpHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("smtp_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "smtp", "connection_start", nil)
// Send SMTP welcome banner
_, _ = conn.Write([]byte("220 mail.example.com ESMTP Postfix\r\n"))
conn.SetDeadline(time.Now().Add(5 * time.Minute))
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
parts := strings.SplitN(line, " ", 2)
if len(parts) < 1 {
continue
}
cmd := strings.ToUpper(parts[0])
arg := ""
if len(parts) > 1 {
arg = parts[1]
}
switch cmd {
case "HELO", "EHLO":
a.logServiceEvent(sessionID, remote, "smtp", "helo", map[string]string{"hostname": arg})
if cmd == "EHLO" {
_, _ = conn.Write([]byte("250-mail.example.com\r\n250-AUTH PLAIN LOGIN\r\n250 OK\r\n"))
} else {
_, _ = conn.Write([]byte("250 mail.example.com\r\n"))
}
case "AUTH":
a.handleSMTPAuth(conn, sessionID, remote, arg)
case "MAIL":
_, _ = conn.Write([]byte("250 OK\r\n"))
case "RCPT":
_, _ = conn.Write([]byte("250 OK\r\n"))
case "DATA":
_, _ = conn.Write([]byte("354 End data with <CR><LF>.<CR><LF>\r\n"))
case "QUIT":
_, _ = conn.Write([]byte("221 Bye\r\n"))
return
default:
_, _ = conn.Write([]byte("502 Command not implemented\r\n"))
}
}
}
func (a *App) handleSMTPAuth(conn net.Conn, sessionID, remote, authLine string) {
parts := strings.Fields(authLine)
if len(parts) < 1 {
_, _ = conn.Write([]byte("501 Syntax error\r\n"))
return
}
method := strings.ToUpper(parts[0])
switch method {
case "PLAIN":
if len(parts) > 1 {
// Decode base64 auth
decoded, err := base64.StdEncoding.DecodeString(parts[1])
if err == nil {
authParts := strings.Split(string(decoded), "\x00")
if len(authParts) >= 3 {
username := authParts[1]
password := authParts[2]
a.logServiceEvent(sessionID, remote, "smtp", "auth_attempt", map[string]string{
"method": "PLAIN",
"username": username,
"password": password,
})
}
}
} else {
_, _ = conn.Write([]byte("334 \r\n"))
return
}
_, _ = conn.Write([]byte("535 Authentication failed\r\n"))
case "LOGIN":
_, _ = conn.Write([]byte("334 VXNlcm5hbWU6\r\n")) // "Username:" in base64
default:
_, _ = conn.Write([]byte("504 Authentication method not supported\r\n"))
}
}
// Telnet Handler
func (a *App) telnetHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("telnet_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "telnet", "connection_start", nil)
// Send telnet login prompt
_, _ = conn.Write([]byte("\r\nUbuntu 20.04.3 LTS\r\n\r\nlogin: "))
conn.SetDeadline(time.Now().Add(2 * time.Minute))
scanner := bufio.NewScanner(conn)
var username string
expectingPassword := false
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if !expectingPassword {
username = line
a.logServiceEvent(sessionID, remote, "telnet", "username_attempt", map[string]string{"username": username})
_, _ = conn.Write([]byte("Password: "))
expectingPassword = true
} else {
password := line
a.logServiceEvent(sessionID, remote, "telnet", "password_attempt", map[string]string{
"username": username,
"password": password,
})
_, _ = conn.Write([]byte("\r\nLogin incorrect\r\nlogin: "))
expectingPassword = false
}
}
}
// MySQL Handler
func (a *App) mysqlHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("mysql_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "mysql", "connection_start", nil)
// Send MySQL handshake packet
handshake := []byte{
0x4a, 0x00, 0x00, 0x00, // packet length + sequence
0x0a, // protocol version
}
handshake = append(handshake, []byte("5.7.34-0ubuntu0.18.04.1\x00")...) // server version
handshake = append(handshake, []byte{
0x01, 0x00, 0x00, 0x00, // connection id
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, // auth plugin data part 1
0x00, // filler
0xff, 0xf7, // capability flags lower 2 bytes
0x08, // character set
0x02, 0x00, // status flags
0x0f, 0x80, // capability flags upper 2 bytes
0x15, // auth plugin data length
}...)
_, _ = conn.Write(handshake)
conn.SetDeadline(time.Now().Add(30 * time.Second))
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err == nil && n > 4 {
// Parse login packet (simplified)
payload := buf[4:n] // skip packet header
if len(payload) > 32 {
// Extract username (simplified parsing)
usernameStart := 32
usernameEnd := usernameStart
for usernameEnd < len(payload) && payload[usernameEnd] != 0 {
usernameEnd++
}
if usernameEnd < len(payload) {
username := string(payload[usernameStart:usernameEnd])
a.logServiceEvent(sessionID, remote, "mysql", "auth_attempt", map[string]string{
"username": username,
})
}
}
}
// Send error packet
errorPacket := []byte{
0x24, 0x00, 0x00, 0x01, // packet header
0xff, // error packet marker
0x10, 0x04, // error code
0x23, 0x48, 0x59, 0x30, 0x30, 0x30, // SQL state
}
errorPacket = append(errorPacket, []byte("Access denied for user")...)
_, _ = conn.Write(errorPacket)
}
// PostgreSQL Handler
func (a *App) postgresqlHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("postgresql_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "postgresql", "connection_start", nil)
conn.SetDeadline(time.Now().Add(30 * time.Second))
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
return
}
if n >= 8 {
// Parse startup message
length := int(buf[0])<<24 | int(buf[1])<<16 | int(buf[2])<<8 | int(buf[3])
if length > 8 && n >= length {
params := string(buf[8:length])
// Extract username from parameters
if strings.Contains(params, "user") {
re := regexp.MustCompile(`user\x00([^\x00]+)`)
matches := re.FindStringSubmatch(params)
if len(matches) > 1 {
username := matches[1]
a.logServiceEvent(sessionID, remote, "postgresql", "auth_attempt", map[string]string{
"username": username,
})
}
}
}
}
// Send authentication request
authRequest := []byte{
0x52, // Authentication message type
0x00, 0x00, 0x00, 0x08, // message length
0x00, 0x00, 0x00, 0x03, // auth type (cleartext password)
}
_, _ = conn.Write(authRequest)
// Read password response
n, err = conn.Read(buf)
if err == nil && n > 5 && buf[0] == 0x70 { // password message
length := int(buf[1])<<24 | int(buf[2])<<16 | int(buf[3])<<8 | int(buf[4])
if length > 5 && n >= length {
password := string(buf[5 : length-1]) // exclude null terminator
a.logServiceEvent(sessionID, remote, "postgresql", "password_attempt", map[string]string{
"password": password,
})
}
}
// Send error response
errorMsg := []byte{
0x45, // Error message type
0x00, 0x00, 0x00, 0x26, // message length
}
errorMsg = append(errorMsg, []byte("SFATAL\x00C28P01\x00Mpassword authentication failed\x00\x00")...)
_, _ = conn.Write(errorMsg)
}
// Redis Handler
func (a *App) redisHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("redis_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "redis", "connection_start", nil)
conn.SetDeadline(time.Now().Add(2 * time.Minute))
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
// Parse Redis protocol
if strings.HasPrefix(line, "*") {
// Multi-bulk request
continue
} else if strings.HasPrefix(line, "$") {
// Bulk string length
continue
} else {
// Command
cmd := strings.ToUpper(line)
switch cmd {
case "AUTH":
// Next line should be password
if scanner.Scan() {
password := strings.TrimSpace(scanner.Text())
a.logServiceEvent(sessionID, remote, "redis", "auth_attempt", map[string]string{
"password": password,
})
}
_, _ = conn.Write([]byte("-ERR invalid password\r\n"))
case "PING":
_, _ = conn.Write([]byte("+PONG\r\n"))
case "INFO":
_, _ = conn.Write([]byte("+redis_version:6.2.6\r\n"))
case "QUIT":
_, _ = conn.Write([]byte("+OK\r\n"))
return
default:
_, _ = conn.Write([]byte("-ERR unknown command\r\n"))
}
}
}
}
// MongoDB Handler
func (a *App) mongodbHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("mongodb_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "mongodb", "connection_start", nil)
conn.SetDeadline(time.Now().Add(30 * time.Second))
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
return
}
if n >= 16 {
// Parse MongoDB wire protocol message
// This is a simplified implementation
a.logServiceEvent(sessionID, remote, "mongodb", "protocol_attempt", map[string]string{
"bytes_received": strconv.Itoa(n),
})
}
// Send connection error
_, _ = conn.Write([]byte("connection refused"))
}
// POP3 Handler
func (a *App) pop3Handler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("pop3_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "pop3", "connection_start", nil)
_, _ = conn.Write([]byte("+OK POP3 server ready\r\n"))
conn.SetDeadline(time.Now().Add(2 * time.Minute))
scanner := bufio.NewScanner(conn)
var username string
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
parts := strings.SplitN(line, " ", 2)
if len(parts) < 1 {
continue
}
cmd := strings.ToUpper(parts[0])
arg := ""
if len(parts) > 1 {
arg = parts[1]
}
switch cmd {
case "USER":
username = arg
a.logServiceEvent(sessionID, remote, "pop3", "username_attempt", map[string]string{"username": username})
_, _ = conn.Write([]byte("+OK\r\n"))
case "PASS":
password := arg
a.logServiceEvent(sessionID, remote, "pop3", "password_attempt", map[string]string{
"username": username,
"password": password,
})
_, _ = conn.Write([]byte("-ERR Authentication failed\r\n"))
case "QUIT":
_, _ = conn.Write([]byte("+OK Bye\r\n"))
return
default:
_, _ = conn.Write([]byte("-ERR Unknown command\r\n"))
}
}
}
// IMAP Handler
func (a *App) imapHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("imap_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "imap", "connection_start", nil)
_, _ = conn.Write([]byte("* OK IMAP4rev1 Service Ready\r\n"))
conn.SetDeadline(time.Now().Add(2 * time.Minute))
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
parts := strings.Fields(line)
if len(parts) < 2 {
continue
}
tag := parts[0]
cmd := strings.ToUpper(parts[1])
switch cmd {
case "LOGIN":
if len(parts) >= 4 {
username := strings.Trim(parts[2], "\"")
password := strings.Trim(parts[3], "\"")
a.logServiceEvent(sessionID, remote, "imap", "login_attempt", map[string]string{
"username": username,
"password": password,
})
}
_, _ = conn.Write([]byte(tag + " NO LOGIN failed\r\n"))
case "CAPABILITY":
_, _ = conn.Write([]byte("* CAPABILITY IMAP4rev1 AUTH=PLAIN\r\n"))
_, _ = conn.Write([]byte(tag + " OK CAPABILITY completed\r\n"))
case "LOGOUT":
_, _ = conn.Write([]byte("* BYE IMAP4rev1 Server logging out\r\n"))
_, _ = conn.Write([]byte(tag + " OK LOGOUT completed\r\n"))
return
default:
_, _ = conn.Write([]byte(tag + " BAD Command not recognized\r\n"))
}
}
}
// RDP Handler (simplified)
func (a *App) rdpHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("rdp_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "rdp", "connection_start", nil)
conn.SetDeadline(time.Now().Add(30 * time.Second))
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
return
}
if n > 0 {
a.logServiceEvent(sessionID, remote, "rdp", "protocol_attempt", map[string]string{
"bytes_received": strconv.Itoa(n),
})
}
// Send RDP connection failure
_, _ = conn.Write([]byte{0x03, 0x00, 0x00, 0x0b, 0x02, 0xf0, 0x80, 0x04, 0x01, 0x00, 0x01})
}
// DNS Handler (UDP)
func (a *App) dnsHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("dns_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "dns", "query_attempt", nil)
conn.SetDeadline(time.Now().Add(10 * time.Second))
buf := make([]byte, 512)
n, err := conn.Read(buf)
if err != nil {
return
}
if n > 12 {
// Parse DNS query (simplified)
a.logServiceEvent(sessionID, remote, "dns", "query_received", map[string]string{
"query_size": strconv.Itoa(n),
})
}
}
// SNMP Handler (UDP)
func (a *App) snmpHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("snmp_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "snmp", "request_attempt", nil)
conn.SetDeadline(time.Now().Add(10 * time.Second))
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
return
}
if n > 0 {
a.logServiceEvent(sessionID, remote, "snmp", "request_received", map[string]string{
"request_size": strconv.Itoa(n),
})
}
}
// LDAP Handler
func (a *App) ldapHandler(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("ldap_%x", time.Now().UnixNano())
a.logServiceEvent(sessionID, remote, "ldap", "connection_start", nil)
conn.SetDeadline(time.Now().Add(30 * time.Second))
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
return
}
if n > 0 {
a.logServiceEvent(sessionID, remote, "ldap", "bind_attempt", map[string]string{
"request_size": strconv.Itoa(n),
})
}
// Send LDAP bind failure
_, _ = conn.Write([]byte{0x30, 0x0c, 0x02, 0x01, 0x01, 0x61, 0x07, 0x0a, 0x01, 0x31, 0x04, 0x00, 0x04, 0x00})
}
// Helper function to log service events
func (a *App) logServiceEvent(sessionID, remote, service, eventType string, details map[string]string) {
if details == nil {
details = make(map[string]string)
}
details["session_id"] = sessionID
details["event_type"] = eventType
a.logEvent(Record{
Timestamp: time.Now().UTC(),
RemoteAddr: remoteIP(remote),
RemotePort: remotePort(remote),
Service: service,
Details: details,
RawPayload: fmt.Sprintf("%s: %s", eventType, sessionID),
})
}