Commit e103a3f4 authored by Kasra EdalatNejad's avatar Kasra EdalatNejad
Browse files

add description and documentation

parent 7a2af894
# KSSH
---
KSSH is an SSH server designed as a plugin for sharif honeypot.
KSSH acts as a honeypot and pretends to provide service. The main difference between this honeypot with conventional servers is that instead of sending received data to the shell or recipient port, it logs and drops the data. You can use usage and Security to change KSSH into a conventional server.
# Design
---
You can access SSH RFC in document/RFC and design documents in document/design. This project is designed based on Object Oriented concepts and implemented with C++. Available document:
* SSH message doc
* KSSH log detail
* Class diagram
* Activity diagram
* Sequence diagram
# Usage
---
## ssh_plugin
ssh_plugin is the server starting points. It reads option from the command line and starts listening on desired port. It creates an SSH object for every connection and let them handle each connection in a separate thread.
SSH can be used directly without using this.
-Warning: plugin_log uses sharif honeypot for logging. It should be changed/disabled for other environments.
## SSH
Provides an abstraction over an SSH connection. Impotant functions:
##### void setAppHostInfo(char *app_id, char *host_id)
This function **must** be called before start. SSH uses these two string to identifies each connection in the logs.
##### void *start(void *args)
args must contain an int* pointing to the socket description. Start gets a socket description and runs SSH protocol on the socket.
## Channel
Provides an abstraction over an SSH channel. KSSH creates a connection between the server and the user, but instead of sending data to the destination it drops the data without running. If you want to use KSSH as a conventional server you should change consumeData method.
##### void consumeData(SSH_String *data, int data_type = 0)
This method logs and deletes the data. You can change this behavior to piping data to the destination.
* data: received data
* data_type: specifies data stream. 0 -> standard output, 1 -> standard err
# Security
---
## Key Management
kssh randomly accepts passwords with probability of ACCEPT_RATIO based on user+password hash. "kasra" works as a master password for every user. You should edit Authentication::authPassword if you want to change this behavior.
## Private Key
There is a test key inside kssh/key for testing purpose. **YOU SHOULD CHANGE IT BEFORE USING KSSH**.
#include <exception>
using namespace std;
#include "KLog.h"
#include "SSH.h"
#include "ssl_util.h"
#include "Transport.h"
#include "utilities.h"
#include "SSH_Types.h"
#include "SSH_Messages.h"
#include "SSH_Constants.h"
#include "Authentication.h"
Authentication::Authentication(SSH* _ssh){
this->ssh = _ssh;
}
bool Authentication::authenticate() {
kinfo("Authentication handle message");
byte type;
BinaryMessage *message;
SSH_MessgaeReader *reader;
SSH_Messagewriter *writer;
SSH_String* ssh_service;
message = ssh->transport->receiveMessage();
reader = new SSH_MessgaeReader(message);
type = reader->readByte();
if(!type == SSH_Constants::SSH_MSG_SERVICE_REQUEST)
ssh->error("Invalid message type: Expected SERVICE_REQUEST recieved: " + to_string(type));
ssh_service = reader->readString();
string service = ssh_string2string(ssh_service, false);
delete ssh_service; ssh_service = NULL;
delete message; message = NULL;
kinfo("recieved service request for " + service);
// cerr << "in: " << service << " exp: " << AUTH_SERVICENAME << " res: " << (service == AUTH_SERVICENAME) << endl;
if(service != AUTH_SERVICENAME)
ssh->error("Invalid service: Expected authentication");
kwarn("--------- recieved SSH_MSG_SERVICE_REQUEST ----------------");
// return true;
kinfo("sending SSH_MSG_SERVICE_ACCEPT");
writer = new SSH_Messagewriter();
writer->writeByte(SSH_Constants::SSH_MSG_SERVICE_ACCEPT);
ssh_service = new SSH_String("ssh-userauth");
writer->writeString(ssh_service);
ssh->transport->sendMessage(writer);
delete ssh_service; ssh_service = NULL;
delete writer; writer = NULL;
kinfo("sended SSH_MSG_SERVICE_ACCEPT");
kspliter();
authRequest();
return true;
}
void Authentication::success() {
haveAuthenticated = true;
kinfo("send user authentication success");
SSH_Messagewriter *writer = new SSH_Messagewriter();
writer->writeByte(SSH_Constants::SSH_MSG_USERAUTH_SUCCESS);
ssh->transport->sendMessage(writer);
delete writer; writer = NULL;
ksuccess("******* Authentication complete *******");
}
void Authentication::failure(bool partial_success = false) {
kfailure("send user authentication failure partial: " + to_string(partial_success));
SSH_NameList* available_methods = string2namelist(SSH_Constants::SUPPORTED_AUTH_METHODS);
SSH_Messagewriter *writer = new SSH_Messagewriter();
writer->writeByte(SSH_Constants::SSH_MSG_USERAUTH_FAILURE);
writer->writeNamelist(available_methods);
writer->writeBoolean(partial_success);
ssh->transport->sendMessage(writer);
delete available_methods; available_methods = NULL;
delete writer; writer = NULL;
// kinfo("Authentication request Failed");
}
bool Authentication::authRequest() {
byte type;
BinaryMessage *message;
SSH_MessgaeReader *reader;
attempts = 0;
while(attempts < Authentication::ATTEMPTS_LIMIT){
bool invalid_req = false;
kwarn("authentication attempt");
kinfo("recieving SSH_MSG_USERAUTH_REQUEST");
message = ssh->transport->receiveMessage();
reader = new SSH_MessgaeReader(message);
type = reader->readByte();
if(!type == SSH_Constants::SSH_MSG_USERAUTH_REQUEST)
ssh->error("Invalid message type: Expected USERAUTH_REQUEST recieved: " + to_string(type));
SSH_String *ssh_username = reader->readString();
SSH_String *ssh_service = reader->readString();
SSH_String *auth_method = reader->readString();
string username = ssh_string2string(ssh_username, false);
string service = ssh_string2string(ssh_service, false);
string method = ssh_string2string(auth_method, false);
kinfo("user authentication request -> service: " + ssh_string2string(ssh_service, false) + " username: " + username + " method: " + method);
if(!serviceAvailable(service))
invalid_req = true;
if(invalid_req)
failure();
else if(method == AUTH_METHOD_NONE)
failure();
else if(method == AUTH_METHOD_PASS){
bool change_pass = reader->readBoolean();
SSH_String *ssh_pass = reader->readString();
SSH_String *ssh_pass_new = NULL;
string pass = ssh_string2string(ssh_pass, false);
if(change_pass)
ssh_pass_new = reader->readString();
KLog *log = new KLog();
log->addValue("username", username);
log->addValue("password", pass);
log->addValue("service", service);
log->addValue("change_pass", change_pass);
if(change_pass) log->addValue("new_pass", ssh_string2string(ssh_pass_new, false));
ssh->plugin_log("auth_password_log", log);
if(change_pass)
failure();
if(authPassword(username, pass))
success();
else
failure();
delete ssh_pass; ssh_pass = NULL;
delete ssh_pass_new; ssh_pass_new = NULL;
}
else if(method == AUTH_METHOD_PUBKEY){
bool have_sig = reader->readBoolean();
SSH_String *ssh_pk_alg = reader->readString();
SSH_String *ssh_pk_blob = reader->readString();
SSH_String *ssh_pk_sig;
if(have_sig)
ssh_pk_sig = reader->readString();
// rsa blob:
// string 'ssh-rsa'
// mpint e
// mpint n
// rsa signature:
// string 'ssh-rsa'
// string hash
// if have_sig == false && want to accept
// send [SSH_MSG_USERAUTH_PK_OK, pk_alg, pk_blob]
KLog *log = new KLog();
log->addValue("username", username);
log->addValue("service", service);
log->addValue("pk_alg", ssh_string2string(ssh_pk_alg, false));
log->addValue("pk_blob", ssh_string2string(ssh_pk_blob, false));
log->addValue("have_sig", have_sig);
ssh->plugin_log("auth_pubkey_log", log);
delete ssh_pk_alg; ssh_pk_alg = NULL;
delete ssh_pk_blob; ssh_pk_blob = NULL;
delete ssh_pk_sig; ssh_pk_sig = NULL;
failure();
}
else
ssh->error("Invalid authentication method");
delete ssh_username; ssh_username = NULL;
delete ssh_service; ssh_service = NULL;
delete auth_method; auth_method = NULL;
delete reader; reader = NULL;
delete message; message = NULL;
kinfo("recieved SSH_MSG_USERAUTH_REQUEST");
// kerror("Authentication Failed");// disconnect
if(haveAuthenticated)
return true;
}
return false;
}
bool Authentication::serviceAvailable(string service){
if(service == "ssh-connection")
return true;
}
bool Authentication::authPassword(string user, string pass){
kinfo("auth password user: " + user + " pass: " + pass);
if(pass == "kasra")
return true;
string data = user + pass + "01234567890123456789";
int user_data_len = user.size() + pass.size();
byte* digest = sha_digest(data.c_str(), min(20, user_data_len) );
int p = * ((int *) digest);
bool out = !(p%ACCEPT_RATIO);
delete digest; digest = NULL;
return out;
}
#include <exception>
using namespace std;
#include "KLog.h"
#include "SSH.h"
#include "ssl_util.h"
#include "Transport.h"
#include "utilities.h"
#include "SSH_Types.h"
#include "SSH_Messages.h"
#include "SSH_Constants.h"
#include "Authentication.h"
Authentication::Authentication(SSH* _ssh){
this->ssh = _ssh;
}
bool Authentication::authenticate() {
kinfo("Authentication handle message");
byte type;
BinaryMessage *message;
SSH_MessgaeReader *reader;
SSH_Messagewriter *writer;
SSH_String* ssh_service;
message = ssh->transport->receiveMessage();
reader = new SSH_MessgaeReader(message);
type = reader->readByte();
if(!type == SSH_Constants::SSH_MSG_SERVICE_REQUEST)
ssh->error("Invalid message type: Expected SERVICE_REQUEST recieved: " + to_string(type));
ssh_service = reader->readString();
string service = ssh_string2string(ssh_service, false);
delete ssh_service; ssh_service = NULL;
delete message; message = NULL;
kinfo("recieved service request for " + service);
// cerr << "in: " << service << " exp: " << AUTH_SERVICENAME << " res: " << (service == AUTH_SERVICENAME) << endl;
if(service != AUTH_SERVICENAME)
ssh->error("Invalid service: Expected authentication");
kwarn("--------- recieved SSH_MSG_SERVICE_REQUEST ----------------");
// return true;
kinfo("sending SSH_MSG_SERVICE_ACCEPT");
writer = new SSH_Messagewriter();
writer->writeByte(SSH_Constants::SSH_MSG_SERVICE_ACCEPT);
ssh_service = new SSH_String("ssh-userauth");
writer->writeString(ssh_service);
ssh->transport->sendMessage(writer);
delete ssh_service; ssh_service = NULL;
delete writer; writer = NULL;
kinfo("sended SSH_MSG_SERVICE_ACCEPT");
kspliter();
authRequest();
return true;
}
void Authentication::success() {
haveAuthenticated = true;
kinfo("send user authentication success");
SSH_Messagewriter *writer = new SSH_Messagewriter();
writer->writeByte(SSH_Constants::SSH_MSG_USERAUTH_SUCCESS);
ssh->transport->sendMessage(writer);
delete writer; writer = NULL;
ksuccess("******* Authentication complete *******");
}
void Authentication::failure(bool partial_success = false) {
kfailure("send user authentication failure partial: " + to_string(partial_success));
SSH_NameList* available_methods = string2namelist(SSH_Constants::SUPPORTED_AUTH_METHODS);
SSH_Messagewriter *writer = new SSH_Messagewriter();
writer->writeByte(SSH_Constants::SSH_MSG_USERAUTH_FAILURE);
writer->writeNamelist(available_methods);
writer->writeBoolean(partial_success);
ssh->transport->sendMessage(writer);
delete available_methods; available_methods = NULL;
delete writer; writer = NULL;
// kinfo("Authentication request Failed");
}
bool Authentication::authRequest() {
byte type;
BinaryMessage *message;
SSH_MessgaeReader *reader;
attempts = 0;
while(attempts < Authentication::ATTEMPTS_LIMIT){
bool invalid_req = false;
kwarn("authentication attempt");
kinfo("recieving SSH_MSG_USERAUTH_REQUEST");
message = ssh->transport->receiveMessage();
reader = new SSH_MessgaeReader(message);
type = reader->readByte();
if(!type == SSH_Constants::SSH_MSG_USERAUTH_REQUEST)
ssh->error("Invalid message type: Expected USERAUTH_REQUEST recieved: " + to_string(type));
SSH_String *ssh_username = reader->readString();
SSH_String *ssh_service = reader->readString();
SSH_String *auth_method = reader->readString();
string username = ssh_string2string(ssh_username, false);
string service = ssh_string2string(ssh_service, false);
string method = ssh_string2string(auth_method, false);
kinfo("user authentication request -> service: " + ssh_string2string(ssh_service, false) + " username: " + username + " method: " + method);
if(!serviceAvailable(service))
invalid_req = true;
if(invalid_req)
failure();
else if(method == AUTH_METHOD_NONE)
failure();
else if(method == AUTH_METHOD_PASS){
bool change_pass = reader->readBoolean();
SSH_String *ssh_pass = reader->readString();
SSH_String *ssh_pass_new = NULL;
string pass = ssh_string2string(ssh_pass, false);
if(change_pass)
ssh_pass_new = reader->readString();
KLog *log = new KLog();
log->addValue("username", username);
log->addValue("password", pass);
log->addValue("service", service);
log->addValue("change_pass", change_pass);
if(change_pass) log->addValue("new_pass", ssh_string2string(ssh_pass_new, false));
ssh->plugin_log("auth_password_log", log);
if(change_pass)
failure();
if(authPassword(username, pass))
success();
else
failure();
delete ssh_pass; ssh_pass = NULL;
delete ssh_pass_new; ssh_pass_new = NULL;
}
else if(method == AUTH_METHOD_PUBKEY){
bool have_sig = reader->readBoolean();
SSH_String *ssh_pk_alg = reader->readString();
SSH_String *ssh_pk_blob = reader->readString();
SSH_String *ssh_pk_sig;
if(have_sig)
ssh_pk_sig = reader->readString();
// rsa blob:
// string 'ssh-rsa'
// mpint e
// mpint n
// rsa signature:
// string 'ssh-rsa'
// string hash
// if have_sig == false && want to accept
// send [SSH_MSG_USERAUTH_PK_OK, pk_alg, pk_blob]
KLog *log = new KLog();
log->addValue("username", username);
log->addValue("service", service);
log->addValue("pk_alg", ssh_string2string(ssh_pk_alg, false));
log->addValue("pk_blob", ssh_string2string(ssh_pk_blob, false));
log->addValue("have_sig", have_sig);
ssh->plugin_log("auth_pubkey_log", log);
delete ssh_pk_alg; ssh_pk_alg = NULL;
delete ssh_pk_blob; ssh_pk_blob = NULL;
delete ssh_pk_sig; ssh_pk_sig = NULL;
failure();
}
else
ssh->error("Invalid authentication method");
delete ssh_username; ssh_username = NULL;
delete ssh_service; ssh_service = NULL;
delete auth_method; auth_method = NULL;
delete reader; reader = NULL;
delete message; message = NULL;
kinfo("recieved SSH_MSG_USERAUTH_REQUEST");
// kerror("Authentication Failed");// disconnect
if(haveAuthenticated)
return true;
}
return false;
}
bool Authentication::serviceAvailable(string service){
if(service == "ssh-connection")
return true;
}
bool Authentication::authPassword(string user, string pass){
kinfo("auth password user: " + user + " pass: " + pass);
if(pass == "kasra")
return true;
string data = user + pass + "01234567890123456789";
int user_data_len = user.size() + pass.size();
byte* digest = sha_digest(data.c_str(), min(20, user_data_len) );
int p = * ((int *) digest);
bool out = !(p%ACCEPT_RATIO);
delete digest; digest = NULL;
return out;
}
#include <exception>
#include <iostream>
using namespace std;
#ifndef __Authentication_h__
#define __Authentication_h__
// #include "SSH.h"
#include "SSH_Messages.h"
class SSH;
class SSH_Message;
class Authentication;
class Authentication {
public: static const int ATTEMPTS_LIMIT = 20;
public: static const int ACCEPT_RATIO = 1000;
public: static constexpr char* AUTH_SERVICENAME = "ssh-userauth";
public: static constexpr char* AUTH_METHOD_NONE = "none";
public: static constexpr char* AUTH_METHOD_PASS = "password";
public: static constexpr char* AUTH_METHOD_PUBKEY = "pubkey";
private: bool haveAuthenticated = false;
private: int attempts = 0;
public: SSH* ssh;
public: Authentication(SSH* _ssh);
public: bool authenticate();
private: void success();
private: void failure(bool partial_success =false);
private: bool authRequest();
private: bool authPassword(string user, string pass);
private: bool serviceAvailable(string service);
};
#endif
#include <exception>
#include <iostream>
using namespace std;
#ifndef __Authentication_h__
#define __Authentication_h__
// #include "SSH.h"
#include "SSH_Messages.h"
class SSH;
class SSH_Message;
class Authentication;
class Authentication {
public: static const int ATTEMPTS_LIMIT = 20;
public: static const int ACCEPT_RATIO = 1000;
public: static constexpr char* AUTH_SERVICENAME = "ssh-userauth";
public: static constexpr char* AUTH_METHOD_NONE = "none";
public: static constexpr char* AUTH_METHOD_PASS = "password";
public: static constexpr char* AUTH_METHOD_PUBKEY = "pubkey";
private: bool haveAuthenticated = false;
private: int attempts = 0;
public: SSH* ssh;
public: Authentication(SSH* _ssh);
public: bool authenticate();
private: void success();
private: void failure(bool partial_success =false);
private: bool authRequest();
private: bool authPassword(string user, string pass);