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

You are now Authorised

15 Nov 2013 • 5 min read

door unlockedThe honeypot's been active for 26 days and it's received 15,680 login attempts. The most common password is "123456" which has been used 323 times and the top username is "root" which has been used 11,703 times.

Some interesting media coverage appeared in the news last week: a list of Adobe usernames and passwords was leaked and then analysed by security researchers (Analysis reveals popular Adobe passwords). The top password, used by 1.9 million Adobe users was "123456" which is also the top password being used by attackers on my honeypot.

I've also setup a Facebook and Twitter script which posts the days most commonly attempted usernames and passwords. You can like the project's Facebook page or follow the project on Twitter for daily attack stats.

In the last post I ended by saying I'd be moving onto phase two: authorisation. Phase two took a little longer to start since the university deadline for project interim reports was on the 5th November. This meant phase two implementation was delayed by about two weeks while I worked on the interim report.

Authorisation

Over the past week I've been working on implementing authorisation and emulating the shell command line interface.

The first step of phase two was to authorise clients that enter a correct username and password. Based on James Halliday's libssh example on GitHub (samplesshd-tty.c line 178), I was able to use the authenticate function to achieve this:

static int authenticate(ssh_session session, struct connection *c) {
    ssh_message message;

    do {
        message=ssh_message_get(session);
        if(!message)
            break;
        switch(ssh_message_type(message)){
            case SSH_REQUEST_AUTH:
                switch(ssh_message_subtype(message)){
                    case SSH_AUTH_METHOD_PASSWORD:
                        printf("User %s wants to auth with pass %s\n",
                               ssh_message_auth_user(message),
                               ssh_message_auth_password(message));
                        log_attempt(c,
                                ssh_message_auth_user(message),
                                ssh_message_auth_password(message));
                        if(auth_password(ssh_message_auth_user(message),
                           ssh_message_auth_password(message))){
                               ssh_message_auth_reply_success(message,0);
                               ssh_message_free(message);
                               return 1;
                           }
                        ssh_message_auth_set_methods(message,
                                                SSH_AUTH_METHOD_PASSWORD |
                                                SSH_AUTH_METHOD_INTERACTIVE);
                        // not authenticated, send default message
                        ssh_message_reply_default(message);
                        break;

                    case SSH_AUTH_METHOD_NONE:
                    default:
                        printf("User %s wants to auth with unknown auth %d\n",
                               ssh_message_auth_user(message),
                               ssh_message_subtype(message));
                        ssh_message_auth_set_methods(message,
                                                SSH_AUTH_METHOD_PASSWORD |
                                                SSH_AUTH_METHOD_INTERACTIVE);
                        ssh_message_reply_default(message);
                        break;
                }
                break;
            default:
                ssh_message_auth_set_methods(message,
                                                SSH_AUTH_METHOD_PASSWORD |
                                                SSH_AUTH_METHOD_INTERACTIVE);
                ssh_message_reply_default(message);
        }
        ssh_message_free(message);
    } while (ssh_get_status(session) != SSH_CLOSED ||
            ssh_get_status(session) != SSH_CLOSED_ERROR);
    return 0;
}

Which uses the auth_password function to check if the correct username and password has been provided:

static int auth_password(const char *user, const char *password){
    if(strcmp(user, "root"))
        return 0;
    if(strcmp(password, "123456"))
        return 0;
    return 1; // authenticated
}

As you can see in the above code I've set the username to "root" and and password to "123456" since this is the most common username and password combination that's been attempted on the honeypot so far.

Once the client has been authorised, the honeypot then sends the SSH_MSG_USERAUTH_SUCCESS (RFC 4252: Responses to Authentication Requests) message to the client, which basically informs the client they've successfully logged in.

Now the client has been authorised, it's time to move on to the next part: emulating the shell.

Shell emulation

Having been authorised, the client is now expecting to be presented with the shell command-line interface. Here's an example of how it looks when I SSH into my raspberry pi from my laptop running Ubuntu:

Raspberry pi SSH terminal screenshot

To get the ball rolling, I started by echoing back anything the client typed into the honeypot:

do{
    i=ssh_channel_read(chan,buf, 2048, 0);
    if(i>0) {
        ssh_channel_write(chan, buf, i);
        if (write(1,buf,i) < 0) {
            printf("error writing to buffer\n");
            return 1;
         }
    }
} while (i>0);

What this does is to display characters typed by the client onto the clients terminal window. However, emulating the shell turned out to be more of a challenge than I expected.

One of the main problems I encountered was how to handle the arrow keys. Since the above code simply echoes back all the clients input, if the client pressed the up arrow key this would result in their command-line cursor moving up on their terminal window. This doesn't sound like a major problem, but the cursor could be moved to anywhere on the terminal window by using the arrow keys, even to text preceding the ssh connection.

This problem turned out to be one of those week-long challenges that seemed like it would never end. I looked into various ways of emulating a shell, even searching around for terminal/shell protocols to prevent the command-line cursor from moving around the entire terminal screen.

In the end I looked at how Kippo and Kojoney tackle this problem. It turns out to be a simple solution: Kojoney just disables the arrow keys altogether (kojoney.py line 158). So, as a temporary fix to the problem of the movable cursor, I implemented code to not echo back arrow key presses.

The result is that, once authenticated, the client is now presented with this line:

root@ubuntu ~ $

Whereby the client can type away commands to their hearts content. At this stage all commands entered are logged but the commands won't actually execute.

Plan for next week

I'm still working on the stats page, which should include various graphs and charts to present the data collected from the honeypots so far.

I'm also aiming to setup a pure honeypot over the weekend which I'll run in parallel with this project. The aim of running the pure honeypot will be to collect more comprehensive data from attacks to compliment the project.

Image credit: "Door ajar" by Kate McDonald, flickr.com/photos/61556972@N05/5630551342

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