2

I'm trying to create a program that demonstrates how issues like heartbleed can occur. This is what I have so far:

#include <stdio.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>

#define LPORT 5555
#define ACCEPT_QUEUE_SIZE 10

typedef struct sockaddr_in sockaddr_in;
typedef struct sockaddr sockaddr;

const char *login_msg = "=== Welcome to super secure admin control panel! ===\nPlease enter the password:";
const char *login_len_msg = "\nPlease enter the length of the password you entered: ";
const char *incorrect_password_msg = "\nACCESS DENIED USING PASSWORD: ";
const char *correct_password_msg = "\nACCESS GRANTED.\n";


int sockfd = 0;
sockaddr_in serv_addr;
void connection_handler(int connfd, const char *secret_password, char *client_address) {
    pid_t my_pid = fork();
    if (my_pid < 0) {
        fprintf(stderr, "[!] Failed to fork a new process for connection handler\n");
        exit(EXIT_FAILURE);
    }
    if (my_pid == 0) {
    if (1) {
        send(connfd, login_msg, strlen(login_msg), 0);
        char user_resp[51], secret_key[10];
        char user_resp_len_msg[6];
        int user_resp_len = 0;
        bzero(&user_resp, sizeof(user_resp));
        bzero(&user_resp_len_msg, sizeof(user_resp_len_msg));
        recv(connfd, &user_resp, 50, 0);
        fprintf(stderr, "[*] Client %s sent password %s\n", client_address, user_resp);
        send(connfd, login_len_msg, strlen(login_len_msg), 0);
        recv(connfd, &user_resp_len_msg, 5, 0);
        fprintf(stderr, "[*] Client %s sent password length string %s\n", client_address, user_resp_len_msg);
        sscanf(user_resp_len_msg, "%d\n", &user_resp_len);
        if (strncmp(secret_password, user_resp, strlen(secret_password)) == 0) {
            fprintf(stderr, "[+] %s successfully completed the challenge\n", client_address);
            send(connfd, correct_password_msg, strlen(correct_password_msg), 0);
        }
        else {
            send(connfd, incorrect_password_msg, strlen(incorrect_password_msg), 0);
            send(connfd, user_resp, user_resp_len, 0);
        }
        close(connfd);
        exit(EXIT_SUCCESS);
    }
}

void interrupt_handler(int signum) {
    fprintf(stderr, "[*] Caught interrupt signal, dying\n");
    close(sockfd);
    exit(EXIT_SUCCESS);
}

int main(int argc, const char * argv[]) {
    if (argc != 2) {
        fprintf(stderr, "[!] Usage: %s [server password]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    setpgrp();
    signal(SIGHUP, SIG_IGN);
    signal(SIGINT, interrupt_handler);

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        fprintf(stderr, "[!] Failed fo create socket\n");
        exit(EXIT_FAILURE);
    }

    int optval = 1;
    if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) != 0) {
        fprintf(stderr, "[!] Failed to set SO_REUSEADDR option on socket\n");
        exit(EXIT_FAILURE);
    }

    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(LPORT);

    if (bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) {
        fprintf(stderr, "[!] Failed to bind to port %d\n", LPORT);
        exit(EXIT_FAILURE);
    }

    if (listen(sockfd, ACCEPT_QUEUE_SIZE) != 0) {
        fprintf(stderr, "[!] Failed to listen on port %d with queue size %d\n", LPORT, ACCEPT_QUEUE_SIZE);
        exit(EXIT_FAILURE);
    }

    sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    char client_addr_str[INET_ADDRSTRLEN];
    while (1) {
        bzero(&client_addr, sizeof(client_addr));
        bzero(&client_addr_str, sizeof(client_addr_str));
        int connfd = accept(sockfd, (sockaddr*)&client_addr, &client_addr_len);
        inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_addr_str, client_addr_len);
        fprintf(stderr, "[*] Got new connection from %s\n", client_addr_str);
        connection_handler(connfd, argv[1], client_addr_str);
    }

    return 0;
}

It's run as ./program [password]. On OSX I noticed that argv got leaked, so I decided that's where the password should go. However, when I tested it on ubuntu this wasn't the case. Is there an easy way to make sure two buffers exist in adjacent parts of memory?

I recognize that predicting undefined behavior is very difficult and there may be no simple answer to the question.

735Tesla
  • 3,162
  • 4
  • 34
  • 57
  • 1
    Note that your messages would be better declared as `const char login_msg[] = "...";` — an array rather than a `char *`. All else apart, it saves space; with the array, just the length of the string is allocated, but with the pointer, you get a (4 or 8 byte) pointer and the string allocated. Further, you have no intention of changing what the pointers point at, but there is nothing in the code to stop you doing so, by accident or design. With arrays that are `const`, you can't (legitimately) change the data. – Jonathan Leffler Jan 26 '15 at 00:01
  • @JonathanLeffler you have a very good point about making it a constant, I'll also try changing it from a pointer to an actual array – 735Tesla Jan 26 '15 at 00:10

0 Answers0