Compare commits

..

39 Commits

Author SHA1 Message Date
a79937c5ae Build(deps): Bump golang.org/x/net from 0.33.0 to 0.36.0 (#180)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.33.0 to 0.36.0.
- [Commits](https://github.com/golang/net/compare/v0.33.0...v0.36.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-14 07:35:41 +01:00
bdabbe9adc Build(deps): Bump golang.org/x/term from 0.29.0 to 0.30.0 (#181)
* Build(deps): Bump golang.org/x/term from 0.29.0 to 0.30.0

Bumps [golang.org/x/term](https://github.com/golang/term) from 0.29.0 to 0.30.0.
- [Commits](https://github.com/golang/term/compare/v0.29.0...v0.30.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update codeql.yml

fixed go version on CodeQL pipeline

Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mario Candela <mario.candela.personal@gmail.com>
2025-03-14 07:27:14 +01:00
db0da03baa Fix: Update http-80.yaml, improve examples HTTP LLM Honeypot
Add LLM Provider name

Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
2025-03-13 08:15:51 +01:00
b062416c00 Feat: Add FallbackCommand for HTTP Strategy, refactor packages strategies (#175)
Add FallbackCommand for HTTP Strategy, refactor packages strategies, improve histories implementations.
2025-03-13 08:06:46 +01:00
933f02911b 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
2025-03-09 13:17:04 +01:00
ef07ca1203 Feat: continuous delivery pipeline add latest tag (#174)
Feat: continuous delivery pipeline add latest tag

Signed-off-by: James Hodgkinson <james@terminaloutcomes.com>
2025-03-02 05:30:36 +01:00
1f59685530 Feat: Improve HTTP Headers serializer json log #172 (#173)
* Changed Event struct, field headers from string to map[string][]string

* Add integration test for http Headers
2025-03-01 12:31:34 +01:00
f658a26b32 Feat: Update docker-image.yml to add multi-platform support (#171)
* Update docker-image.yml

Adds multi-arch support

Signed-off-by: James Hodgkinson <james@terminaloutcomes.com>
Co-authored-by: Mario Candela <mario.candela.personal@gmail.com>
2025-02-28 11:36:15 +01:00
3fb8a667b3 Update codeql.yml
Upgrade codeQL from v2 to v3

Signed-off-by: Mario Candela <mario.candela.personal@gmail.com>
2025-02-24 08:16:34 +01:00
8963bbc86d Fix: mapping LLMModel for SSH inline, removed old comments on docker-c… (#168)
Fix mapping LLMModel for SSH inline, removed old comments on docker-compose.yml
2025-02-20 22:41:28 +01:00
44ec44ea5c Fix LLM model name typo 2025-02-20 18:20:17 +01:00
38297faed2 Feat: Refactoring LLM Plugin, update docs. (#165)
Refactoring LLM Plugin, update docs.
2025-02-16 22:48:59 +01:00
8703d1afda Fix: llm plugin OpenAI generates random plaintext (hallucinations) (#163)
* Add tests to adopt TDD.

* Fix bug, LLM hallucinations
2025-02-16 16:27:10 +01:00
db804474d3 Add support for TLS based HTTP connections. (#158)
* Add support for TLS based HTTP connections, With Unit Tests.
2025-02-13 20:54:22 +01:00
48dd70d523 Build(deps): Bump golang.org/x/crypto from 0.32.0 to 0.33.0 (#157) 2025-02-10 07:07:41 +01:00
4813685834 Bump github.com/go-resty/resty/v2 from 2.16.4 to 2.16.5 (#156)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.16.4 to 2.16.5.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.16.4...v2.16.5)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-24 08:32:45 +01:00
6f6acb212b Bump github.com/go-resty/resty/v2 from 2.16.3 to 2.16.4 (#155)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.16.3 to 2.16.4.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.16.3...v2.16.4)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-21 08:16:54 +01:00
99c7287c02 Feat: Refactoring plugin:LLM honeypot custom prompt (#154)
refactoring LLM honeypot custom prompt
2025-01-16 08:46:13 +01:00
c3d2ff885d Feat: LLM Honeypot allow specifying the custom prompt #152 (#153)
* implement new feature, custom prompt

* Add doc for custom prompt
2025-01-14 08:45:30 +01:00
f1b35e9e43 Bump github.com/go-resty/resty/v2 from 2.16.2 to 2.16.3 (#151)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.16.2 to 2.16.3.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.16.2...v2.16.3)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-10 09:09:15 +01:00
d74499bb37 Bump golang.org/x/crypto from 0.31.0 to 0.32.0 (#149)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.31.0 to 0.32.0.
- [Commits](https://github.com/golang/crypto/compare/v0.31.0...v0.32.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-07 16:18:27 +01:00
5bba406e6b Bump github.com/gliderlabs/ssh from 0.3.7 to 0.3.8 (#148)
Bumps [github.com/gliderlabs/ssh](https://github.com/gliderlabs/ssh) from 0.3.7 to 0.3.8.
- [Release notes](https://github.com/gliderlabs/ssh/releases)
- [Commits](https://github.com/gliderlabs/ssh/compare/v0.3.7...v0.3.8)

---
updated-dependencies:
- dependency-name: github.com/gliderlabs/ssh
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-13 08:02:07 +01:00
ec931bd6f9 Bump golang.org/x/crypto from 0.30.0 to 0.31.0 (#147)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.30.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.30.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-12 07:58:47 +01:00
b7f7aa0170 Bump golang.org/x/crypto from 0.29.0 to 0.30.0 (#146)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.29.0 to 0.30.0.
- [Commits](https://github.com/golang/crypto/compare/v0.29.0...v0.30.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 09:07:15 +01:00
79f9162f24 Bump github.com/stretchr/testify from 1.9.0 to 1.10.0 (#145)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.9.0...v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-25 08:39:56 +01:00
24828e503b Bump github.com/go-resty/resty/v2 from 2.16.1 to 2.16.2 (#144)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.16.1 to 2.16.2.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.16.1...v2.16.2)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-25 08:39:40 +01:00
acd5aa0e9c Bump github.com/go-resty/resty/v2 from 2.16.0 to 2.16.1 (#143)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.16.0 to 2.16.1.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.16.0...v2.16.1)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 08:29:07 +01:00
480b734834 Bump github.com/go-resty/resty/v2 from 2.15.3 to 2.16.0 (#142)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.15.3 to 2.16.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.15.3...v2.16.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 15:26:54 +01:00
7556c76652 Bump golang.org/x/crypto from 0.28.0 to 0.29.0 (#141) 2024-11-09 23:40:16 +01:00
11421665db Bump github.com/prometheus/client_golang from 1.20.4 to 1.20.5 (#140) 2024-10-16 07:38:30 +02:00
cce0dcfa30 Bump golang.org/x/crypto from 0.27.0 to 0.28.0 (#137)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.27.0 to 0.28.0.
- [Commits](https://github.com/golang/crypto/compare/v0.27.0...v0.28.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 11:45:02 +02:00
4740b2b6f5 Bump github.com/go-resty/resty/v2 from 2.15.2 to 2.15.3 (#136)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.15.2 to 2.15.3.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.15.2...v2.15.3)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 14:14:45 +02:00
27eb88e050 Bump github.com/go-resty/resty/v2 from 2.15.1 to 2.15.2 (#135)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.15.1 to 2.15.2.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.15.1...v2.15.2)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 11:42:54 +02:00
9eaa503def Bump github.com/go-resty/resty/v2 from 2.15.0 to 2.15.1 (#134)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.15.0 to 2.15.1.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.15.0...v2.15.1)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 11:29:37 +02:00
f07ce7d3be Bump github.com/prometheus/client_golang from 1.20.3 to 1.20.4 (#133)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.20.3 to 1.20.4.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.20.3...v1.20.4)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 08:48:32 +02:00
a924b2cb8b Bump github.com/go-resty/resty/v2 from 2.14.0 to 2.15.0 (#132)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.14.0 to 2.15.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.14.0...v2.15.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 09:40:34 +02:00
8e81a8721e Feat: manage SSH inline command (#130)
manage SSH inline command
2024-09-07 12:54:37 +02:00
f40ce9215e Bump github.com/prometheus/client_golang from 1.20.2 to 1.20.3 (#128)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.20.2 to 1.20.3.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.20.3/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.20.2...v1.20.3)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-06 08:47:31 +02:00
0fc9ebb05e Bump golang.org/x/crypto from 0.26.0 to 0.27.0 (#127)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.26.0 to 0.27.0.
- [Commits](https://github.com/golang/crypto/compare/v0.26.0...v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-06 08:46:43 +02:00
23 changed files with 933 additions and 479 deletions

View File

@ -26,8 +26,13 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.20.0
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
@ -35,6 +40,6 @@ jobs:
run: go build ./...
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@ -1,31 +1,32 @@
---
name: Docker Hub Image
on:
push:
tags:
- 'v*.*.*'
jobs:
CD:
runs-on: ubuntu-latest
steps:
-
name: Checkout
- name: Checkout
uses: actions/checkout@v3
-
name: Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
-
name: Set up Docker Buildx
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build and push
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
tags: m4r10/beelzebub:${{ github.ref_name }}
tags: |
m4r10/beelzebub:${{ github.ref_name }}
m4r10/beelzebub:latest
platforms: linux/amd64,linux/arm64

View File

@ -211,9 +211,9 @@ commands:
#### Example SSH Honeypot
###### Honeypot LLM Honeypots
###### LLM Honeypots
Example with OpenAI GPT-4:
Follow a SSH LLM Honeypot using OpenAI as provider LLM:
```yaml
apiVersion: "v1"
@ -228,11 +228,12 @@ serverName: "ubuntu"
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
deadlineTimeoutSeconds: 60
plugin:
llmModel: "gpt4-o"
llmProvider: "openai"
llmModel: "gpt-4o" #Models https://platform.openai.com/docs/models
openAISecretKey: "sk-proj-123456"
```
Example with Ollama Llama3:
Examples with local Ollama instance using model codellama:7b:
```yaml
apiVersion: "v1"
@ -247,9 +248,30 @@ serverName: "ubuntu"
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
deadlineTimeoutSeconds: 60
plugin:
llmModel: "llama3"
llmProvider: "ollama"
llmModel: "codellama:7b" #Models https://ollama.com/search
host: "http://example.com/api/chat" #default http://localhost:11434/api/chat
```
Example with custom prompt:
```yaml
apiVersion: "v1"
protocol: "ssh"
address: ":2222"
description: "SSH interactive OpenAI GPT-4"
commands:
- regex: "^(.+)$"
plugin: "LLMHoneypot"
serverVersion: "OpenSSH"
serverName: "ubuntu"
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
deadlineTimeoutSeconds: 60
plugin:
llmProvider: "openai"
llmModel: "gpt-4o"
openAISecretKey: "sk-proj-123456"
prompt: "You will act as an Ubuntu Linux terminal. The user will type commands, and you are to reply with what the terminal should show. Your responses must be contained within a single code block."
```
###### SSH Honeypot on Port 22

View File

@ -3,15 +3,18 @@ package builder
import (
"errors"
"fmt"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/plugins"
"github.com/mariocandela/beelzebub/v3/protocols"
"github.com/mariocandela/beelzebub/v3/protocols/strategies"
"github.com/mariocandela/beelzebub/v3/tracer"
"io"
"net/http"
"os"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/plugins"
"github.com/mariocandela/beelzebub/v3/protocols"
"github.com/mariocandela/beelzebub/v3/protocols/strategies/HTTP"
"github.com/mariocandela/beelzebub/v3/protocols/strategies/SSH"
"github.com/mariocandela/beelzebub/v3/protocols/strategies/TCP"
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/prometheus/client_golang/prometheus/promhttp"
amqp "github.com/rabbitmq/amqp091-go"
log "github.com/sirupsen/logrus"
@ -106,9 +109,9 @@ Honeypot Framework, happy hacking!`)
}()
// Init Protocol strategies
secureShellStrategy := &strategies.SSHStrategy{}
hypertextTransferProtocolStrategy := &strategies.HTTPStrategy{}
transmissionControlProtocolStrategy := &strategies.TCPStrategy{}
secureShellStrategy := &SSH.SSHStrategy{}
hypertextTransferProtocolStrategy := &HTTP.HTTPStrategy{}
transmissionControlProtocolStrategy := &TCP.TCPStrategy{}
// Init Tracer strategies, and set the trace strategy default HTTP
protocolManager := protocols.InitProtocolManager(b.traceStrategy, hypertextTransferProtocolStrategy)
@ -122,7 +125,7 @@ Honeypot Framework, happy hacking!`)
return err
} else {
if len(honeypotsConfiguration) == 0 {
return errors.New("No honeypots configuration found")
return errors.New("no honeypots configuration found")
}
b.beelzebubServicesConfiguration = honeypotsConfiguration
}
@ -132,20 +135,16 @@ Honeypot Framework, happy hacking!`)
switch beelzebubServiceConfiguration.Protocol {
case "http":
protocolManager.SetProtocolStrategy(hypertextTransferProtocolStrategy)
break
case "ssh":
protocolManager.SetProtocolStrategy(secureShellStrategy)
break
case "tcp":
protocolManager.SetProtocolStrategy(transmissionControlProtocolStrategy)
break
default:
log.Fatalf("Protocol %s not managed", beelzebubServiceConfiguration.Protocol)
continue
log.Fatalf("protocol %s not managed", beelzebubServiceConfiguration.Protocol)
}
if err := protocolManager.InitService(beelzebubServiceConfiguration); err != nil {
return errors.New(fmt.Sprintf("Error during init protocol: %s, %s", beelzebubServiceConfiguration.Protocol, err.Error()))
return fmt.Errorf("error during init protocol: %s, %s", beelzebubServiceConfiguration.Protocol, err.Error())
}
}

View File

@ -22,5 +22,6 @@ commands:
plugin: "LLMHoneypot"
statusCode: 200
plugin:
llmModel: "gpt4-o"
openAISecretKey: "sk-proj-123456"
llmProvider: "openai"
llmModel: "gpt-4o"
openAISecretKey: "sk-proj-123456"

View File

@ -7,7 +7,9 @@ commands:
plugin: "LLMHoneypot"
serverVersion: "OpenSSH"
serverName: "ubuntu"
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$"
passwordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456|1234)$"
deadlineTimeoutSeconds: 6000
plugin:
llmModel: "llama3"
llmProvider: "openai"
llmModel: "gpt-4o"
openAISecretKey: "sk-proj-12345"

View File

@ -3,18 +3,18 @@ version: "3.9"
services:
beelzebub:
build: .
#network_mode: host # Not work on Mac OS
container_name: beelzebub
restart: always
ports: # Remove me, if you use configuration network_mode: host
ports:
- "22:22"
- "2222:2222"
- "8080:8080"
- "8081:8081"
- "80:80"
- "3306:3306"
- "2112:2112" # Prometheus openmetrics
- "2112:2112" #Prometheus Open Metrics
environment:
RABBITMQ_URI: ${RABBITMQ_URI}
OPEN_AI_SECRET_KEY: ${OPEN_AI_SECRET_KEY}
volumes:
- "./configurations:/configurations"

16
go.mod
View File

@ -3,16 +3,17 @@ module github.com/mariocandela/beelzebub/v3
go 1.20
require (
github.com/gliderlabs/ssh v0.3.7
github.com/go-resty/resty/v2 v2.14.0
github.com/gliderlabs/ssh v0.3.8
github.com/go-resty/resty/v2 v2.16.5
github.com/google/uuid v1.6.0
github.com/jarcoal/httpmock v1.3.1
github.com/melbahja/goph v1.4.0
github.com/prometheus/client_golang v1.20.2
github.com/prometheus/client_golang v1.20.5
github.com/rabbitmq/amqp091-go v1.10.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.26.0
github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.35.0
golang.org/x/term v0.30.0
gopkg.in/yaml.v3 v3.0.1
)
@ -31,8 +32,7 @@ require (
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.23.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/sys v0.31.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
)

69
go.sum
View File

@ -8,12 +8,11 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
github.com/go-resty/resty/v2 v2.14.0 h1:/rhkzsAqGQkozwfKS5aFAbb6TyKd3zyFRWcdRXLPCAU=
github.com/go-resty/resty/v2 v2.14.0/go.mod h1:IW6mekUOsElt9C7oWr0XRt9BNSD6D5rr9mhk6NjmNHg=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
@ -37,8 +36,8 @@ github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
@ -52,42 +51,26 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -97,42 +80,22 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=

View File

@ -0,0 +1,47 @@
package historystore
import (
"sync"
"github.com/mariocandela/beelzebub/v3/plugins"
)
// HistoryStore is a thread-safe structure for storing Messages used to build LLM Context.
type HistoryStore struct {
sync.RWMutex
sessions map[string][]plugins.Message
}
// NewHistoryStore returns a prepared HistoryStore
func NewHistoryStore() *HistoryStore {
return &HistoryStore{
sessions: make(map[string][]plugins.Message),
}
}
// HasKey returns true if the supplied key exists in the map.
func (hs *HistoryStore) HasKey(key string) bool {
hs.RLock()
defer hs.RUnlock()
_, ok := hs.sessions[key]
return ok
}
// Query returns the value stored at the map
func (hs *HistoryStore) Query(key string) []plugins.Message {
hs.RLock()
defer hs.RUnlock()
return hs.sessions[key]
}
// Append will add the slice of Mesages to the entry for the key.
// If the map has not yet been initalised, then a new map is created.
func (hs *HistoryStore) Append(key string, message ...plugins.Message) {
hs.Lock()
defer hs.Unlock()
// In the unexpected case that the map has not yet been initalised, create it.
if hs.sessions == nil {
hs.sessions = make(map[string][]plugins.Message)
}
hs.sessions[key] = append(hs.sessions[key], message...)
}

View File

@ -0,0 +1,47 @@
package historystore
import (
"testing"
"github.com/mariocandela/beelzebub/v3/plugins"
"github.com/stretchr/testify/assert"
)
func TestNewHistoryStore(t *testing.T) {
hs := NewHistoryStore()
assert.NotNil(t, hs)
assert.NotNil(t, hs.sessions)
}
func TestHasKey(t *testing.T) {
hs := NewHistoryStore()
hs.sessions["testKey"] = []plugins.Message{}
assert.True(t, hs.HasKey("testKey"))
assert.False(t, hs.HasKey("nonExistentKey"))
}
func TestQuery(t *testing.T) {
hs := NewHistoryStore()
expectedMessages := []plugins.Message{{Role: "user", Content: "Hello"}}
hs.sessions["testKey"] = expectedMessages
actualMessages := hs.Query("testKey")
assert.Equal(t, expectedMessages, actualMessages)
}
func TestAppend(t *testing.T) {
hs := NewHistoryStore()
message1 := plugins.Message{Role: "user", Content: "Hello"}
message2 := plugins.Message{Role: "assistant", Content: "Hi"}
hs.Append("testKey", message1)
assert.Equal(t, []plugins.Message{message1}, hs.sessions["testKey"])
hs.Append("testKey", message2)
assert.Equal(t, []plugins.Message{message1, message2}, hs.sessions["testKey"])
}
func TestAppendNilSessions(t *testing.T) {
hs := &HistoryStore{}
message1 := plugins.Message{Role: "user", Content: "Hello"}
hs.Append("testKey", message1)
assert.NotNil(t, hs.sessions)
assert.Equal(t, []plugins.Message{message1}, hs.sessions["testKey"])
}

View File

@ -67,8 +67,11 @@ func (suite *IntegrationTestSuite) TestInvokeHTTPHoneypot() {
response, err := resty.New().R().
Get(suite.httpHoneypotHost + "/index.php")
response.Header().Del("Date")
suite.Require().NoError(err)
suite.Equal(http.StatusOK, response.StatusCode())
suite.Equal(http.Header{"Content-Length": []string{"15"}, "Content-Type": []string{"text/html"}, "Server": []string{"Apache/2.4.53 (Debian)"}, "X-Powered-By": []string{"PHP/7.4.29"}}, response.Header())
suite.Equal("mocked response", string(response.Body()))
response, err = resty.New().R().

View File

@ -52,6 +52,8 @@ type Plugin struct {
OpenAISecretKey string `yaml:"openAISecretKey"`
Host string `yaml:"host"`
LLMModel string `yaml:"llmModel"`
LLMProvider string `yaml:"llmProvider"`
Prompt string `yaml:"prompt"`
}
// BeelzebubServiceConfiguration is the struct that contains the configurations of the honeypot service
@ -60,6 +62,7 @@ type BeelzebubServiceConfiguration struct {
Protocol string `yaml:"protocol"`
Address string `yaml:"address"`
Commands []Command `yaml:"commands"`
FallbackCommand Command `yaml:"fallbackCommand"`
ServerVersion string `yaml:"serverVersion"`
ServerName string `yaml:"serverName"`
DeadlineTimeoutSeconds int `yaml:"deadlineTimeoutSeconds"`
@ -67,6 +70,8 @@ type BeelzebubServiceConfiguration struct {
Description string `yaml:"description"`
Banner string `yaml:"banner"`
Plugin Plugin `yaml:"plugin"`
TLSCertPath string `yaml:"tlsCertPath"`
TLSKeyPath string `yaml:"tlsKeyPath"`
}
// Command is the struct that contains the configurations of the commands

View File

@ -49,15 +49,22 @@ func mockReadfilebytesBeelzebubServiceConfiguration(filePath string) ([]byte, er
apiVersion: "v1"
protocol: "http"
address: ":8080"
tlsCertPath: "/tmp/cert.crt"
tlsKeyPath: "/tmp/cert.key"
commands:
- regex: "wp-admin"
handler: "login"
headers:
- "Content-Type: text/html"
fallbackCommand:
handler: "404 Not Found!"
statusCode: 404
plugin:
openAISecretKey: "qwerty"
llmModel: "llama3"
llmProvider: "ollama"
host: "localhost:1563"
prompt: "hello world"
`)
return beelzebubServiceConfiguration, nil
}
@ -130,9 +137,15 @@ func TestReadConfigurationsServicesValid(t *testing.T) {
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Handler, "login")
assert.Equal(t, len(firstBeelzebubServiceConfiguration.Commands[0].Headers), 1)
assert.Equal(t, firstBeelzebubServiceConfiguration.Commands[0].Headers[0], "Content-Type: text/html")
assert.Equal(t, firstBeelzebubServiceConfiguration.FallbackCommand.Handler, "404 Not Found!")
assert.Equal(t, firstBeelzebubServiceConfiguration.FallbackCommand.StatusCode, 404)
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.OpenAISecretKey, "qwerty")
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.LLMModel, "llama3")
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.LLMProvider, "ollama")
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.Host, "localhost:1563")
assert.Equal(t, firstBeelzebubServiceConfiguration.Plugin.Prompt, "hello world")
assert.Equal(t, firstBeelzebubServiceConfiguration.TLSCertPath, "/tmp/cert.crt")
assert.Equal(t, firstBeelzebubServiceConfiguration.TLSKeyPath, "/tmp/cert.key")
}
func TestGelAllFilesNameByDirName(t *testing.T) {
@ -168,7 +181,8 @@ func TestGelAllFilesNameByDirNameError(t *testing.T) {
files, err := gelAllFilesNameByDirName("nosuchfile")
assert.Nil(t, files)
assert.Equal(t, "open nosuchfile: no such file or directory", err.Error())
// Windows and Linux return slightly different error strings, but share a common prefix, so check for that.
assert.Contains(t, err.Error(), "open nosuchfile: ")
}
func TestReadFileBytesByFilePath(t *testing.T) {

View File

@ -85,7 +85,7 @@ func TestGetHoneypotsConfigurationsWithResults(t *testing.T) {
resp, err := httpmock.NewJsonResponse(200, &[]HoneypotConfigResponseDTO{
{
ID: "123456",
Config: "apiVersion: \"v1\"\nprotocol: \"ssh\"\naddress: \":2222\"\ndescription: \"SSH interactive ChatGPT\"\ncommands:\n - regex: \"^(.+)$\"\n plugin: \"LLMHoneypot\"\nserverVersion: \"OpenSSH\"\nserverName: \"ubuntu\"\npasswordRegex: \"^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$\"\ndeadlineTimeoutSeconds: 60\nplugin:\n llmModel: \"gpt4-o\"\n openAISecretKey: \"1234\"\n",
Config: "apiVersion: \"v1\"\nprotocol: \"ssh\"\naddress: \":2222\"\ndescription: \"SSH interactive ChatGPT\"\ncommands:\n - regex: \"^(.+)$\"\n plugin: \"LLMHoneypot\"\nserverVersion: \"OpenSSH\"\nserverName: \"ubuntu\"\npasswordRegex: \"^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$\"\ndeadlineTimeoutSeconds: 60\nplugin:\n llmModel: \"gpt-4o\"\n openAISecretKey: \"1234\"\n",
TokenID: "1234567",
},
})
@ -120,7 +120,7 @@ func TestGetHoneypotsConfigurationsWithResults(t *testing.T) {
PasswordRegex: "^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$",
DeadlineTimeoutSeconds: 60,
Plugin: parser.Plugin{
LLMModel: "gpt4-o",
LLMModel: "gpt-4o",
OpenAISecretKey: "1234",
},
},

View File

@ -6,25 +6,29 @@ import (
"fmt"
"github.com/go-resty/resty/v2"
"github.com/mariocandela/beelzebub/v3/tracer"
log "github.com/sirupsen/logrus"
"os"
"regexp"
"strings"
)
const (
systemPromptVirtualizeLinuxTerminal = "You will act as an Ubuntu Linux terminal. The user will type commands, and you are to reply with what the terminal should show. Your responses must be contained within a single code block. Do not provide explanations or type commands unless explicitly instructed by the user. Your entire response/output is going to consist of a simple text with \n for new line, and you will NOT wrap it within string md markers"
systemPromptVirtualizeLinuxTerminal = "You will act as an Ubuntu Linux terminal. The user will type commands, and you are to reply with what the terminal should show. Your responses must be contained within a single code block. Do not provide note. Do not provide explanations or type commands unless explicitly instructed by the user. Your entire response/output is going to consist of a simple text with \n for new line, and you will NOT wrap it within string md markers"
systemPromptVirtualizeHTTPServer = "You will act as an unsecure HTTP Server with multiple vulnerability like aws and git credentials stored into root http directory. The user will send HTTP requests, and you are to reply with what the server should show. Do not provide explanations or type commands unless explicitly instructed by the user."
LLMPluginName = "LLMHoneypot"
openAIGPTEndpoint = "https://api.openai.com/v1/chat/completions"
openAIEndpoint = "https://api.openai.com/v1/chat/completions"
ollamaEndpoint = "http://localhost:11434/api/chat"
)
type LLMHoneypot struct {
Histories []Message
OpenAIKey string
client *resty.Client
Protocol tracer.Protocol
Model LLMModel
Host string
Histories []Message
OpenAIKey string
client *resty.Client
Protocol tracer.Protocol
Provider LLMProvider
Model string
Host string
CustomPrompt string
}
type Choice struct {
@ -70,21 +74,21 @@ func (role Role) String() string {
return [...]string{"system", "user", "assistant"}[role]
}
type LLMModel int
type LLMProvider int
const (
LLAMA3 LLMModel = iota
GPT4O
Ollama LLMProvider = iota
OpenAI
)
func FromStringToLLMModel(llmModel string) (LLMModel, error) {
switch llmModel {
case "llama3":
return LLAMA3, nil
case "gpt4-o":
return GPT4O, nil
func FromStringToLLMProvider(llmProvider string) (LLMProvider, error) {
switch strings.ToLower(llmProvider) {
case "ollama":
return Ollama, nil
case "openai":
return OpenAI, nil
default:
return -1, fmt.Errorf("model %s not found", llmModel)
return -1, fmt.Errorf("provider %s not found, valid providers: ollama, openai", llmProvider)
}
}
@ -92,17 +96,26 @@ func InitLLMHoneypot(config LLMHoneypot) *LLMHoneypot {
// Inject the dependencies
config.client = resty.New()
if os.Getenv("OPEN_AI_SECRET_KEY") != "" {
config.OpenAIKey = os.Getenv("OPEN_AI_SECRET_KEY")
}
return &config
}
func buildPrompt(histories []Message, protocol tracer.Protocol, command string) ([]Message, error) {
func (llmHoneypot *LLMHoneypot) buildPrompt(command string) ([]Message, error) {
var messages []Message
var prompt string
switch protocol {
switch llmHoneypot.Protocol {
case tracer.SSH:
prompt = systemPromptVirtualizeLinuxTerminal
if llmHoneypot.CustomPrompt != "" {
prompt = llmHoneypot.CustomPrompt
}
messages = append(messages, Message{
Role: SYSTEM.String(),
Content: systemPromptVirtualizeLinuxTerminal,
Content: prompt,
})
messages = append(messages, Message{
Role: USER.String(),
@ -112,13 +125,17 @@ func buildPrompt(histories []Message, protocol tracer.Protocol, command string)
Role: ASSISTANT.String(),
Content: "/home/user",
})
for _, history := range histories {
for _, history := range llmHoneypot.Histories {
messages = append(messages, history)
}
case tracer.HTTP:
prompt = systemPromptVirtualizeHTTPServer
if llmHoneypot.CustomPrompt != "" {
prompt = llmHoneypot.CustomPrompt
}
messages = append(messages, Message{
Role: SYSTEM.String(),
Content: systemPromptVirtualizeHTTPServer,
Content: prompt,
})
messages = append(messages, Message{
Role: USER.String(),
@ -143,7 +160,7 @@ func (llmHoneypot *LLMHoneypot) openAICaller(messages []Message) (string, error)
var err error
requestJson, err := json.Marshal(Request{
Model: "gpt-4o",
Model: llmHoneypot.Model,
Messages: messages,
Stream: false,
})
@ -156,7 +173,7 @@ func (llmHoneypot *LLMHoneypot) openAICaller(messages []Message) (string, error)
}
if llmHoneypot.Host == "" {
llmHoneypot.Host = openAIGPTEndpoint
llmHoneypot.Host = openAIEndpoint
}
log.Debug(string(requestJson))
@ -175,14 +192,14 @@ func (llmHoneypot *LLMHoneypot) openAICaller(messages []Message) (string, error)
return "", errors.New("no choices")
}
return response.Result().(*Response).Choices[0].Message.Content, nil
return removeQuotes(response.Result().(*Response).Choices[0].Message.Content), nil
}
func (llmHoneypot *LLMHoneypot) ollamaCaller(messages []Message) (string, error) {
var err error
requestJson, err := json.Marshal(Request{
Model: "llama3",
Model: llmHoneypot.Model,
Messages: messages,
Stream: false,
})
@ -206,24 +223,30 @@ func (llmHoneypot *LLMHoneypot) ollamaCaller(messages []Message) (string, error)
}
log.Debug(response)
return response.Result().(*Response).Message.Content, nil
return removeQuotes(response.Result().(*Response).Message.Content), nil
}
func (llmHoneypot *LLMHoneypot) ExecuteModel(command string) (string, error) {
var err error
var prompt []Message
prompt, err := buildPrompt(llmHoneypot.Histories, llmHoneypot.Protocol, command)
prompt, err = llmHoneypot.buildPrompt(command)
if err != nil {
return "", err
}
switch llmHoneypot.Model {
case LLAMA3:
switch llmHoneypot.Provider {
case Ollama:
return llmHoneypot.ollamaCaller(prompt)
case GPT4O:
case OpenAI:
return llmHoneypot.openAICaller(prompt)
default:
return "", errors.New("no model selected")
return "", fmt.Errorf("provider %d not found, valid providers: ollama, openai", llmHoneypot.Provider)
}
}
func removeQuotes(content string) string {
regex := regexp.MustCompile("(```( *)?([a-z]*)?(\\n)?)")
return regex.ReplaceAllString(content, "")
}

View File

@ -6,6 +6,7 @@ import (
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/stretchr/testify/assert"
"net/http"
"os"
"testing"
)
@ -16,8 +17,13 @@ func TestBuildPromptEmptyHistory(t *testing.T) {
var histories []Message
command := "pwd"
honeypot := LLMHoneypot{
Histories: histories,
Protocol: tracer.SSH,
}
//When
prompt, err := buildPrompt(histories, tracer.SSH, command)
prompt, err := honeypot.buildPrompt(command)
//Then
assert.Nil(t, err)
@ -35,21 +41,53 @@ func TestBuildPromptWithHistory(t *testing.T) {
command := "pwd"
honeypot := LLMHoneypot{
Histories: histories,
Protocol: tracer.SSH,
}
//When
prompt, err := buildPrompt(histories, tracer.SSH, command)
prompt, err := honeypot.buildPrompt(command)
//Then
assert.Nil(t, err)
assert.Equal(t, SystemPromptLen+1, len(prompt))
}
func TestBuildPromptWithCustomPrompt(t *testing.T) {
//Given
var histories = []Message{
{
Role: "cat hello.txt",
Content: "world",
},
}
command := "pwd"
honeypot := LLMHoneypot{
Histories: histories,
Protocol: tracer.SSH,
CustomPrompt: "act as calculator",
}
//When
prompt, err := honeypot.buildPrompt(command)
//Then
assert.Nil(t, err)
assert.Equal(t, prompt[0].Content, "act as calculator")
assert.Equal(t, prompt[0].Role, SYSTEM.String())
}
func TestBuildExecuteModelFailValidation(t *testing.T) {
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
OpenAIKey: "",
Protocol: tracer.SSH,
Model: GPT4O,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
@ -59,13 +97,78 @@ func TestBuildExecuteModelFailValidation(t *testing.T) {
assert.Equal(t, "openAIKey is empty", err.Error())
}
func TestBuildExecuteModelOpenAISecretKeyFromEnv(t *testing.T) {
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
OpenAIKey: "",
Protocol: tracer.SSH,
Model: "gpt-4o",
Provider: OpenAI,
}
os.Setenv("OPEN_AI_SECRET_KEY", "sdjdnklfjndslkjanfk")
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
assert.Equal(t, "sdjdnklfjndslkjanfk", openAIGPTVirtualTerminal.OpenAIKey)
}
func TestBuildExecuteModelWithCustomPrompt(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterMatcherResponder("POST", openAIEndpoint,
httpmock.BodyContainsString("hello world"),
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Choices: []Choice{
{
Message: Message{
Role: SYSTEM.String(),
Content: "[default]\nregion = us-west-2\noutput = json",
},
},
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
OpenAIKey: "sdjdnklfjndslkjanfk",
Protocol: tracer.HTTP,
Model: "gpt-4o",
Provider: OpenAI,
CustomPrompt: "hello world",
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
openAIGPTVirtualTerminal.client = client
//When
str, err := openAIGPTVirtualTerminal.ExecuteModel("GET /.aws/credentials")
//Then
assert.Nil(t, err)
assert.Equal(t, "[default]\nregion = us-west-2\noutput = json", str)
}
func TestBuildExecuteModelFailValidationStrategyType(t *testing.T) {
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
OpenAIKey: "",
Protocol: tracer.TCP,
Model: GPT4O,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
@ -80,7 +183,8 @@ func TestBuildExecuteModelFailValidationModelType(t *testing.T) {
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
Protocol: tracer.SSH,
Model: 5,
Model: "llama3",
Provider: 5,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
@ -98,7 +202,7 @@ func TestBuildExecuteModelSSHWithResultsOpenAI(t *testing.T) {
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
httpmock.RegisterResponder("POST", openAIEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Choices: []Choice{
@ -121,7 +225,8 @@ func TestBuildExecuteModelSSHWithResultsOpenAI(t *testing.T) {
Histories: make([]Message, 0),
OpenAIKey: "sdjdnklfjndslkjanfk",
Protocol: tracer.SSH,
Model: GPT4O,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
@ -159,7 +264,8 @@ func TestBuildExecuteModelSSHWithResultsLLama(t *testing.T) {
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
Protocol: tracer.SSH,
Model: LLAMA3,
Model: "llama3",
Provider: Ollama,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
@ -179,7 +285,7 @@ func TestBuildExecuteModelSSHWithoutResults(t *testing.T) {
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
httpmock.RegisterResponder("POST", openAIEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Choices: []Choice{},
@ -195,7 +301,8 @@ func TestBuildExecuteModelSSHWithoutResults(t *testing.T) {
Histories: make([]Message, 0),
OpenAIKey: "sdjdnklfjndslkjanfk",
Protocol: tracer.SSH,
Model: GPT4O,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
@ -214,7 +321,7 @@ func TestBuildExecuteModelHTTPWithResults(t *testing.T) {
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
httpmock.RegisterResponder("POST", openAIEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Choices: []Choice{
@ -237,7 +344,8 @@ func TestBuildExecuteModelHTTPWithResults(t *testing.T) {
Histories: make([]Message, 0),
OpenAIKey: "sdjdnklfjndslkjanfk",
Protocol: tracer.HTTP,
Model: GPT4O,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
@ -257,7 +365,7 @@ func TestBuildExecuteModelHTTPWithoutResults(t *testing.T) {
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", openAIGPTEndpoint,
httpmock.RegisterResponder("POST", openAIEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Choices: []Choice{},
@ -273,7 +381,8 @@ func TestBuildExecuteModelHTTPWithoutResults(t *testing.T) {
Histories: make([]Message, 0),
OpenAIKey: "sdjdnklfjndslkjanfk",
Protocol: tracer.HTTP,
Model: GPT4O,
Model: "gpt-4o",
Provider: OpenAI,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
@ -287,14 +396,105 @@ func TestBuildExecuteModelHTTPWithoutResults(t *testing.T) {
}
func TestFromString(t *testing.T) {
model, err := FromStringToLLMModel("llama3")
model, err := FromStringToLLMProvider("openai")
assert.Nil(t, err)
assert.Equal(t, LLAMA3, model)
assert.Equal(t, OpenAI, model)
model, err = FromStringToLLMModel("gpt4-o")
model, err = FromStringToLLMProvider("ollama")
assert.Nil(t, err)
assert.Equal(t, GPT4O, model)
assert.Equal(t, Ollama, model)
model, err = FromStringToLLMModel("beelzebub-model")
assert.Errorf(t, err, "model beelzebub-model not found")
model, err = FromStringToLLMProvider("beelzebub-model")
assert.Errorf(t, err, "provider beelzebub-model not found")
}
func TestBuildExecuteModelSSHWithoutPlaintextSection(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", ollamaEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Message: Message{
Role: SYSTEM.String(),
Content: "```plaintext\n```\n",
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
Protocol: tracer.SSH,
Model: "llama3",
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
openAIGPTVirtualTerminal.client = client
//When
str, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
//Then
assert.Nil(t, err)
assert.Equal(t, "", str)
}
func TestBuildExecuteModelSSHWithoutQuotesSection(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
// Given
httpmock.RegisterResponder("POST", ollamaEndpoint,
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, &Response{
Message: Message{
Role: SYSTEM.String(),
Content: "```\n```\n",
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
llmHoneypot := LLMHoneypot{
Histories: make([]Message, 0),
Protocol: tracer.SSH,
Model: "llama3",
Provider: Ollama,
}
openAIGPTVirtualTerminal := InitLLMHoneypot(llmHoneypot)
openAIGPTVirtualTerminal.client = client
//When
str, err := openAIGPTVirtualTerminal.ExecuteModel("ls")
//Then
assert.Nil(t, err)
assert.Equal(t, "", str)
}
func TestRemoveQuotes(t *testing.T) {
plaintext := "```plaintext\n```"
bash := "```bash\n```"
onlyQuotes := "```\n```"
complexText := "```plaintext\ntop - 10:30:48 up 1 day, 4:30, 2 users, load average: 0.15, 0.10, 0.08\nTasks: 198 total, 1 running, 197 sleeping, 0 stopped, 0 zombie\n```"
complexText2 := "```\ntop - 15:06:59 up 10 days, 3:17, 1 user, load average: 0.10, 0.09, 0.08\nTasks: 285 total\n```"
assert.Equal(t, "", removeQuotes(plaintext))
assert.Equal(t, "", removeQuotes(bash))
assert.Equal(t, "", removeQuotes(onlyQuotes))
assert.Equal(t, "top - 10:30:48 up 1 day, 4:30, 2 users, load average: 0.15, 0.10, 0.08\nTasks: 198 total, 1 running, 197 sleeping, 0 stopped, 0 zombie\n", removeQuotes(complexText))
assert.Equal(t, "top - 15:06:59 up 10 days, 3:17, 1 user, load average: 0.10, 0.09, 0.08\nTasks: 285 total\n", removeQuotes(complexText2))
}

View File

@ -0,0 +1,186 @@
package HTTP
import (
"fmt"
"io"
"net"
"net/http"
"regexp"
"strings"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/plugins"
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
type HTTPStrategy struct{}
type httpResponse struct {
StatusCode int
Headers []string
Body string
}
func (httpStrategy HTTPStrategy) Init(servConf parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
serverMux := http.NewServeMux()
serverMux.HandleFunc("/", func(responseWriter http.ResponseWriter, request *http.Request) {
traceRequest(request, tr, servConf.Description)
var matched bool
var resp httpResponse
var err error
for _, command := range servConf.Commands {
var err error
matched, err = regexp.MatchString(command.Regex, request.RequestURI)
if err != nil {
log.Errorf("error parsing regex: %s, %s", command.Regex, err.Error())
resp.StatusCode = 500
resp.Body = "500 Internal Server Error"
continue
}
if matched {
resp, err = buildHTTPResponse(servConf, command, request)
if err != nil {
log.Errorf("error building http response: %s: %v", request.RequestURI, err)
resp.StatusCode = 500
resp.Body = "500 Internal Server Error"
}
break
}
}
// If none of the main commands matched, and we have a fallback command configured, process it here.
// The regexp is ignored for fallback commands, as they are catch-all for any request.
if !matched {
command := servConf.FallbackCommand
if command.Handler != "" || command.Plugin != "" {
resp, err = buildHTTPResponse(servConf, command, request)
if err != nil {
log.Errorf("error building http response: %s: %v", request.RequestURI, err)
resp.StatusCode = 500
resp.Body = "500 Internal Server Error"
}
}
}
setResponseHeaders(responseWriter, resp.Headers, resp.StatusCode)
fmt.Fprint(responseWriter, resp.Body)
})
go func() {
var err error
// Launch a TLS supporting server if we are supplied a TLS Key and Certificate.
// If relative paths are supplied, they are relative to the CWD of the binary.
// The can be self-signed, only the client will validate this (or not).
if servConf.TLSKeyPath != "" && servConf.TLSCertPath != "" {
err = http.ListenAndServeTLS(servConf.Address, servConf.TLSCertPath, servConf.TLSKeyPath, serverMux)
} else {
err = http.ListenAndServe(servConf.Address, serverMux)
}
if err != nil {
log.Errorf("error during init HTTP Protocol: %v", err)
return
}
}()
log.WithFields(log.Fields{
"port": servConf.Address,
"commands": len(servConf.Commands),
}).Infof("Init service: %s", servConf.Description)
return nil
}
func buildHTTPResponse(servConf parser.BeelzebubServiceConfiguration, command parser.Command, request *http.Request) (httpResponse, error) {
resp := httpResponse{
Body: command.Handler,
Headers: command.Headers,
StatusCode: command.StatusCode,
}
if command.Plugin == plugins.LLMPluginName {
llmProvider, err := plugins.FromStringToLLMProvider(servConf.Plugin.LLMProvider)
if err != nil {
log.Errorf("error: %v", err)
resp.Body = "404 Not Found!"
return resp, err
}
llmHoneypot := plugins.LLMHoneypot{
Histories: make([]plugins.Message, 0),
OpenAIKey: servConf.Plugin.OpenAISecretKey,
Protocol: tracer.HTTP,
Host: servConf.Plugin.Host,
Model: servConf.Plugin.LLMModel,
Provider: llmProvider,
CustomPrompt: servConf.Plugin.Prompt,
}
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
command := fmt.Sprintf("%s %s", request.Method, request.RequestURI)
completions, err := llmHoneypotInstance.ExecuteModel(command)
if err != nil {
resp.Body = "404 Not Found!"
return resp, fmt.Errorf("ExecuteModel error: %s, %v", command, err)
}
resp.Body = completions
}
return resp, nil
}
func traceRequest(request *http.Request, tr tracer.Tracer, HoneypotDescription string) {
bodyBytes, err := io.ReadAll(request.Body)
body := ""
if err == nil {
body = string(bodyBytes)
}
host, port, _ := net.SplitHostPort(request.RemoteAddr)
event := tracer.Event{
Msg: "HTTP New request",
RequestURI: request.RequestURI,
Protocol: tracer.HTTP.String(),
HTTPMethod: request.Method,
Body: body,
HostHTTPRequest: request.Host,
UserAgent: request.UserAgent(),
Cookies: mapCookiesToString(request.Cookies()),
Headers: request.Header,
Status: tracer.Stateless.String(),
RemoteAddr: request.RemoteAddr,
SourceIp: host,
SourcePort: port,
ID: uuid.New().String(),
Description: HoneypotDescription,
}
// Capture the TLS details from the request, if provided.
if request.TLS != nil {
event.Msg = "HTTPS New Request"
event.TLSServerName = request.TLS.ServerName
}
tr.TraceEvent(event)
}
func mapCookiesToString(cookies []*http.Cookie) string {
cookiesString := ""
for _, cookie := range cookies {
cookiesString += cookie.String()
}
return cookiesString
}
func setResponseHeaders(responseWriter http.ResponseWriter, headers []string, statusCode int) {
for _, headerStr := range headers {
keyValue := strings.Split(headerStr, ":")
if len(keyValue) > 1 {
responseWriter.Header().Add(keyValue[0], keyValue[1])
}
}
// http.StatusText(statusCode): empty string if the code is unknown.
if len(http.StatusText(statusCode)) > 0 {
responseWriter.WriteHeader(statusCode)
}
}

View File

@ -0,0 +1,242 @@
package SSH
import (
"fmt"
"net"
"regexp"
"strings"
"time"
"github.com/mariocandela/beelzebub/v3/historystore"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/plugins"
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/gliderlabs/ssh"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"golang.org/x/term"
)
type SSHStrategy struct {
Sessions *historystore.HistoryStore
}
func (sshStrategy *SSHStrategy) Init(servConf parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
if sshStrategy.Sessions == nil {
sshStrategy.Sessions = historystore.NewHistoryStore()
}
go func() {
server := &ssh.Server{
Addr: servConf.Address,
MaxTimeout: time.Duration(servConf.DeadlineTimeoutSeconds) * time.Second,
IdleTimeout: time.Duration(servConf.DeadlineTimeoutSeconds) * time.Second,
Version: servConf.ServerVersion,
Handler: func(sess ssh.Session) {
uuidSession := uuid.New()
host, port, _ := net.SplitHostPort(sess.RemoteAddr().String())
sessionKey := "SSH" + host + sess.User()
// Inline SSH command
if sess.RawCommand() != "" {
for _, command := range servConf.Commands {
matched, err := regexp.MatchString(command.Regex, sess.RawCommand())
if err != nil {
log.Errorf("error regex: %s, %s", command.Regex, err.Error())
continue
}
if matched {
commandOutput := command.Handler
if command.Plugin == plugins.LLMPluginName {
llmProvider, err := plugins.FromStringToLLMProvider(servConf.Plugin.LLMProvider)
if err != nil {
log.Errorf("error: %s", err.Error())
commandOutput = "command not found"
llmProvider = plugins.OpenAI
}
var histories []plugins.Message
if sshStrategy.Sessions.HasKey(sessionKey) {
histories = sshStrategy.Sessions.Query(sessionKey)
}
llmHoneypot := plugins.LLMHoneypot{
Histories: histories,
OpenAIKey: servConf.Plugin.OpenAISecretKey,
Protocol: tracer.SSH,
Host: servConf.Plugin.Host,
Model: servConf.Plugin.LLMModel,
Provider: llmProvider,
CustomPrompt: servConf.Plugin.Prompt,
}
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
if commandOutput, err = llmHoneypotInstance.ExecuteModel(sess.RawCommand()); err != nil {
log.Errorf("error ExecuteModel: %s, %s", sess.RawCommand(), err.Error())
commandOutput = "command not found"
}
}
sess.Write(append([]byte(commandOutput), '\n'))
tr.TraceEvent(tracer.Event{
Msg: "New SSH Raw Command Session",
Protocol: tracer.SSH.String(),
RemoteAddr: sess.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
Status: tracer.Start.String(),
ID: uuidSession.String(),
Environ: strings.Join(sess.Environ(), ","),
User: sess.User(),
Description: servConf.Description,
Command: sess.RawCommand(),
CommandOutput: commandOutput,
})
var histories []plugins.Message
if sshStrategy.Sessions.HasKey(sessionKey) {
histories = sshStrategy.Sessions.Query(sessionKey)
}
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.Append(sessionKey, histories...)
tr.TraceEvent(tracer.Event{
Msg: "End SSH Raw Command Session",
Status: tracer.End.String(),
ID: uuidSession.String(),
})
return
}
}
}
tr.TraceEvent(tracer.Event{
Msg: "New SSH Terminal Session",
Protocol: tracer.SSH.String(),
RemoteAddr: sess.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
Status: tracer.Start.String(),
ID: uuidSession.String(),
Environ: strings.Join(sess.Environ(), ","),
User: sess.User(),
Description: servConf.Description,
})
terminal := term.NewTerminal(sess, buildPrompt(sess.User(), servConf.ServerName))
var histories []plugins.Message
if sshStrategy.Sessions.HasKey(sessionKey) {
histories = sshStrategy.Sessions.Query(sessionKey)
}
for {
commandInput, err := terminal.ReadLine()
if err != nil {
break
}
if commandInput == "exit" {
break
}
for _, command := range servConf.Commands {
matched, err := regexp.MatchString(command.Regex, commandInput)
if err != nil {
log.Errorf("error regex: %s, %s", command.Regex, err.Error())
continue
}
if matched {
commandOutput := command.Handler
if command.Plugin == plugins.LLMPluginName {
llmProvider, err := plugins.FromStringToLLMProvider(servConf.Plugin.LLMProvider)
if err != nil {
log.Errorf("error: %s, fallback OpenAI", err.Error())
llmProvider = plugins.OpenAI
}
llmHoneypot := plugins.LLMHoneypot{
Histories: histories,
OpenAIKey: servConf.Plugin.OpenAISecretKey,
Protocol: tracer.SSH,
Host: servConf.Plugin.Host,
Model: servConf.Plugin.LLMModel,
Provider: llmProvider,
CustomPrompt: servConf.Plugin.Prompt,
}
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
if commandOutput, err = llmHoneypotInstance.ExecuteModel(commandInput); err != nil {
log.Errorf("error ExecuteModel: %s, %s", commandInput, err.Error())
commandOutput = "command not found"
}
}
histories = append(histories, plugins.Message{Role: plugins.USER.String(), Content: commandInput})
histories = append(histories, plugins.Message{Role: plugins.ASSISTANT.String(), Content: commandOutput})
terminal.Write(append([]byte(commandOutput), '\n'))
tr.TraceEvent(tracer.Event{
Msg: "New SSH Terminal Session",
RemoteAddr: sess.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
Status: tracer.Interaction.String(),
Command: commandInput,
CommandOutput: commandOutput,
ID: uuidSession.String(),
Protocol: tracer.SSH.String(),
Description: servConf.Description,
})
break
}
}
}
// Add all history events for the terminal session to the store.
// This is done at the end of the session to avoid excess lock operations.
sshStrategy.Sessions.Append(sessionKey, histories...)
tr.TraceEvent(tracer.Event{
Msg: "End SSH Session",
Status: tracer.End.String(),
ID: uuidSession.String(),
})
},
PasswordHandler: func(ctx ssh.Context, password string) bool {
host, port, _ := net.SplitHostPort(ctx.RemoteAddr().String())
tr.TraceEvent(tracer.Event{
Msg: "New SSH Login Attempt",
Protocol: tracer.SSH.String(),
Status: tracer.Stateless.String(),
User: ctx.User(),
Password: password,
Client: ctx.ClientVersion(),
RemoteAddr: ctx.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
ID: uuid.New().String(),
Description: servConf.Description,
})
matched, err := regexp.MatchString(servConf.PasswordRegex, password)
if err != nil {
log.Errorf("error regex: %s, %s", servConf.PasswordRegex, err.Error())
return false
}
return matched
},
}
err := server.ListenAndServe()
if err != nil {
log.Errorf("error during init SSH Protocol: %s", err.Error())
}
}()
log.WithFields(log.Fields{
"port": servConf.Address,
"commands": len(servConf.Commands),
}).Infof("GetInstance service %s", servConf.Protocol)
return nil
}
func buildPrompt(user string, serverName string) string {
return fmt.Sprintf("%s@%s:~$ ", user, serverName)
}

View File

@ -1,12 +1,13 @@
package strategies
package TCP
import (
"fmt"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
"net"
"time"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/tracer"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
@ -14,8 +15,8 @@ import (
type TCPStrategy struct {
}
func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
listen, err := net.Listen("tcp", beelzebubServiceConfiguration.Address)
func (tcpStrategy *TCPStrategy) Init(servConf parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
listen, err := net.Listen("tcp", servConf.Address)
if err != nil {
log.Errorf("Error during init TCP Protocol: %s", err.Error())
return err
@ -25,8 +26,8 @@ func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.Beelze
for {
if conn, err := listen.Accept(); err == nil {
go func() {
conn.SetDeadline(time.Now().Add(time.Duration(beelzebubServiceConfiguration.DeadlineTimeoutSeconds) * time.Second))
conn.Write([]byte(fmt.Sprintf("%s\n", beelzebubServiceConfiguration.Banner)))
conn.SetDeadline(time.Now().Add(time.Duration(servConf.DeadlineTimeoutSeconds) * time.Second))
conn.Write(fmt.Appendf([]byte{}, "%s\n", servConf.Banner))
buffer := make([]byte, 1024)
command := ""
@ -46,7 +47,7 @@ func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.Beelze
SourceIp: host,
SourcePort: port,
ID: uuid.New().String(),
Description: beelzebubServiceConfiguration.Description,
Description: servConf.Description,
})
conn.Close()
}()
@ -55,8 +56,8 @@ func (tcpStrategy *TCPStrategy) Init(beelzebubServiceConfiguration parser.Beelze
}()
log.WithFields(log.Fields{
"port": beelzebubServiceConfiguration.Address,
"banner": beelzebubServiceConfiguration.Banner,
}).Infof("Init service %s", beelzebubServiceConfiguration.Protocol)
"port": servConf.Address,
"banner": servConf.Banner,
}).Infof("Init service %s", servConf.Protocol)
return nil
}

View File

@ -1,149 +0,0 @@
package strategies
import (
"fmt"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/plugins"
"github.com/mariocandela/beelzebub/v3/tracer"
"io"
"net"
"net/http"
"regexp"
"strings"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
type HTTPStrategy struct {
beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration
}
func (httpStrategy HTTPStrategy) Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
httpStrategy.beelzebubServiceConfiguration = beelzebubServiceConfiguration
serverMux := http.NewServeMux()
serverMux.HandleFunc("/", func(responseWriter http.ResponseWriter, request *http.Request) {
traceRequest(request, tr, beelzebubServiceConfiguration.Description)
for _, command := range httpStrategy.beelzebubServiceConfiguration.Commands {
matched, err := regexp.MatchString(command.Regex, request.RequestURI)
if err != nil {
log.Errorf("Error regex: %s, %s", command.Regex, err.Error())
continue
}
if matched {
responseHTTPBody := command.Handler
if command.Plugin == plugins.LLMPluginName {
llmModel, err := plugins.FromStringToLLMModel(beelzebubServiceConfiguration.Plugin.LLMModel)
if err != nil {
log.Errorf("Error fromString: %s", err.Error())
responseHTTPBody = "404 Not Found!"
}
llmHoneypot := plugins.LLMHoneypot{
Histories: make([]plugins.Message, 0),
OpenAIKey: beelzebubServiceConfiguration.Plugin.OpenAISecretKey,
Protocol: tracer.HTTP,
Host: beelzebubServiceConfiguration.Plugin.Host,
Model: llmModel,
}
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
command := fmt.Sprintf("%s %s", request.Method, request.RequestURI)
if completions, err := llmHoneypotInstance.ExecuteModel(command); err != nil {
log.Errorf("Error ExecuteModel: %s, %s", command, err.Error())
responseHTTPBody = "404 Not Found!"
} else {
responseHTTPBody = completions
}
}
setResponseHeaders(responseWriter, command.Headers, command.StatusCode)
fmt.Fprintf(responseWriter, responseHTTPBody)
break
}
}
})
go func() {
err := http.ListenAndServe(httpStrategy.beelzebubServiceConfiguration.Address, serverMux)
if err != nil {
log.Errorf("Error during init HTTP Protocol: %s", err.Error())
return
}
}()
log.WithFields(log.Fields{
"port": beelzebubServiceConfiguration.Address,
"commands": len(beelzebubServiceConfiguration.Commands),
}).Infof("Init service: %s", beelzebubServiceConfiguration.Description)
return nil
}
func traceRequest(request *http.Request, tr tracer.Tracer, HoneypotDescription string) {
bodyBytes, err := io.ReadAll(request.Body)
body := ""
if err == nil {
body = string(bodyBytes)
}
host, port, _ := net.SplitHostPort(request.RemoteAddr)
tr.TraceEvent(tracer.Event{
Msg: "HTTP New request",
RequestURI: request.RequestURI,
Protocol: tracer.HTTP.String(),
HTTPMethod: request.Method,
Body: body,
HostHTTPRequest: request.Host,
UserAgent: request.UserAgent(),
Cookies: mapCookiesToString(request.Cookies()),
Headers: mapHeaderToString(request.Header),
Status: tracer.Stateless.String(),
RemoteAddr: request.RemoteAddr,
SourceIp: host,
SourcePort: port,
ID: uuid.New().String(),
Description: HoneypotDescription,
})
}
func mapHeaderToString(headers http.Header) string {
headersString := ""
for key := range headers {
for _, values := range headers[key] {
headersString += fmt.Sprintf("[Key: %s, values: %s],", key, values)
}
}
return headersString
}
func mapCookiesToString(cookies []*http.Cookie) string {
cookiesString := ""
for _, cookie := range cookies {
cookiesString += cookie.String()
}
return cookiesString
}
func setResponseHeaders(responseWriter http.ResponseWriter, headers []string, statusCode int) {
for _, headerStr := range headers {
keyValue := strings.Split(headerStr, ":")
if len(keyValue) > 1 {
responseWriter.Header().Add(keyValue[0], keyValue[1])
}
}
// http.StatusText(statusCode): empty string if the code is unknown.
if len(http.StatusText(statusCode)) > 0 {
responseWriter.WriteHeader(statusCode)
}
}

View File

@ -1,160 +0,0 @@
package strategies
import (
"fmt"
"github.com/mariocandela/beelzebub/v3/parser"
"github.com/mariocandela/beelzebub/v3/plugins"
"github.com/mariocandela/beelzebub/v3/tracer"
"net"
"regexp"
"strings"
"time"
"github.com/gliderlabs/ssh"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh/terminal"
)
type SSHStrategy struct {
}
func (sshStrategy *SSHStrategy) Init(beelzebubServiceConfiguration parser.BeelzebubServiceConfiguration, tr tracer.Tracer) error {
go func() {
server := &ssh.Server{
Addr: beelzebubServiceConfiguration.Address,
MaxTimeout: time.Duration(beelzebubServiceConfiguration.DeadlineTimeoutSeconds) * time.Second,
IdleTimeout: time.Duration(beelzebubServiceConfiguration.DeadlineTimeoutSeconds) * time.Second,
Version: beelzebubServiceConfiguration.ServerVersion,
Handler: func(sess ssh.Session) {
uuidSession := uuid.New()
host, port, _ := net.SplitHostPort(sess.RemoteAddr().String())
tr.TraceEvent(tracer.Event{
Msg: "New SSH Session",
Protocol: tracer.SSH.String(),
RemoteAddr: sess.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
Status: tracer.Start.String(),
ID: uuidSession.String(),
Environ: strings.Join(sess.Environ(), ","),
User: sess.User(),
Description: beelzebubServiceConfiguration.Description,
Command: sess.RawCommand(),
})
term := terminal.NewTerminal(sess, buildPrompt(sess.User(), beelzebubServiceConfiguration.ServerName))
var histories []plugins.Message
for {
commandInput, err := term.ReadLine()
if err != nil {
break
}
if commandInput == "exit" {
break
}
for _, command := range beelzebubServiceConfiguration.Commands {
matched, err := regexp.MatchString(command.Regex, commandInput)
if err != nil {
log.Errorf("Error regex: %s, %s", command.Regex, err.Error())
continue
}
if matched {
commandOutput := command.Handler
if command.Plugin == plugins.LLMPluginName {
llmModel, err := plugins.FromStringToLLMModel(beelzebubServiceConfiguration.Plugin.LLMModel)
if err != nil {
log.Errorf("Error fromString: %s", err.Error())
commandOutput = "command not found"
}
llmHoneypot := plugins.LLMHoneypot{
Histories: histories,
OpenAIKey: beelzebubServiceConfiguration.Plugin.OpenAISecretKey,
Protocol: tracer.SSH,
Host: beelzebubServiceConfiguration.Plugin.Host,
Model: llmModel,
}
llmHoneypotInstance := plugins.InitLLMHoneypot(llmHoneypot)
if commandOutput, err = llmHoneypotInstance.ExecuteModel(commandInput); err != nil {
log.Errorf("Error ExecuteModel: %s, %s", commandInput, err.Error())
commandOutput = "command not found"
}
}
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'))
tr.TraceEvent(tracer.Event{
Msg: "New SSH Terminal Session",
RemoteAddr: sess.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
Status: tracer.Interaction.String(),
Command: commandInput,
CommandOutput: commandOutput,
ID: uuidSession.String(),
Protocol: tracer.SSH.String(),
Description: beelzebubServiceConfiguration.Description,
})
break
}
}
}
tr.TraceEvent(tracer.Event{
Msg: "End SSH Session",
Status: tracer.End.String(),
ID: uuidSession.String(),
})
},
PasswordHandler: func(ctx ssh.Context, password string) bool {
host, port, _ := net.SplitHostPort(ctx.RemoteAddr().String())
tr.TraceEvent(tracer.Event{
Msg: "New SSH attempt",
Protocol: tracer.SSH.String(),
Status: tracer.Stateless.String(),
User: ctx.User(),
Password: password,
Client: ctx.ClientVersion(),
RemoteAddr: ctx.RemoteAddr().String(),
SourceIp: host,
SourcePort: port,
ID: uuid.New().String(),
Description: beelzebubServiceConfiguration.Description,
})
matched, err := regexp.MatchString(beelzebubServiceConfiguration.PasswordRegex, password)
if err != nil {
log.Errorf("Error regex: %s, %s", beelzebubServiceConfiguration.PasswordRegex, err.Error())
return false
}
return matched
},
}
err := server.ListenAndServe()
if err != nil {
log.Errorf("Error during init SSH Protocol: %s", err.Error())
}
}()
log.WithFields(log.Fields{
"port": beelzebubServiceConfiguration.Address,
"commands": len(beelzebubServiceConfiguration.Commands),
}).Infof("GetInstance service %s", beelzebubServiceConfiguration.Protocol)
return nil
}
func buildPrompt(user string, serverName string) string {
return fmt.Sprintf("%s@%s:~$ ", user, serverName)
}

View File

@ -2,10 +2,11 @@
package tracer
import (
log "github.com/sirupsen/logrus"
"sync"
"time"
log "github.com/sirupsen/logrus"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
@ -26,7 +27,7 @@ type Event struct {
User string
Password string
Client string
Headers string
Headers map[string][]string
Cookies string
UserAgent string
HostHTTPRequest string
@ -36,6 +37,7 @@ type Event struct {
Description string
SourceIp string
SourcePort string
TLSServerName string
}
type (