Skip to content

Commit 933f029

Browse files
authored
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
1 parent ef07ca1 commit 933f029

File tree

5 files changed

+57
-13
lines changed

5 files changed

+57
-13
lines changed

configurations/services/http-80.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ commands:
2222
plugin: "LLMHoneypot"
2323
statusCode: 200
2424
plugin:
25-
llmModel: "gpt4-o"
25+
llmModel: "gpt-4o"
2626
openAISecretKey: "sk-proj-123456"

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ services:
1515
- "2112:2112" #Prometheus Open Metrics
1616
environment:
1717
RABBITMQ_URI: ${RABBITMQ_URI}
18+
OPEN_AI_SECRET_KEY: ${OPEN_AI_SECRET_KEY}
1819
volumes:
1920
- "./configurations:/configurations"

plugins/llm-integration.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/go-resty/resty/v2"
88
"github.com/mariocandela/beelzebub/v3/tracer"
99
log "github.com/sirupsen/logrus"
10+
"os"
1011
"regexp"
1112
"strings"
1213
)
@@ -95,6 +96,10 @@ func InitLLMHoneypot(config LLMHoneypot) *LLMHoneypot {
9596
// Inject the dependencies
9697
config.client = resty.New()
9798

99+
if os.Getenv("OPEN_AI_SECRET_KEY") != "" {
100+
config.OpenAIKey = os.Getenv("OPEN_AI_SECRET_KEY")
101+
}
102+
98103
return &config
99104
}
100105

plugins/llm-integration_test.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/mariocandela/beelzebub/v3/tracer"
77
"github.com/stretchr/testify/assert"
88
"net/http"
9+
"os"
910
"testing"
1011
)
1112

@@ -85,7 +86,7 @@ func TestBuildExecuteModelFailValidation(t *testing.T) {
8586
Histories: make([]Message, 0),
8687
OpenAIKey: "",
8788
Protocol: tracer.SSH,
88-
Model: "gpt4-o",
89+
Model: "gpt-4o",
8990
Provider: OpenAI,
9091
}
9192

@@ -96,6 +97,24 @@ func TestBuildExecuteModelFailValidation(t *testing.T) {
9697
assert.Equal(t, "openAIKey is empty", err.Error())
9798
}
9899

