[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Key exchange fail


Hey there,

I recently read your docs on implementing a ssh connection and read one of
your example codes called ssh_server.c which works quite fine in my own
device. I've been trying to implement the C++ version of this code with
classes in play for days but the code just won't work. I am trying to
create a ssh server with threads (pthread) mainly and it fails in
ssh_server.cpp
in handle_session function at line 187.
KEY_FILE macro is defined in my cmake file. I've been able to get
connection but key exchange fails somehow.
I've been trying to solve the problem for few days now and being a beginner
in programming it is starting to get quite frustrating so I've decided to
reach out for help.

Best regards
// main.cpp

#include "ssh_server.hpp"

int main(int argc, char** argv) {
    std::unique_ptr<SSHServer> server;

    if (argc == 1) {
        server = std::make_unique<SSHServer>();
    }
    else if (argc == 5) {
        server = std::make_unique<SSHServer>(argv[1], atoi(argv[2]), argv[3], argv[4]);
    }
    else {
        std::cout << RED << "Usage: " << argv[0] << " [ip port username password]" << RESET << std::endl;
        return 1;
    }

    server->register_signal_handler();
    server->set_options();
    server->start();

    return 0;
}
#include "callbacks.hpp"

namespace cb {
    int auth_password(ssh_session session, const char* user, const char* password, void* userdata) {
        struct session_data_struct* sdata = static_cast<struct session_data_struct*>(userdata);

        if (strcmp(user, sdata->username->c_str()) == 0 && strcmp(password, sdata->password->c_str()) == 0) {
            std::cout << GREEN << "[auth_password]: Login successful" << RESET << std::endl;
            sdata->authenticated = 1;
            return SSH_AUTH_SUCCESS;
        }
        std::cout << RED << "[auth_password]: Failed login attempt: " << user << ":" << password << RESET << std::endl;
        sdata->auth_attempts++;
        return SSH_AUTH_DENIED;
    }
    ssh_channel channel_open(ssh_session session, void* userdata) {
        struct session_data_struct* sdata = static_cast<struct session_data_struct*>(userdata);

        std::cout << GREEN << "[channel_open]: Channel request has been made" << RESET << std::endl;
        sdata->channel = ssh_channel_new(session);
        return sdata->channel;
    }
    int pty_request(ssh_session session, ssh_channel channel, const char *term, int cols, int rows, int py, int px, void *userdata) {
        return 0;
    }
    int pty_resize(ssh_session session, ssh_channel channel, int cols, int rows, int py, int px, void *userdata) {
        return 0;
    }
    int shell_request(ssh_session session, ssh_channel channel, void *userdata) {
        return 0;
    }
    int exec_request(ssh_session session, ssh_channel channel,const char *command, void *userdata) {
        return 0;
    }
    int data_function(ssh_session session, ssh_channel channel, void *data, uint32_t len, int is_stderr, void *userdata) {
        return 0;
    }
}
// ssh_server.cpp

#include "ssh_server.hpp"

SSHServer* SSHServer::instance_ = nullptr;

SSHServer::SSHServer() {
    ip_ = DEFAULT_IP;
    port_ = DEFAULT_PORT;
    username_ = DEFAULT_USERNAME;
    password_ = DEFAULT_PASSWORD;
    shutdown_flag_ = false;
    instance_ = this;
}

SSHServer::SSHServer(const char* ip, uint32_t port, const char* username, const char* password) {
    ip_ = ip;
    port_ = port;
    username_ = username;
    password_ = password;
    shutdown_flag_ = false;
    instance_ = this;
}

SSHServer::~SSHServer() {
    cleanup();
}

void SSHServer::sigterm_handler(int signum) {
    if (signum == SIGINT) {
        instance_->shutdown_flag_ = true;
    }
}

void SSHServer::sigchld_handler(int signum) {
    (void) signum;
    while (waitpid(-1, NULL, WNOHANG) > 0) {
        // Reap all the zombie processes
    }
}

void SSHServer::register_signal_handler() {
    struct sigaction sa;

    // SIGINT handler
    sa.sa_handler = sigterm_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGINT, &sa, NULL);

    // SIGCHLD handler
    sa.sa_handler = sigchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
    sigaction(SIGCHLD, &sa, NULL);
}

