feat: Improve SSH LLM honeypot, preserve session after attacker logout (#179)

* Migrate from deprecated library "golang.org/x/crypto/ssh/terminal" to "golang.org/x/term"

* Feat: Inject OpenAI secret key from environment variable

* Feat: Add test for OpenAI secret key injection from environment variable

* Fix: Correct llmModel value in http-80.yaml configuration

* Feat: Add OPEN_AI_SECRET_KEY environment variable to docker-compose.yml

* Feat: Implement session management for SSHStrategy with command history
This commit is contained in:
Mario Candela
2025-03-09 13:17:04 +01:00
committed by GitHub
parent ef07ca1203
commit 933f02911b
5 changed files with 57 additions and 13 deletions

View File

@ -13,13 +13,15 @@ import (
"github.com/gliderlabs/ssh"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/term"
)
type SSHStrategy struct {
Sessions map[string][]plugins.Message
}
func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
sshStrategy.Sessions = make(map[string][]plugins.Message)
go func() {
server := &ssh.Server{
Addr: beelzebubServiceConfiguration.Address,
@ -30,7 +32,9 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
uuidSession := uuid.New()
host, port, _ := net.SplitHostPort(sess.RemoteAddr().String())
sessionKey := host + sess.User()
// Inline SSH command
if sess.RawCommand() != "" {
for _, command := range beelzebubServiceConfiguration.Commands {
matched, err := regexp.MatchString(command.Regex, sess.RawCommand())
@ -52,8 +56,14 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
llmProvider = plugins.OpenAI
}
histories := make([]plugins.Message, 0)
if sshStrategy.Sessions[sessionKey] != nil {
histories = sshStrategy.Sessions[sessionKey]
}
llmHoneypot := plugins.LLMHoneypot{
Histories: make([]plugins.Message, 0),
Histories: histories,
OpenAIKey: beelzebubServiceConfiguration.Plugin.OpenAISecretKey,
Protocol: tracer.SSH,
Host: beelzebubServiceConfiguration.Plugin.Host,
@ -86,6 +96,10 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
Command: sess.RawCommand(),
CommandOutput: commandOutput,
})
var histories []plugins.Message
histories = append(histories, plugins.Message{Role: plugins.USER.String(), Content: sess.RawCommand()})
histories = append(histories, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
sshStrategy.Sessions[sessionKey] = histories
tr.TraceEvent(tracer.Event{
Msg: "End SSH Session",
Status: tracer.End.String(),
@ -109,10 +123,14 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
Description: beelzebubServiceConfiguration.Description,
})
term := terminal.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
terminal := term.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
var histories []plugins.Message
if sshStrategy.Sessions[sessionKey] != nil {
histories = sshStrategy.Sessions[sessionKey]
}
for {
commandInput, err := term.ReadLine()
commandInput, err := terminal.ReadLine()
if err != nil {
break
}
@ -160,7 +178,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
histories = append(histories, plugins.Message{Role: plugins.USER.String(), Content: commandInput})
histories = append(histories, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
term.Write(append([]byte(commandOutput), '\n'))
terminal.Write(append([]byte(commandOutput), '\n'))
tr.TraceEvent(tracer.Event{
Msg: "New SSH Terminal Session",
@ -178,6 +196,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
}
}
}
sshStrategy.Sessions[sessionKey] = histories
tr.TraceEvent(tracer.Event{
Msg: "End SSH Session",
Status: tracer.End.String(),