100+
func TestBuildExecuteModelOpenAISecretKeyFromEnv(t *testing.T) {
101+
102+
llmHoneypot := LLMHoneypot{
103+
Histories: make([]Message, 0),
104+
OpenAIKey: "",
105+
Protocol: tracer.SSH,
106+
Model: "gpt-4o",
107+
Provider: OpenAI,
108+
}
109+
110+
os.Setenv("OPEN_AI_SECRET_KEY", "sdjdnklfjndslkjanfk")
111+
112+
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
113+
114+
assert.Equal(t, "sdjdnklfjndslkjanfk", openAIGPTVirtualTerminal.OpenAIKey)
115+
116+
}
117+
99118
func TestBuildExecuteModelWithCustomPrompt(t *testing.T) {
100119
client := resty.New()
101120
httpmock.ActivateNonDefault(client.GetClient())
@@ -126,7 +145,7 @@ func TestBuildExecuteModelWithCustomPrompt(t *testing.T) {
126145
Histories: make([]Message, 0),
127146
OpenAIKey: "sdjdnklfjndslkjanfk",
128147
Protocol: tracer.HTTP,
129-
Model: "gpt4-o",
148+
Model: "gpt-4o",
130149
Provider: OpenAI,
131150
CustomPrompt: "hello world",
132151
}
@@ -148,7 +167,7 @@ func TestBuildExecuteModelFailValidationStrategyType(t *testing.T) {
148167
Histories: make([]Message, 0),
149168
OpenAIKey: "",
150169
Protocol: tracer.TCP,
151-
Model: "gpt4-o",
170+
Model: "gpt-4o",
152171
Provider: OpenAI,
153172
}
154173

@@ -206,7 +225,7 @@ func TestBuildExecuteModelSSHWithResultsOpenAI(t *testing.T) {
206225
Histories: make([]Message, 0),
207226
OpenAIKey: "sdjdnklfjndslkjanfk",
208227
Protocol: tracer.SSH,
209-
Model: "gpt4-o",
228+
Model: "gpt-4o",
210229
Provider: OpenAI,
211230
}
212231

@@ -282,7 +301,7 @@ func TestBuildExecuteModelSSHWithoutResults(t *testing.T) {
282301
Histories: make([]Message, 0),
283302
OpenAIKey: "sdjdnklfjndslkjanfk",
284303
Protocol: tracer.SSH,
285-
Model: "gpt4-o",
304+
Model: "gpt-4o",
286305
Provider: OpenAI,
287306
}
288307

@@ -325,7 +344,7 @@ func TestBuildExecuteModelHTTPWithResults(t *testing.T) {
325344
Histories: make([]Message, 0),
326345
OpenAIKey: "sdjdnklfjndslkjanfk",
327346
Protocol: tracer.HTTP,
328-
Model: "gpt4-o",
347+
Model: "gpt-4o",
329348
Provider: OpenAI,
330349
}
331350

@@ -362,7 +381,7 @@ func TestBuildExecuteModelHTTPWithoutResults(t *testing.T) {
362381
Histories: make([]Message, 0),
363382
OpenAIKey: "sdjdnklfjndslkjanfk",
364383
Protocol: tracer.HTTP,
365-
Model: "gpt4-o",
384+
Model: "gpt-4o",
366385
Provider: OpenAI,
367386
}
368387

protocols/strategies/ssh.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ import (
1313
"github.com/gliderlabs/ssh"
1414
"github.com/google/uuid"
1515
log "github.com/sirupsen/logrus"
16-
"golang.org/x/crypto/ssh/terminal"
16+
"golang.org/x/term"
1717
)
1818

1919
type SSHStrategy struct {
20+
Sessions map[string][]plugins.Message
2021
}
2122

2223
func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
24+
sshStrategy.Sessions = make(map[string][]plugins.Message)
2325
go func() {
2426
server := &ssh.Server{
2527
Addr: beelzebubServiceConfiguration.Address,
@@ -30,7 +32,9 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
3032
uuidSession := uuid.New()
3133

3234
host, port, _ := net.SplitHostPort(sess.RemoteAddr().String())
35+
sessionKey := host + sess.User()
3336

37+
// Inline SSH command
3438
if sess.RawCommand() != "" {
3539
for _, command := range beelzebubServiceConfiguration.Commands {
3640
matched, err := regexp.MatchString(command.Regex, sess.RawCommand())
@@ -52,8 +56,14 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
5256
llmProvider = plugins.OpenAI
5357
}
5458

59+
histories := make([]plugins.Message, 0)
60+
61+
if sshStrategy.Sessions[sessionKey] != nil {
62+
histories = sshStrategy.Sessions[sessionKey]
63+
}
64+
5565
llmHoneypot := plugins.LLMHoneypot{
56-
Histories: make([]plugins.Message, 0),
66+
Histories: histories,
5767
OpenAIKey: beelzebubServiceConfiguration.Plugin.OpenAISecretKey,
5868
Protocol: tracer.SSH,
5969
Host: beelzebubServiceConfiguration.Plugin.Host,
@@ -86,6 +96,10 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
8696
Command: sess.RawCommand(),
8797
CommandOutput: commandOutput,
8898
})
99+
var histories []plugins.Message
100+
histories = append(histories, plugins.Message{Role: plugins.USER.String(), Content: sess.RawCommand()})
101+
histories = append(histories, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
102+
sshStrategy.Sessions[sessionKey] = histories
89103
tr.TraceEvent(tracer.Event{
90104
Msg: "End SSH Session",
91105
Status: tracer.End.String(),
@@ -109,10 +123,14 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
109123
Description: beelzebubServiceConfiguration.Description,
110124
})
111125

112-
term := terminal.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
126+
terminal := term.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
113127
var histories []plugins.Message
128+
if sshStrategy.Sessions[sessionKey] != nil {
129+
histories = sshStrategy.Sessions[sessionKey]
130+
}
131+
114132
for {
115-
commandInput, err := term.ReadLine()
133+
commandInput, err := terminal.ReadLine()
116134
if err != nil {
117135
break
118136
}
@@ -160,7 +178,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
160178
histories = append(histories, plugins.Message{Role: plugins.USER.String(), Content: commandInput})
161179
histories = append(histories, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
162180

163-
term.Write(append([]byte(commandOutput), '\n'))
181+
terminal.Write(append([]byte(commandOutput), '\n'))
164182

165183
tr.TraceEvent(tracer.Event{
166184
Msg: "New SSH Terminal Session",
@@ -178,6 +196,7 @@ func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.Beelze
178196
}
179197
}
180198
}
199+
sshStrategy.Sessions[sessionKey] = histories
181200
tr.TraceEvent(tracer.Event{
182201
Msg: "End SSH Session",
183202
Status: tracer.End.String(),

0 commit comments

Comments
 (0)