void SSHServer::set_options() {
    int rc = 0;
    rc = ssh_init();
    if (rc < 0) {
        std::cerr << RED << "[SSHServer::start]: ssh_init failed" << RESET << std::endl;
        return;
    }
    std::cout << MAGENTA << "[SSHServer::set_options]: Setting the SSH server options" << RESET << std::endl;
    sshbind_ = ssh_bind_new();
    if (sshbind_ == NULL) {
        std::cerr << RED << "[SSHServer::set_options]: ssh_bind_new failed" << RESET << std::endl;
        return;
    }
    int verbosity_ = SSH_LOG_PACKET;
    ssh_bind_options_set(sshbind_, SSH_BIND_OPTIONS_LOG_VERBOSITY, &verbosity_);
    ssh_bind_options_set(sshbind_, SSH_BIND_OPTIONS_BINDADDR, ip_.c_str());
    ssh_bind_options_set(sshbind_, SSH_BIND_OPTIONS_BINDPORT, &port_);
    ssh_bind_options_set(sshbind_, SSH_BIND_OPTIONS_RSAKEY, KEY_FILE);
}

void SSHServer::start() {
    int rc;
    std::cout << MAGENTA << "[SSHServer::start]: Starting the SSH server" << RESET << std::endl;

    rc = ssh_bind_listen(sshbind_);
    if (rc < 0) {
        std::cerr << RED << "[SSHServer::start]: " << ssh_get_error(sshbind_) << RESET << std::endl;
        return;
    }

    while (shutdown_flag_ == false) {
        session_ = ssh_new();
        if (session_ == NULL) {
            std::cerr << RED << "[SSHServer::start]: Failed to allocate session" << RESET << std::endl;
            continue;
        }
        std::cout << MAGENTA << "[SSHServer::start]: Waiting for a new connection" << RESET << std::endl;
        // Block until new connection
        if (ssh_bind_accept(sshbind_, session_) != SSH_ERROR) {
            pthread_t t;
            rc = pthread_create(&t, NULL, session_thread_helper, this); // pass instance_ later on
            if (rc == 0) {
                pthread_detach(t);
                continue;
            }
            //threads_.push_back(std::move(t)); 
            
        } else {
            std::cerr << RED << "[SSHServer::start]: " << ssh_get_error(sshbind_) << RESET << std::endl;
        }
    }

    return;
}

void* SSHServer::session_thread_helper(void* arg) {
    SSHServer* server = static_cast<SSHServer*>(arg);
    server->session_thread(server->session_);
    return nullptr;
}

void SSHServer::session_thread(ssh_session __s) {
    ssh_session session = __s;
    ssh_event event;

    std::cout << MAGENTA << "[SSHServer::session_thread]: Starting a new session thread" << RESET << std::endl;
    event = ssh_event_new();
    if (event != NULL) {
        handle_session(event, session);
    } else {
        std::cerr << RED << "[SSHServer::session_thread]: Failed to create event" << RESET << std::endl;
    }
    
    if (ssh_is_connected(session)) ssh_disconnect(session);
}


