252 lines
7.2 KiB
C
252 lines
7.2 KiB
C
#include "..\pch.h"
|
|
|
|
/**
|
|
This module is a plain C class emulation. The POC written by decoder was in cpp and used some classes
|
|
in particular for the local negotiator.
|
|
See https://stackoverflow.com/questions/40992945/convert-a-cpp-class-cpp-file-into-a-c-structure-c-file
|
|
for how I emulated a class in pure C.
|
|
|
|
This class defines a base server object. Inheritance will be emulated in order to create a
|
|
Rogue WinRM service and a simple http server derived from this class.
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
Constructor of the Server class. Setup the addresses of class methods,
|
|
and initialize some arguments
|
|
|
|
@param Server* this Address of the instantiated object.
|
|
@param char* listen_address Address of IP (as string)
|
|
@param char* listen_port Address of port (as string)
|
|
@param BOOL debug TRUE or FALSE. Defined in main/dllmain
|
|
*/
|
|
void initService(Server* this, char* listen_address, char* listen_port)
|
|
{
|
|
ZeroMemory(this, sizeof(Server));
|
|
|
|
this->destruct = &destructService;
|
|
this->listenerStart = &startListener;
|
|
this->serverStop = &SocketError;
|
|
this->listen_address = listen_address;
|
|
this->listen_port = listen_port;
|
|
this->socket = INVALID_SOCKET;
|
|
this->socketInfos = NULL;
|
|
|
|
ZeroMemory(&this->hints, sizeof(this->hints));
|
|
this->hints.ai_family = AF_INET;
|
|
this->hints.ai_socktype = SOCK_STREAM;
|
|
this->hints.ai_protocol = IPPROTO_TCP;
|
|
this->hints.ai_flags = AI_PASSIVE;
|
|
|
|
this->listenerStart(this);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
Destructor of the Server class. Free allocated memory.
|
|
|
|
@param Server* this Address of the instantiated object.
|
|
*/
|
|
void destructService(Server* this)
|
|
{
|
|
/*for (this->allocationMapLen=10; this->allocationMapLen > 0; this->allocationMapLen--)
|
|
{
|
|
free(this->allocationMap[this->allocationMapLen]);
|
|
}*/
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Initialization of the server :
|
|
- Initialize Winsock
|
|
- Resolve the server address and port
|
|
- Create a SOCKET for connecting to server
|
|
- Setup the TCP listening socket
|
|
- Accept a client socket and put it as argument to make it available for derived servers.
|
|
- Clean the listen socket
|
|
|
|
@param Server* this Address of the instantiated object.
|
|
*/
|
|
static void startListener(Server* this)
|
|
{
|
|
int iResult = -1;
|
|
WSADATA wsaData;
|
|
SOCKET ListenSocket = INVALID_SOCKET;
|
|
struct addrinfo* result = NULL;
|
|
|
|
// Initialize Winsock
|
|
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
|
if (iResult != 0)
|
|
{
|
|
dprintf("[startListener] ERROR: WSAStartup failed with error: %d", iResult);
|
|
exit(-1);
|
|
}
|
|
dprintf("[startListener] SUCCESS: WSAStartup initialized");
|
|
|
|
// Resolve the server address and port
|
|
iResult = getaddrinfo(this->listen_address, this->listen_port, &this->hints, &this->socketInfos);
|
|
if (iResult != 0)
|
|
{
|
|
dprintf("[startListener] ERROR: getaddrinfo failed with error: %d\n", iResult);
|
|
WSACleanup();
|
|
exit(-1);
|
|
}
|
|
dprintf("[startListener] SUCCESS: getaddrinfo initialized. host:%s, port: %s", this->listen_address, this->listen_port);
|
|
|
|
// Create a SOCKET for connecting to server
|
|
ListenSocket = socket(this->socketInfos->ai_family, this->socketInfos->ai_socktype, this->socketInfos->ai_protocol);
|
|
if (ListenSocket == INVALID_SOCKET)
|
|
{
|
|
dprintf("[startListener] ERROR: socket creation failed with error: %ld\n", WSAGetLastError());
|
|
freeaddrinfo(this->socketInfos);
|
|
WSACleanup();
|
|
exit(-1);
|
|
}
|
|
dprintf("[startListener] SUCCESS: socket created.");
|
|
|
|
// Setup the TCP listening socket
|
|
iResult = bind(ListenSocket, this->socketInfos->ai_addr, (int)this->socketInfos->ai_addrlen);
|
|
if (iResult == SOCKET_ERROR)
|
|
{
|
|
if (strcmp(this->listen_port, "5985") == 0) { dprintf("[startListener] ERROR: WinRM already running on port 5985. Unexploitable!"); }
|
|
dprintf("[startListener] ERROR: bind failed with error: %d", WSAGetLastError());
|
|
freeaddrinfo(this->socketInfos);
|
|
this->serverStop(this, ListenSocket, "[startListener] ERROR: bind failed");
|
|
}
|
|
dprintf("[startListener] SUCCESS: socket bound.");
|
|
|
|
freeaddrinfo(this->socketInfos);
|
|
|
|
iResult = listen(ListenSocket, SOMAXCONN);
|
|
if (iResult == SOCKET_ERROR) { this->serverStop(this, ListenSocket, "[startListener] ERROR: Listen stage failed"); }
|
|
dprintf("[startListener] SUCCESS: socket is now listening for incoming connexions.");
|
|
|
|
// Accept a client socket
|
|
this->socket = accept(ListenSocket, NULL, NULL);
|
|
if (this->socket == INVALID_SOCKET) { this->serverStop(this, this->socket, "[startListener] ERROR: Accept stage failed"); }
|
|
dprintf("[startListener] SUCCESS: socket accept stage successful.");
|
|
|
|
// No longer need server socket
|
|
closesocket(ListenSocket);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
In case of failure, close properly the server:
|
|
- Shutdown this->socket
|
|
- Close this->socket
|
|
- Invoke the class destructor
|
|
|
|
@param Server* this Address of the instantiated object
|
|
@param SOCKET Socket Socket to close. If -1, then set it to this->socket
|
|
@param char* error_message Message to be displayed
|
|
*/
|
|
static void SocketError(Server* this, SOCKET Socket, char* error_message)
|
|
{
|
|
if (Socket == -1) { Socket = this->socket; }
|
|
dprintf("[SocketError] SOCKET ERROR: WSAGetLastError: %d", WSAGetLastError());
|
|
dprintf("%s", error_message);
|
|
shutdown(Socket, SD_SEND);
|
|
closesocket(Socket);
|
|
WSACleanup();
|
|
this->destruct(this);
|
|
exit(-1);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Print network packets received or to be send for debug purposes in tcpdump style.
|
|
This function is only for debug purpose. If DEBUGTRACE preprocessor constant is
|
|
not set, this function returns immediately without doing anything.
|
|
*/
|
|
void hexDump_if_debug_env(char* desc, void* addr, int len)
|
|
{
|
|
#ifdef DEBUGTRACE
|
|
int i;
|
|
int written_chars = 0;
|
|
unsigned char buff[17];
|
|
unsigned char temporary_buff[20] = { 0 };
|
|
unsigned char line[74] = { 0 };
|
|
unsigned char* pc = (unsigned char*)addr;
|
|
|
|
// Output description if given.
|
|
if (desc != NULL)
|
|
dprintf("[hexDump] %s:\n", desc);
|
|
|
|
if (len == 0)
|
|
{
|
|
dprintf("[hexDump] ERROR: data are zero length.");
|
|
return;
|
|
}
|
|
if (len < 0)
|
|
{
|
|
dprintf("[hexDump] ERROR: data are negative length.: %i", len);
|
|
return;
|
|
}
|
|
|
|
dprintf("[hexDump] Hexdump of packet:");
|
|
|
|
// Process every byte in the data.
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
// Multiple of 16 means new line (with line offset).
|
|
if ((i % 16) == 0)
|
|
{
|
|
// Just don't print ASCII for the zeroth line.
|
|
if (i != 0)
|
|
{
|
|
strcat_s(line, 74, " ");
|
|
strcat_s(line, 74, buff);
|
|
dprintf("[hexDump] %s", line);
|
|
ZeroMemory(line, 74);
|
|
}
|
|
|
|
// Put the offset in line buffer.
|
|
written_chars = sprintf_s(temporary_buff, 20, " %04x ", i);
|
|
strcat_s(line, 74, temporary_buff);
|
|
ZeroMemory(temporary_buff, 20);
|
|
}
|
|
|
|
// Now the hex code for the specific character.
|
|
written_chars = sprintf_s(temporary_buff, 20, " %02x", pc[i]);
|
|
strcat_s(line, 74, temporary_buff);
|
|
ZeroMemory(temporary_buff, 20);
|
|
|
|
// And store a printable ASCII character for later.
|
|
if ((pc[i] < 0x20) || (pc[i] > 0x7e))
|
|
buff[i % 16] = '.';
|
|
else
|
|
buff[i % 16] = pc[i];
|
|
buff[(i % 16) + 1] = '\0';
|
|
}
|
|
|
|
// Pad out last line if not exactly 16 characters.
|
|
while ((i % 16) != 0)
|
|
{
|
|
written_chars = sprintf_s(temporary_buff, 20, " ");
|
|
strcat_s(line, 74, temporary_buff);
|
|
ZeroMemory(temporary_buff, 20);
|
|
i++;
|
|
}
|
|
|
|
// And print the final ASCII bit.
|
|
written_chars = sprintf_s(temporary_buff, 20, " %s\n", buff);
|
|
strcat_s(line, 74, temporary_buff);
|
|
ZeroMemory(temporary_buff, 20);
|
|
dprintf("[hexDump] %s", line);
|
|
#endif
|
|
|
|
return;
|
|
}
|