SSH honeypot, deployed in the wild, collecting and sharing data

Creating an SSH Session

18 Oct 2013 • 5 min read

laptop connectedLast week I ended by saying I needed to adjust my server.c code to use a secure socket in order to create an SSH session

However, having researched how to create a secure socket in C (Stack overflow: Turn a simple Socket into an SSL Socket), and then spending an entire afternoon implementing the code, it didn't work.

Running a server on a secure socket isn't enough to initiate an SSH session. With the server running I tried using an SSH client to connect - nothing happened.

So I looked at the RFC (Wikipedia page on RFC) for SSH. The main SSH RFC protocol (RFC 4251), Section 1: Introduction, explains how there are three major components to the SSH protocol:

  • The Transport Layer Protocol [SSH-TRANS] (RFC 4253)
  • The User Authentication Protocol [SSH-USERAUTH] (RFC 4252)
  • The Connection Protocol [SSH-CONNECT] (RFC 4254)

I read through the details of the SSH_TRANS protocol. In particular Section 4: Connection Setup.

Having read through the protocol details I now understood that an SSH session in initiated over a standard TCP/IP connection. The first communication that's sent between client and server is the identification string:

SSH-protoversion-softwareversion SP comments CR LF

Now I'm starting to gain an understanding of how the SSH protocol works I'm also realising just how much work would be involved in setting up my own SSH session.

At this point I emailed my project supervisor and he suggested I looked for a library that already implements the SSH protocol.

There are various SSH libraries out there (openssh, libssh2, libssh), but only libssh supports implementing an SSH server or more commonly known as an SSH daemon (sshd for short) - see Wikipedia page on what a daemon is.

libssh

So, at this point I've found a library which should allow implementation of an sshd. I just need to find a good example of how to implement it.

While browsing around the libssh source code on GitHub I found an example of an sshd: samplesshd.c.

The above example of an sshd has really helped me to understand how to create an SSH session in C. So the final task is for me to write my own C code which will allow an SSH client to connect.

At this early stage of the honeypot project, to keep things simple, I just want to log all attempted username and passwords. I don't want to actually authorise anyone just yet, that comes later.

I haven't had time yet this week to start writing the code for the basic SSH honeypot. However, thanks to my project supervisor for pointing me in the right direction, I have found an excellent example of what I'd like to achieve in order to get the ball rolling with this project.

While browsing through existing projects at GitHub, we stumbled across sshpot by Pete Morris.

This is the example code I've been looking for: (source: sshpot by Peter Morris)

#include "config.h"
#include "auth.h"

#include <libssh/libssh.h>
#include <libssh/server.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <getopt.h>
#include <errno.h>
#include <sys/wait.h>

#define MINPORT 0
#define MAXPORT 65535

/* Global so they can be cleaned up at SIGINT. */
static ssh_session session;
static ssh_bind sshbind;


/* Print usage information to `stream', exit with `exit_code'. */
static void usage(FILE *stream, int exit_code) {
    fprintf(stream, "Usage: sshpot [-h] [-p <port>]\n");
    fprintf(stream,
            "   -h  --help          Display this usage information.\n"
            "   -p  --port <port>   Port to listen on; defaults to 22.\n");
    exit(exit_code);
}


/* Return the c-string `p' as an int if it is a valid port 
 * in the range of MINPORT - MAXPORT, or -1 if invalid. */
static int valid_port(char *p) {
    int port;
    char *endptr;

    port = strtol(p, &endptr, 10);
    if (port >= MINPORT && port <= MAXPORT && !*endptr && errno == 0) 
        return port;

    return -1;
}


/* Signal handler for cleaning up after children. We want to do cleanup
 * at SIGCHILD instead of waiting in main so we can accept multiple
 * simultaneous connections. */
static int cleanup(void) {
    int status;
    int pid;
    pid_t wait3(int *statusp, int options, struct rusage *rusage);

    while ((pid=wait3(&status, WNOHANG, NULL)) > 0) {
        if (DEBUG) { printf("process %d reaped\n", pid); }
    }

    /* Re-install myself for the next child. */
    signal(SIGCHLD, (void (*)())cleanup);

    return 0;
}


/* SIGINT handler. Cleanup the ssh* objects and exit. */
static void wrapup(void) {
    ssh_disconnect(session);
    ssh_bind_free(sshbind);
    ssh_finalize();
    exit(0);
}


int main(int argc, char *argv[]) {
    int port = DEFAULTPORT;

    /* Handle command line options. */
    int next_opt = 0;
    const char *short_opts = "hp:";
    const struct option long_opts[] = {
        { "help",   0, NULL, 'h' },
        { "port",   1, NULL, 'p' },
        { NULL,     0, NULL, 0   }
    };

    while (next_opt != -1) {
        next_opt = getopt_long(argc, argv, short_opts, long_opts, NULL);
        switch (next_opt) {
            case 'h':
                usage(stdout, 0);
                break;

            case 'p':
                if ((port = valid_port(optarg)) < 0) {
                    fprintf(stderr, "Port must range from %d - %d\n\n", MINPORT, MAXPORT);
                    usage(stderr, 1);
                }
                break;

            case '?':
                usage(stderr, 1);
                break;

            case -1:
                break;

            default:
                fprintf(stderr, "Fatal error, aborting...\n");
                exit(1);
        }
    }

    /* There shouldn't be any other parameters. */
    if (argv[optind]) {
        fprintf(stderr, "Invalid parameter `%s'\n\n", argv[optind]);
        usage(stderr, 1);
    }

    /* Install the signal handlers to cleanup after children and at exit. */
    signal(SIGCHLD, (void (*)())cleanup);
    signal(SIGINT, (void(*)())wrapup);

    /* Create and configure the ssh session. */
    session=ssh_new();
    sshbind=ssh_bind_new();
    ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, LISTENADDRESS);
    ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port);
    ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, "ssh-rsa");
    ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY,RSA_KEYFILE);

    /* Listen on `port' for connections. */
    if (ssh_bind_listen(sshbind) < 0) {
        printf("Error listening to socket: %s\n",ssh_get_error(sshbind));
        return -1;
    }
    if (DEBUG) { printf("Listening on port %d.\n", port); }

    /* Loop forever, waiting for and handling connection attempts. */
    while (1) {
        if (ssh_bind_accept(sshbind, session) == SSH_ERROR) {
            fprintf(stderr, "Error accepting a connection: `%s'.\n",ssh_get_error(sshbind));
            return -1;
        }
        if (DEBUG) { printf("Accepted a connection.\n"); }

        switch (fork())  {
            case -1:
                fprintf(stderr,"Fork returned error: `%d'.\n",-1);
                exit(-1);

            case 0:
                exit(handle_auth(session));

            default:
                break;
        }
    }

    return 0;
}

So, this week I'll be attempting to implement my own version of sshpot and hopefully I can start logging some SSH login attempts on my server.

Image credit: "Connected to the World" by Stuart McKinnon, flickr.com/photos/stuballscramble/2941702825/

About the author

Simon BellSimon Bell is an award-winning Cyber Security Researcher, Software Engineer, and Web Security Specialist. Simon's research papers have been published internationally, and his findings have featured in Ars Technica, The Hacker News, PC World, among others. He founded Secure Honey, an open-source honeypot and threat intelligence project, in 2013. He has a PhD in Information Security and a BSc in Computer Science.

Follow Simon on Twitter: @SimonByte