void SSHServer::handle_session(ssh_event event, ssh_session session) {
    int n; // Used to give the client 5 seconds before terminating the session
    int rc = 0;    

    std::cout << BLUE << "[SSHServer::handle_session]: Handling a new session: " << session << RESET << std::endl;
    struct winsize wsize = {
        .ws_row = 0,
        .ws_col = 0,
        .ws_xpixel = 0,
        .ws_ypixel = 0
    };

    struct session_data_struct sdata = {
        .channel = NULL,
        .auth_attempts = 0,
        .authenticated = 0,
        .username = &username_,
        .password = &password_
    };

    struct channel_data_struct cdata = {
        .pid = 0,
        .pty_master = -1,
        .pty_slave = -1,
        .child_stdin = -1,
        .child_stdout = -1,
        .child_stderr = -1,
        .event = NULL,
        .winsize = &wsize
    };

    struct ssh_server_callbacks_struct server_cb;
    server_cb.userdata = &sdata;
    server_cb.auth_password_function = cb::auth_password;
    server_cb.channel_open_request_session_function = cb::channel_open;

    struct ssh_channel_callbacks_struct channel_cb;
    channel_cb.userdata = &cdata,
    channel_cb.channel_pty_request_function = cb::pty_request;
    channel_cb.channel_pty_window_change_function = cb::pty_resize;
    channel_cb.channel_shell_request_function = cb::shell_request;
    //channel_cb.channel_exec_request_function = cb::exec_request; // wont be allowed later
    channel_cb.channel_data_function = cb::data_function;

    ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD);

    ssh_callbacks_init(&server_cb);
    ssh_callbacks_init(&channel_cb);

    ssh_set_server_callbacks(session, &server_cb);

    // Key exchange
    if (ssh_handle_key_exchange(session) != SSH_OK) {
        std::cerr << RED << "[SSHServer::handle_session]: Key exchange failed: " << ssh_get_error(session) << RESET << std::endl;
        goto cleanup;
    }
    std::cout << GREEN << "[SSHServer::handle_session]: Successful key exchange" << RESET << std::endl;
    
    rc = ssh_event_add_session(event, session);
    if (rc != SSH_OK) {
        std::cerr << RED << "[SSHServer::handle_session]: Failed to add session to event" << RESET << std::endl;
        goto cleanup;
    }

    // Authentica and open a channel
    while (sdata.authenticated == 0 || sdata.channel == NULL) {
        /* To Do! Simulate a successful brute force attempt at random X attempts to make the attacker get inside*/
        if (sdata.auth_attempts >= 3) {
            std::cout << YELLOW << "[SSHServer::handle_session]: Auth attempts exceeded" << RESET << std::endl;
            goto cleanup;
        }
        if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
            std::cerr << RED << "[SSHServer::handle_session]: " << ssh_get_error(session) << RESET << std::endl;
            goto cleanup;
        }
    }
    std::cout << GREEN << "[SSHServer::handle_session]: Authenticated user" << RESET << std::endl;



cleanup:
    std::cout << YELLOW << "[SSHServer::handle_session]: Cleaning up the session" << RESET << std::endl;
    if(ssh_is_connected(session)) ssh_disconnect(session); std::cout << YELLOW << "[SSHServer::handle_session]: Disconnected the session" << RESET << std::endl;
    if(session != nullptr) ssh_free(session); session = nullptr; std::cout << YELLOW << "[SSHServer::handle_session]: Freed the session" << RESET << std::endl;
    if(event != nullptr) ssh_event_free(event); std::cout << YELLOW << "[SSHServer::handle_session]: Freed the event" << RESET << std::endl;
    if(cdata.event != nullptr) ssh_event_free(cdata.event); std::cout << YELLOW << "[SSHServer::handle_session]: Freed the sdata event" << RESET << std::endl;
    if(ssh_channel_is_open(sdata.channel)) ssh_channel_free(sdata.channel); std::cout << YELLOW << "[SSHServer::handle_session]: Freed the channel" << RESET << std::endl;
}

void SSHServer::cleanup() {
    std::cout << MAGENTA << "[SSHServer::cleanup]: Stopping the SSH server" << RESET << std::endl;
    if (sshbind_ != nullptr) ssh_bind_free(sshbind_); 
    if (ssh_is_connected) ssh_disconnect(session_);
    ssh_finalize();

    /*for (auto& t : threads_) {
        pthread_join(t, NULL);
    }*/

    return;
}
/*
This file has the appropriate callback functions and the data structures for the SSH server
*/

