oSSH is a dirty mix of honey and tar, delivered by a fake SSH server.

  • By Tox
  • Last update: Nov 27, 2022
  • Comments: 16

oSSH

... is a dirty mix of honey and tar, delivered by a fake SSH server.

Once running it will patiently wait for bots going after that sweet honey. When a bot tries to connect for the first time oSSH will check if username and password are already recorded. In that case it will kick the bot and wait for it to come back. If the bot has something new (either username or password), oSSH will gladly let the bot in and record the credentials. For bots that offer a username and a password that oSSH doesn't know, oSSH will roll dice to decide whether to let the bot in. This applies to new hosts, known hosts will always be let it.

Once inside, oSSH will add some tar to the mix. The bot can run commands and access a filesystem, but it will be painfully slow and all data returned will be fake. Meanwhile oSSH will record what the bot is doing, fingerprint it and store it in a file for manual inspection.

In addition to being painfully slow, the bot will connect to a pretty broken system where many commands result in errors reminiscent of failing hardware, bad configuration and alike.

How oSSH behaves can be configured via a YAML config file, a fake file system and command templates.

oSSH can also sync with other oSSH nodes to share hosts, user names, passwords and fingerprints.

Installation

The following assumes that you will use /etc/ossh as data directory. If you want something else you need to substitute accordingly and set path_data in the config.

First of all you need to become root (or run everything with sudo). Then get the repo and build the executable:

git clone https://github.com/Toxyl/ossh.git
cd ossh
CGO_ENABLED=0 go build
mv ossh /usr/local/bin/

Create directories and copy data from the repo:

