Cybersecurity-Projects/PROJECTS/advanced/honeypot-network/internal/sshd/shell.go

148 lines
2.9 KiB
Go

/*
©AngelaMos | 2026
shell.go
Interactive shell emulation for the SSH honeypot
Provides a realistic bash-like terminal using golang.org/x/term
with a root prompt, Ubuntu MOTD banner on connect, and full
read-eval-print loop. All I/O is captured by the asciicast recorder
and every command is published to the event bus for analysis.
*/
package sshd
import (
"encoding/json"
"errors"
"fmt"
"io"
"strings"
"time"
"github.com/google/uuid"
"golang.org/x/crypto/ssh"
"golang.org/x/term"
"github.com/CarterPerez-dev/hive/internal/config"
"github.com/CarterPerez-dev/hive/internal/event"
"github.com/CarterPerez-dev/hive/internal/session"
"github.com/CarterPerez-dev/hive/pkg/types"
)
func RunShell(
ch ssh.Channel,
sessionID string,
sensorID string,
sourceIP string,
username string,
hostname string,
bus *event.Bus,
recorder *session.Recorder,
cols int,
rows int,
) {
now := time.Now().UTC()
banner := fmt.Sprintf(
config.SSHMOTDTemplate,
now.Format("Mon Jan 2 15:04:05 UTC 2006"),
now.Add(-24*time.Hour).Format(
"Mon Jan 2 15:04:05 2006",
),
)
writeAndRecord(ch, recorder, []byte(banner))
fs := NewFakeFS(hostname)
cmdCtx := &CommandContext{
FS: fs,
Hostname: hostname,
Username: username,
CWD: "/root",
}
prompt := fmt.Sprintf(
"%s@%s:%s# ", username, hostname, cmdCtx.CWD,
)
terminal := term.NewTerminal(ch, prompt)
_ = terminal.SetSize(cols, rows)
for {
line, err := terminal.ReadLine()
if err != nil {
if !errors.Is(err, io.EOF) {
break
}
break
}
line = strings.TrimSpace(line)
if line == "" {
continue
}
recorder.WriteInput([]byte(line + "\n"))
publishCommand(
bus, sessionID, sensorID,
sourceIP, line, hostname,
)
if line == "exit" || line == "logout" || line == "quit" {
writeAndRecord(
ch, recorder, []byte("logout\r\n"),
)
return
}
output := DispatchCommand(line, cmdCtx)
if output != "" {
crlf := strings.ReplaceAll(output, "\n", "\r\n")
writeAndRecord(ch, recorder, []byte(crlf))
}
prompt = fmt.Sprintf(
"%s@%s:%s# ",
username, hostname, cmdCtx.CWD,
)
terminal.SetPrompt(prompt)
}
}
func writeAndRecord(
w io.Writer,
recorder *session.Recorder,
data []byte,
) {
_, _ = w.Write(data)
recorder.WriteOutput(data)
}
func publishCommand(
bus *event.Bus,
sessionID string,
sensorID string,
sourceIP string,
command string,
hostname string,
) {
serviceData, _ := json.Marshal(map[string]string{
"command": command,
"hostname": hostname,
})
bus.Publish(config.TopicCommand, &types.Event{
ID: uuid.Must(uuid.NewV7()).String(),
SessionID: sessionID,
SensorID: sensorID,
Timestamp: time.Now().UTC(),
ServiceType: types.ServiceSSH,
EventType: types.EventCommand,
SourceIP: sourceIP,
Protocol: types.ProtocolTCP,
SchemaVersion: config.SchemaVersion,
ServiceData: serviceData,
})
}