#ifndef CALLBACKS_HPP
#define CALLBACKS_HPP

#include <utmp.h>
#include <pty.h>
#include <libssh/callbacks.h>
#include <libssh/server.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <cstdlib>
#include <string>
#include <iostream>
#include <memory>
#include <atomic>
#include <vector>
#include "color_codes.hpp"

#define BUF_SIZE 10240
#define DEFAULT_PORT 2222
#define DEFAULT_IP "0.0.0.0"
#define DEFAULT_USERNAME "username"
#define DEFAULT_PASSWORD "password"
#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR)

struct session_data_struct {
    ssh_channel channel;
    int auth_attempts;
    int authenticated;
    std::string* username;
    std::string* password;
};

struct channel_data_struct {
    pid_t pid; // PID of the child process the channel will spawn
    socket_t pty_master;
    socket_t pty_slave; 
    /* For communicating with the child process */
    socket_t child_stdin; 
    socket_t child_stdout; 
    socket_t child_stderr;
    ssh_event event; // Event to poll the descriptors
    struct winsize* winsize; // Terminal size struct
};

namespace cb{
    int auth_password(ssh_session session, const char* user, const char* password, void* userdata);
    ssh_channel channel_open(ssh_session session, void* userdata);
    int pty_request(ssh_session session, ssh_channel channel, const char *term, int cols, int rows, int py, int px, void *userdata);
    int pty_resize(ssh_session session, ssh_channel channel, int cols, int rows, int py, int px, void *userdata);
    int shell_request(ssh_session session, ssh_channel channel, void *userdata);
    int data_function(ssh_session session, ssh_channel channel, void *data, uint32_t len, int is_stderr, void *userdata);
    int process_stdout(socket_t fd, int revents, void* userdata);
}
#endif // CALLBACKS_HPP
#ifndef COLOR_CODES_H
#define COLOR_CODES_H

#define RESET  "\033[0m"
#define RED "\033[31m[-] "
#define GREEN "\033[32m[+] "
#define YELLOW "\033[33m[!] "
#define BLUE "\033[34m"
#define MAGENTA "\033[35m"
#define CYAN "\033[36m"

#endif // COLOR_CODES_H
/* This is the base of the LatroWeb Honeypot */
/*
    * It uses libssh SSH as communication protocol
    * It simulates a realistic environment for the attacker
    * Works together with the audit Logger
    * Will enable the attacker to enter the system with random amount of tries making the brute force attack seem successful
*/

/*

Copyright 2025 Yusuf Saka

This file is part of the LatroWeb HoneyPot Project.
Feel free to copy this file, modify it in any way.
Used for educational purposes and research only.

*/

#ifndef SSH_SERVER_H
#define SSH_SERVER_H

#include "callbacks.hpp"

#define DEFAULT_PORT 2222
#define DEFAULT_IP "0.0.0.0"
#define DEFAULT_USERNAME "username"
#define DEFAULT_PASSWORD "password"
#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR)

class SSHServer {
public:
    SSHServer();
    SSHServer(const char* ip, uint32_t port, const char* username, const char* password);
    ~SSHServer();
    void set_options();
    void start();
    void start2();
    void cleanup();
    void handle_session(ssh_event event, ssh_session session);
    static void* session_thread_helper(void* arg);
    void session_thread(ssh_session __s);
    static void sigterm_handler(int signum);
    static void sigchld_handler(int signum);
    void register_signal_handler();
private:
    std::string ip_;
    uint32_t port_;
    std::string username_;
    std::string password_;
    ssh_bind sshbind_;
    ssh_session session_;
    std::atomic<bool> shutdown_flag_;
    static SSHServer* instance_;
    std::vector<pthread_t> threads_;
    int verbosity_;
};

#endif // SSH_SERVER_H

Follow-Ups:
Re: Key exchange failJakub Jelen <jjelen@xxxxxxxxxx>
Archive administrator: postmaster@lists.cynapses.org