mkdir -p /etc/ossh/{captures,commands,ffs}
cp -R commands/* /etc/ossh/commands/
cp -R ffs/* /etc/ossh/ffs/
cp ossh.service /etc/systemd/system/ossh.service
cp config.example.yaml /etc/ossh/config.yaml

Configure your instance:

nano /etc/ossh/config.yaml

And then enable the service:

systemctl enable ossh

Finally you can start trapping bots in that sweet tar:

service ossh start

You can monitor oSSHs operation using journalctl:

journalctl -u ossh -f --output cat

Configuration

Sluggishness

oSSH slows down responses to simulate a slow machine and to waste the bots time. This ratelimit can be defined in the config (ratelimit). Sometimes bots run commands with little output, so oSSH will add some penalty for every input character to slow things down a bit more for them. This can be defined in the config as well (input_delay).

Sync operations between nodes are exempt from the restrictions.

Command Responses

The commands section of the config allows you to customize oSSHs responses to commands. You can also create more elaborate responses using Golang templating, see the commands directory for examples.

Commands are evaluated in the following order:

rewriters (config)

These are pairs of regular expressions and replacements and will be executed in the given order on any user/bot input. Be aware that captures are made after rewriters have been applied.

my-little-pony

When this command is encountered oSSH will print a list with stats, such as amounts of collected user names and passwords. Consider it to be an admin-command which you can use to get stats. It cannot be configured and is included in this list for the sake of completeness.

exit (config)

If a command matches this list the connection will be terminated with a time-wasting response: ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@

simple (config)

These are pairs with a command to match and a response. Responses can use some template variables so one can, e.g., simulate the whoami command. Available variables are:

Variable Effect
{{ .User }} User name the attacker logged in with
{{ .IP }} IP of the attacker
{{ .IPLocal }} IP of oSSH
{{ .Port }} Port of the attacker
{{ .PortLocal }} Port of oSSH
{{ .HostName }} Host name of oSSH
{{ .InputRaw }} Raw input line that matched the command
{{ .Command }} Command that matched
{{ .Arguments }} Array with the arguments

permission_denied (config)

If a command matches this list oSSH will respond with:
{{ .Command }}: permission denied

disk_error (config)

If a command matches this list oSSH will respond with:
end_request: I/O error

command_not_found (config)

If a command matches this list oSSH will respond with:
{{ .Command }}: command not found

file_not_found (config)

If a command matches this list oSSH will respond with:
{{ .Command }}: No such file or directory

not_implemented (config)

If a command matches this list oSSH will respond with:
{{ .Command }}: Function not implemented

Command templates

If none of the above steps matched, oSSH will look in the commands directory (see further below) for a matching response template and parse that.

Nothing defined anywhere

If there is still no match oSSH will simply return:
{{ .Command }}: command not found

Syncing

If you run multiple instances of oSSH, you might want them to share their knowledge. To do so you can create credentials, store them in the config of each instance and then restart the instances. Once done they will regularly sync up with all nodes defined in their config. Assuming you have nodes running on 192.168.0.10, 192.168.0.20 and 192.168.0.30, the config could look like this:

Node 1 (192.168.0.10)

sync:
  interval: 1 # in minutes
  nodes:
    - host: 192.168.0.20
      port: 22
      user: 078e5067ec45f123a11b0845b5ddba3fea63e243118454ce07f85d7639eb4ec4
      password: 91ca82fc115605a4e21de7f9fc005b450ef6baa69fef56dbfbaf64375c21fd4f
    - host: 192.168.0.30
      port: 22
      user: 8fe36196f2c4bb5a63f390c3e2e1152a3ababcd3f41c1295b86f773c9c53c632
      password: ea5f6f80595c72c5e1ee8198a651f7584a3b293afbefcf228dd8b1659b6864c9

Node 2 (192.168.0.20)

sync:
  interval: 1 # in minutes
  nodes:
    - host: 192.168.0.10
      port: 22
      user: 42ba9f2b9b6e44a1b2744a243201d3147d174232de467899ab7e20df374101df
      password: 8dd88f838d80197836c59ccdd8fbde1feed27688a443132be5c0cb0e999b603f
    - host: 192.168.0.30
      port: 22
      user: 8fe36196f2c4bb5a63f390c3e2e1152a3ababcd3f41c1295b86f773c9c53c632
      password: ea5f6f80595c72c5e1ee8198a651f7584a3b293afbefcf228dd8b1659b6864c9

Node 3 (192.168.0.30)

sync:
  interval: 1 # in minutes
  nodes:
    - host: 192.168.0.10
      port: 22
      user: 42ba9f2b9b6e44a1b2744a243201d3147d174232de467899ab7e20df374101df
      password: 8dd88f838d80197836c59ccdd8fbde1feed27688a443132be5c0cb0e999b603f
    - host: 192.168.0.20
      port: 22
      user: 078e5067ec45f123a11b0845b5ddba3fea63e243118454ce07f85d7639eb4ec4
      password: 91ca82fc115605a4e21de7f9fc005b450ef6baa69fef56dbfbaf64375c21fd4f

Data directory

If you don't want to keep data in the default location (/etc/ossh), you can define an alternate location in the config like this:

path_data: /usr/share/ossh

Within that directory you will find bind a bunch of files with data collected by oSSH:

File Description
hosts.txt List of attacker IPs
users.txt List of user names
passwords.txt List of passwords
fingerprints.txt List of payload fingerprints

Captures directory

The subdirectory captures is the collection of payloads received from bots. Whenever a bot connects oSSH will record what it's doing and then save that recording as an ASCIICast v2 (you can use asciinema to play them back). Captures are saved per host, so you can, e.g., identify especially aggressive bots. The last part of the file name is the fingerprint of the sequence. Existing files will not be overwritten.

Fake File System (FFS)

The subdirectory ffs contains the files and directories bots can browse. You can modify the directory content at runtime to react to new payloads. For example: if bots commonly cat a specific file, you can create a very lengthy fake version of that file in the ffs directory. Next time a bot cats it, it will be waiting for a long time :D

Commands directory

The subdirectory commands contains templates for commands that need more elaborate behavior. Like the ffs directory it can be modified at runtime. These files are Golang templates, see this for more information in regards to the templating language.

Download

ossh.zip

Comments(16)

  • 1

    Web interface

    Admin commands like my-little-pony are a somewhat hacky solution and come with their own potential (security) issues. Maybe we should make a web interface instead where one can get basic stats of an instance and edit its config. Using websockets we could also stream the logs to the web interface.

  • 2

    OverlayFS stuck mounts

    Sometimes (in the order of several times a day to once every few days) the mount cleanup repeatedly fails with the error:

    cleanup worker, close overlay '/etc/ossh/ffs/sandboxes/62.173.60.106/merge-1652635240': unmount: invalid argument
    
  • 3

    Perceptual hashes instead of SHA1?

    I've captured, e.g., a bunch of payloads to attack HiveOS where the only difference between payloads is the password used but with SHA1 hashes I can't easily identify them without scanning each payload that I have. Would it be possible to use perceptual hashes instead to identify payloads? That might help to categorize payloads and detect relations between them.

  • 4

    Device or resource busy

    After connecting I ran cd (without args) which caused oSSH to crash. Seems like that left the overlayFS in an undefined state:

    rm: cannot remove 'ffs/sandboxes/127.0.0.1/merge-1651444220': Device or resource busy
    
  • 5

    fatal error: concurrent map writes

    fatal error: concurrent map writes
    goroutine 832520 [running]:
    runtime.throw({0x7bc07c?, 0xc004bd5890?})
            /home/tox/sdk/go1.18/src/runtime/panic.go:992 +0x71 fp=0xc0059bf7d0 sp=0xc0059bf7a0 pc=0x4339d1
    runtime.mapassign_faststr(0x7634a0, 0xc0001ac690, {0xc004bd5890, 0xe})
            /home/tox/sdk/go1.18/src/runtime/map_faststr.go:295 +0x38b fp=0xc0059bf838 sp=0xc0059bf7d0 pc=0x412e2b
    main.(*OSSHServer).authHandler(0xc0001a9b90, {0x84fb60, 0xc0028c04e0}, {0xc000f60908, 0x6})
            /home/tox/go/src/github.com/toxyl/ossh/server.go:467 +0x5c9 fp=0xc0059bf918 sp=0xc0059bf838 pc=0x724fa9
    main.(*OSSHServer).authHandler-fm({0x84fb60?, 0xc0028c04e0?}, {0xc000f60908?, 0xc003158080?})
            <autogenerated>:1 +0x45 fp=0xc0059bf950 sp=0xc0059bf918 pc=0x727ea5
    github.com/gliderlabs/ssh.(*Server).config.func1({0x84eb60?, 0xc003158080?}, {0xc000aef46c, 0x6, 0xc005917720?})
            /root/go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:138 +0x8d fp=0xc0059bf9a8 sp=0xc0059bf950 pc=0x70e60d
    golang.org/x/crypto/ssh.(*connection).serverAuthenticate(0xc003158080, 0xc006781380)
            /root/go/pkg/mod/golang.org/x/[email protected]/ssh/server.go:480 +0x51f fp=0xc0059bfd68 sp=0xc0059bf9a8 pc=0x6fe71f
    golang.org/x/crypto/ssh.(*connection).serverHandshake(0xc003158080, 0xc006781380)
            /root/go/pkg/mod/golang.org/x/[email protected]/ssh/server.go:278 +0x51e fp=0xc0059bfe38 sp=0xc0059bfd68 pc=0x6fd1fe
    golang.org/x/crypto/ssh.NewServerConn({0x84f658, 0xc003377880}, 0xc0067808f0)
            /root/go/pkg/mod/golang.org/x/[email protected]/ssh/server.go:206 +0x134 fp=0xc0059bfed0 sp=0xc0059bfe38 pc=0x6fc9b4
    github.com/gliderlabs/ssh.(*Server).HandleConn(0xc0000d8120, {0x84f708, 0xc004a2c920})
            /root/go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:281 +0x1ed fp=0xc0059bffb8 sp=0xc0059bfed0 pc=0x70f3ed
    github.com/gliderlabs/ssh.(*Server).Serve.func3()
            /root/go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:258 +0x2e fp=0xc0059bffe0 sp=0xc0059bffb8 pc=0x70f10e
    runtime.goexit()
            /home/tox/sdk/go1.18/src/runtime/asm_amd64.s:1571 +0x1 fp=0xc0059bffe8 sp=0xc0059bffe0 pc=0x4628a1
    created by github.com/gliderlabs/ssh.(*Server).Serve
            /root/go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:258 +0x245
    goroutine 1 [IO wait]:
    internal/poll.runtime_pollWait(0x7fd52f25f8e8, 0x72)
            /home/tox/sdk/go1.18/src/runtime/netpoll.go:302 +0x89
    internal/poll.(*pollDesc).wait(0xc000012100?, 0xc000022a00?, 0x0)
            /home/tox/sdk/go1.18/src/internal/poll/fd_poll_runtime.go:83 +0x32
    internal/poll.(*pollDesc).waitRead(...)
            /home/tox/sdk/go1.18/src/internal/poll/fd_poll_runtime.go:88
    internal/poll.(*FD).Accept(0xc000012100)
            /home/tox/sdk/go1.18/src/internal/poll/fd_unix.go:614 +0x22c
    net.(*netFD).accept(0xc000012100)
            /home/tox/sdk/go1.18/src/net/fd_unix.go:172 +0x35
    net.(*TCPListener).accept(0xc00000c090)
            /home/tox/sdk/go1.18/src/net/tcpsock_posix.go:139 +0x28
    net.(*TCPListener).Accept(0xc00000c090)
            /home/tox/sdk/go1.18/src/net/tcpsock.go:288 +0x3d
    github.com/gliderlabs/ssh.(*Server).Serve(0xc0000d8120, {0x84e1e8?, 0xc00000c090})
            /root/go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:237 +0x1bd
    github.com/gliderlabs/ssh.(*Server).ListenAndServe(0xc0000d8120)
            /root/go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:338 +0x6a
    main.(*OSSHServer).Start(0xc0001a9b90)
            /home/tox/go/src/github.com/toxyl/ossh/server.go:521 +0xea
    main.main()
            /home/tox/go/src/github.com/toxyl/ossh/main.go:8 +0x45
    goroutine 38 [sleep, 1 minutes]:
    time.Sleep(0xdf8475800)
            /home/tox/sdk/go1.18/src/runtime/time.go:194 +0x12e
    main.NewOSSHServer.func1()
            /home/tox/go/src/github.com/toxyl/ossh/server.go:561 +0x45
    created by main.NewOSSHServer
            /home/tox/go/src/github.com/toxyl/ossh/server.go:559 +0x1e5
    goroutine 826446 [chan receive, 34 minutes]:
    github.com/gliderlabs/ssh.(*Server).HandleConn(0xc0000d8120, {0x84f708, 0xc0002248a8})
            /root/go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:296 +0x3ea
    created by github.com/gliderlabs/ssh.(*Server).Serve
            /root/go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:258 +0x245
    goroutine 823899 [select, 51 minutes]:
    golang.org/x/crypto/ssh.(*handshakeTransport).kexLoop(0xc004f2b340)
            /root/go/pkg/mod/golang.org/x/[email protected]/ssh/handshake.go:268 +0x47d
    created by golang.org/x/crypto/ssh.newServerTransport
            /root/go/pkg/mod/golang.org/x/[email protected]/ssh/handshake.go:143 +0x136
    goroutine 827671 [sync.Cond.Wait, 28 minutes]:
    sync.runtime_notifyListWait(0xc0058ced90, 0x0)
            /home/tox/sdk/go1.18/src/runtime/sema.go:513 +0x13d
    sync.(*Cond).Wait(0x70f940?)
            /home/tox/sdk/go1.18/src/sync/cond.go:56 +0x8c
    golang.org/x/crypto/ssh.(*mux).Wait(0xc0015ddc70)
            /root/go/pkg/mod/golang.org/x/[email protected]/ssh/mux.go:110 +0x99
    golang.org/x/crypto/ssh.NewClient.func1()
            /root/go/pkg/mod/golang.org/x/[email protected]/ssh/client.go:62 +0x2a
    created by golang.org/x/crypto/ssh.NewClient
            /root/go/pkg/mod/golang.org/x/[email protected]/ssh/client.go:61 +0x176
    goroutine 831075 [IO wait, 4 minutes]:
    internal/poll.runtime_pollWait(0x7fd52f0e68b0, 0x72)
            /home/tox/sdk/go1.18/src/runtime/netpoll.go:302 +0x89
    internal/poll.(*pollDesc).wait(0xc003630880?, 0xc0018f6000?, 0x0)
            /home/tox/sdk/go1.18/src/internal/poll/fd_poll_runtime.go:83 +0x32
    internal/poll.(*pollDesc).waitRead(...)
            /home/tox/sdk/go1.18/src/internal/poll/fd_poll_runtime.go:88
    internal/poll.(*FD).Read(0xc003630880, {0xc0018f6000, 0x1000, 0x1000})
            /home/tox/sdk/go1.18/src/internal/poll/fd_unix.go:167 +0x25a
    net.(*netFD).Read(0xc003630880, {0xc0018f6000?, 0xa58000?, 0xa58000?})
            /home/tox/sdk/go1.18/src/net/fd_posix.go:55 +0x29
    net.(*conn).Read(0xc0036221e8, {0xc0018f6000?, 0x5bb489?, 0xc0038e3b00?})
            /home/tox/sdk/go1.18/src/net/net.go:183 +0x45
    github.com/gliderlabs/ssh.(*serverConn).Read(0xc003a05980, {0xc0018f6000, 0x1000, 0x1000})
            /root/go/pkg/mod/github.com/gliderlabs/[email protected]/conn.go:28 +0x52
    bufio.(*Reader).Read(0xc0001919e0, {0xc002d23230, 0x4, 0x40cade?})
            /home/tox/sdk/go1.18/src/bufio/bufio.go:236 +0x1b4
    io.ReadAtLeast({0x84c4c0, 0xc0001919e0}, {0xc002d23230, 0x4, 0x4}, 0x4)
            /home/tox/sdk/go1.18/src/io/io.go:331 +0x9a
    io.ReadFull(...)
    ...
    
  • 6

    Change capture format to asciicast v2

    see https://github.com/asciinema/asciinema/blob/master/doc/asciicast-v2.md

    example:

    {"version": 2, "width": 80, "height": 24, "timestamp": 1504467315, "title": "Demo", "env": {"TERM": "xterm-256color", "SHELL": "/bin/zsh"}}
    [0.248848, "o", "\u001b[1;31mHello \u001b[32mWorld!\u001b[0m\n"]
    [1.001376, "o", "That was ok\rThis is better."]
    [2.143733, "o", " "]
    [6.541828, "o", "Bye!"]
    
  • 7

    SCP support

    Once we have a dynamic file system(#4) we can add support for SCP file transfer which is a common method of getting files like scripts or executables on a server.

    I found this article which explains how the scp protocol works: https://goteleport.com/blog/scp-familiar-simple-insecure-slow/

    It seems to consist of 2 parts:

    1. we have to emulate the scp command, which if called with some undocumented flags should start capturing data sent over the connection.
    2. The data sent is a custom ASCII based protocol

    There are a number of SCP client libraries, but no server libraries as far as I can find, since everyone used openssh as the SSH server. So this one will require some custom protocol implementation

  • 8

    Add a 'dynamic' file system

    Currently we have a read-only file system, which works for simple bots which want to read a file and them bail, but this is not enough if we want to capture more advanced "attacks". It is supposedly very common to upload a binary or script and then to execute it instead of typing out each command one by one.

    In memory vs on disk

    On-disk RAM/memory is more limited than disk in terms of amounts of storage. By just actually storing files on the disk of the host, we have the most amount room. We get some aspects for free, like directories, path resolution, metadata.

    By doing this we risk breakout, if done incorrectly, bots or clever people can break out of the honeypot and read/write from the non-sandboxed region of the disk. We can limit this by running the honeypot as a user with limited access outside its directory and by using a chroot to limit the access of the process to this subdir to avoid escaping the sandbox (this would involve creating sub processes and IPC so the main process can still write the results as usual).

    In-memory In memory is more "work" since we have to emulate FS features like directories, metadata, permissions. We are limited in the amount of data we can store in such a FS so this likely only works for ephemeral data. However, it is also the easiest to secure.

    Shared vs isolated

    Shared A shared FS has a few advantages, namely that it is easy to implement, uses less storage and is more realistic. However, it makes it harder to track/isolate which files were changes by which session/client and it makes it so different bots can interfere with eachother.

    Isolated Having a FS per client makes it easy to see what files were created or altered by individual clients. The main downside it that you have to give each client its own FS which required more resources and is more complex

    Persistent vs Ephemeral

    Persistant Having a FS persist over multiple sessions is the most realistic way to emulate files. This assumes bots may do something, an later come back to do actions which depend on their earlier visits. The main downside is that tracking per-session changes is more difficult and that it takes more resources since we need to store FS'es for a certain amount of time

    Ephemeral After a session is done, we could just delete the files, or at least not make them accessible to the same client on subsequent visits.

    Proposals

    Isolated, Ephemeral, in-memory FS Give each session a in-memory FS, staring with a clone of the read-only FS, then recording all actions (creating, editing, deleting). Upon session close we save the FS to disk for analysis and discard the in-memory data.

    Shared, Persistent, on-disk One of the simpelest ways to solve the issue is to just designate some sub dir somewhere and have all clients share this directory. Everyone can use it, and we clean it up once a week to avoid permanently breaking bots. However, recording the contents of files, specifically which session is responsible for which change is difficult, would require a lot of code to make work.

    Isolated, Persistent, on-disk My personal favorite. We can use OverlayFS, a built-in kernel feature. We start with the base layer(the current read-only FS), each client will get its own layer on top of that. We can give each session its own layer which can be layered on top of layers created by the same client in earlier sessions. This makes it easy to see exactly what files changes in which session and compare them over time.

  • 9

    IP whitelist to prevent recording ourselves

    Currently one can get their own IP and "shell" sessions recorded when testing the honeytarpot. Which is obviously not a good idea if one wants to use the recorded host IPs asa blacklist. Combined with the sync feature this could lead the unintended side effects.

    An idea could be to introduce an IP whitelist section to the config, combined with safe credentials, similar to the sync feature itself. And then use that list in the addHost function (and other places) to prevent whitelisted IPs from being logged.

  • 10

    Failed to parse template string cat

    Failed to parse template string cat: template: cat.gotmpl:6:3: executing "cat" at <file $a1>: error calling file: runtime error: slice bounds out of range [:3] with length 1
    
  • 11

    ansible, metrics, better syncing, better session handling, better logging, bug fixes, ...

    • fixes #17
    • added Ansible playbook
    • added Metrics Server using Prometheus and Grafana dashboard
    • added SSH key capture
    • embedded web interface and command templates in executable
    • fixed bug where scp command fails on file names in single quotes
    • fixed data races, deadlocks, etc.pp.
    • fixed processing whitelisted IPs
    • improved command processing (removing env var prefixes such as "MYENV=1234 my-command", export & unset templates, removing unhandled command arguments)
    • introduced glog and gutils packages and adjusted code accordingly
    • reduced dependencies on global vars to improve gc
    • removed fsb, fbi, cia, nsa & ru command templates to avoid easy identification of oSSH instances
    • updated logging for more concice information
    • updated OverlayFS (init only when using go-implemented commands, single overlay per IP)
    • updated payload handling (group using locality sensitive hashing, storage location, no more saving of individual captures)
    • updated session handling (activity tracking, cleanup)
    • updated sync server (automatic cleanup, SyncCommands struct, syncing with non-cluster nodes)
    • updated UI Server (error logging using glog, bugfix for redirect feature)
    • updated web interface filtering mechanism
    • using the UI Server is now optional (disabled by default in the Ansible playbook, enabled in config example)
  • 12

    Getting this to work on OpenBSD 7.2 (snapshot)

    Right now the Golang version on OpenBSD struggles with

    ./fake_shell_overlayfs.go:290:15: undefined: unix.Mount ./fake_shell_overlayfs.go:306:45: undefined: syscall.MNT_DETACH

    This has to do with the overlayFS is what Docker uses, and Docker does not work (natively) on OpenBSD.

    To make Docker work on OpenBSD there has to be a VM/VMD running with Alpine that runs Docker. Performance may be flakey but i can always try to make that working.

    I'll keep you updated with that project.

  • 13

    Restructure oSSH using uber/fx

    I recently became aware of the uber/fx library, which has the goal of decreasing the work needed to init/bootstrap a complex application like oSSH. Perhaps it is not something we need or want for this scale of application, but it does make the code more modular, like the the services system of Symfony.

  • 14

    Improved security by process isolation

    There are a few security risks with the current architecture of the honeypot which I would like to address. We are playing with fire so to speak since we are inviting the internet to come onto our server and see what they will do. We have to make sure that we don't accidentally allow attackers to escape our sandbox and do actual harm to the host.

    We currently run the pot as the root user, which is not ideal for security. However we still want to run as root so we can use some features like mounting file systems to create OverlayFS's.

    My proposal is to split the application into two different kinds of processes where we have one 'main' thread which runs as root and can do everything on the system we need it to do. Then when a new SSH session is created, the main thread will setup the environment and spawn a child process(or we have one Go process which will spawn a goroutine). The child process will drop its privileges and change to a 'sandbox' user who can't do anything outside what we allow it to do. We can even place the process in a chroot jail so it can't even attempt to access files outside of its designated directory.

    Initially this child process also will not have the SSH connection. We can transfer the file descriptor of the SSH connection over an UNIX socket. We can use this same unix socket to allow the child process to communicate back to the main process to for example send back the captured input/output of the session so the main process can store is in a directory to which the child has no acccess.

    In the future we could even expand on this by placing the process in isolated namespaces of we so desire to for example block the process from using the network https://unix.stackexchange.com/questions/68956/block-network-access-of-a-process

  • 15

    Script execution support

    It would be nice if we could support script execution, bash / sh scripts are a common way to automate something, simply upload a script via SCP or download it via wget/curl and execute it.

    This is easier said than done, first step is to recognize when we call scripts. We should consider that a client is attempting to call a file on the FS like ./somescript.sh or via bash somescript.sh. In the first case we have to check for the shebang line #!/bin/bash or #!/bin/sh. We don't have to do this if the script is envoked directly by the bash command.

    Bash/sh scripts must be parsed and evaluated since it can contain commands(cat somefile), builtins(echo or alias) and even logic like for and if statements. Support for these features will likely be added gradually, but having some rudimentary support would be handy.

  • 16

    ToDo list: Missing templates for responses

    • [ ] unset
    • [ ] export
    • [x] nvidia-smi
    • [x] ip
    • [x] ifconfigcloud
    • [x] sed
    • [ ] python3
    • [ ] yum
    • [ ] crontab -r
    • [x] ./.senpai.loader
    • [x] ./23
    • [x] ./23s
    • [ ] ./awoo
    • [ ] ./lan
    • [ ] ./ok.sh
    • [x] ./sh
    • [x] ./x86_64
    • [ ] ./sc.sh
    • [ ] PATH=/dev/shm/:/tmp/:./:/var/tmp/:/root/:/etc/:$PATH
    • [ ] PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    • [x] #!/bin/sh
    • [ ] (really, an empty string, maybe a line break?)
    • [ ] >/var/log/lastlog
    • [ ] >/var/log/wtmp
    • [ ] >/var/log/btmp