概述

Checkpoint安全研究人员发现了一系列针对广告商进行欺诈活动的应用程序。其中有一个恶意软件PreAMo,可以通过点击从三家广告商的banner来模拟用户,这三家广告上分别是Presage, Admob和Mopub。

6款恶意软件的下载量一共超过9000万次,目前Google已经从Google Play中移除了受感染的应用程序。

图1 – RAM Master Google Play信息

PreAMo

PreAMo是由三部分不同的代码组成的,每部分代码负责处理一个广告商。因为这三块代码是分布在不同的package中的,触发方式不同,而且是没有联系的。将这三部分代码连接在一起的是都与同一个C2服务器进行通信,该C2服务器被用来发送统计数据和接收配置数据。这三部分代码的行为也是类似的,他们在广告网络加载的banner上注册了一个监听器,一旦banner加载,PreAMo就使用Android框架 ‘MotionEvent’类来模拟点击。

但由于广告库在实现过程中存在差异,PreAMo背后的攻击者使用不同的方法来处理每个广告商。

1. Ad Agency #1 – Admob:

‘PreAMo’在com.google.android.gms.ads.internal.tools.ConfigProvider的manifest中进行了注册,目的就是在host应用启动时初始化AdMobFixer类。该类会用计时器来注册一个动态接收器来周期性地从C2服务器检查配置更新。

图2 – 接收器注册

 ‘PreAMo’用两种不同的方法来检测是否展示了广告banner,第一种方法是使用反射来使其中断为内部结构,并安装回调,第二张方法是基于活动生命周期回调的。

图3 – 检测方法 #1

图4 – 检测方法#2

每当应用中有新活动创建时安卓系统就会通知监听器。PreAMo会从顶级的窗口(Décor)开始递归地搜索特定的Ad View

图5 –OnAdActiviy的实现

成功检测到banner后,PreAMo会检查以下条件集:

· 用户是否organic。该标记是com.DianXinOS库的一部分,是根据INSTALL_REFERRER的监听器接收的intent设置的。如果内容含有organic这个词,表明应用是在Google Play中搜索后安装的,该值就会设置为True,否则设置为false。只有当值为False时,Autoclicking才工作。但在PreAMo的一些版本中,该值是从C2服务器接收的。

· 用户还没有点击广告banner。

· 点击的间隔和每日点击的最大量低于预定义的限制。

· 检查随机值。

如果上面的条件都满足, PreAMo就在banner上模拟点击。为了达到这个目的,恶意软件会从文件assets/xdd读取预定义的坐标位置,在有些情况中,这是基于banner的大小的,PreAMo可以使用随机生成的坐标。

图6 – PreAMo执行的条件检查

图7 – 从xdd读取的预决定的坐标

图8 – 使用随机生成的坐标

Ad Agency #2 – Presage:

这部分代码的执行是来源于com.DianXinOS.OService类。在调用onStartCommand的方法中,PreAMo会开始一个新的线程来周期性地显示来自Presage (ogury)库的插播广告。

在线程中,恶意软件会与C2服务器进行通信,并从URL hxxps://res.mnexuscdn[.]com/dp/0845e0150308bcdf5ef03ba8295075f9来加载配置数据。

图9 – 线程创建

图10 – Ad策略(配置数据)

PreAMo会接收配置文件并检查ads (min_i_sec_limit)和每日最大ads (max_p_ad)的周期。在验证成功后,PreAMo会从Presage显示与广告有关的活动。

图11 – 检查广告配置

图12 – 插播广告

除了从Presage显示广告外,PreAMo还会为主机应用注册自己的活动管理器,再实现以下的方法(图13)并替换默认的Presage web客户端来使用随机生成的坐标来点击banner(图14)。

图13 – Presage的再实现方法

图14 – 随机生成的坐标

Ad Agency #3 – Mopub:

当PreAMo处理Mopub时,初始化的部分会位于内容提供商中。提供者位于com.android.stats.tools.InitProvider类中,在主机应用开始时,也就是OnCreate方法中,就会执行代码并为计时器的配置。

计时器会周期性地发送请求到URL集,检查配置更新:

· hxxps://res.mnexuscdn[.]com/dp/a79a2d1b9a8252fcc6917d6c46211199’

· hxxps://res.mnexuscdn[.]com/dp/75d109e54ce75b05064374ae0b77a359’

· hxxps://res.mnexuscdn[.]com/dp/dd87a43132f3ce443d1e50b29019de3b’

· hxxps://res.mnexuscdn[.]com/dp/8c7d12b8c28d8e573439c59627df9092’

与前面的部分类似,配置包括不同的延迟和与点击算法相关的条件。因为Mopub库是开源的,ProAMo开源注入代码,融合到库的源代码中。比如,使用方法com.mopub.common.AdUrlGenerator中的伪造的package信息。

图15 – 伪造的package信息

同样的方式,攻击者会修改Mraid控制器的代码来处理WebView组件中打开的链接。控制器含有实现MraidListener接口的对象,其中含有JS触发的事件的回调。其中一个回调方法叫做onOpen,开源对JS中打开的URL做出反应。

图16 –伪造的主机信息

图17 – MraidController实现

onOpen的实现叫做class URLHandler,负责处理scheme对应的URL。PreAMo背后的攻击者对这一过程做了修改。如果URL的处理正确,而且是一个HTML页面,就模拟点击。

图18 – Clicking Imitation #1

HTMLWebViewClient做的事情也是类似的,其中含有一个方法shouldOverrideUrlLoading,会在URL在当前WebView中加载的时候给主机应用一个机会进行控制。Mopub就用它来检查如何处理要在WebView中加载的URL,但恶意软件开发者会注入自己的代码来在要加载的页面上执行点击。

图19 – Click imitation #2

C2服务器

C2服务器域名res.mnexuscdn.com是过匿名服务注册的,根据passive total的数据表明最早是2018年12月12日出现的。

图20 –RiskIQ的WhoIS数据

从AdMob点击接收配置数据:

· https://res.mnexuscdn[.]com/dp/4456476a30e111ba1b3ee299c279d51a’

从Mopub 点击接收配置数据:

· https://res.mnexuscdn[.]com/dp/a79a2d1b9a8252fcc6917d6c46211199’

· https://res.mnexuscdn[.]com/dp/dd87a43132f3ce443d1e50b29019de3b’

· https://res.mnexuscdn[.]com/dp/8c7d12b8c28d8e573439c59627df9092’

· https://res.mnexuscdn[.]com/dp/cdaef0b66c347591bffd32260d76c6cc’

· https://res.mnexuscdn[.]com/dp/9a641c69cf5186982e8ac8c4e06e80f4’

· https://res.mnexuscdn[.]com/dp/f61f3f92d0dbcebe26d47dea3af1492a’

从presage/ogury 点击接收配置数据:

· https://res.mnexuscdn[.]com/dp/0845e0150308bcdf5ef03ba8295075f9’

漏洞简介

首先,我抓取并手动执行了该应用程序上的所有用户操作。之后,我通过burp检查了http历史记录中的所有的请求,坦率的说我当时想要挖掘url重定向漏洞。所以,我通过Burp搜索了url=所有可能的参数,并最终发现了一个如下所示的网址:

https://example.com/viewimage/?url=AWS图像位置(该图像存储在AWS,所以需要从这里加载)

实际上,这个端点就是用来加载我长传到网站上的图像的,而我的图像是存储在aws存储桶中的。很明显,既然它要加载内容,那为什么不从其他域加载内容呢?试试RFI如何?

下面是我所采取的具体步骤:

尝试XSS(可惜失败了)

首先,我尝试挖掘一个XSS漏洞。为此,我参阅了关于挖掘XSS漏洞的文章。我的做法就是在网址中添加http://brutelogic.com.br/poc.svg。 所以,最终制作出来的网址变成https://example.com/viewimage/?url=http://brutelogic.com.br/poc.svg。 之后,我访问了该网址,但它并没有加载内容,而是下载了一个简单的文本文件,不过该文件内并没有任何内容。

尝试读取本地文件(成功了)

接下来,我尝试通过URL模式来读取内部数据并让服务器执行某些操作(如file:///、dict://、ftp://、gopher://等)。所以,最终制作出来的网址为https://example.com/viewimage/?url=file:///etc/passwd。

然后,又下载了一个文本文件:

1.jpeg

当我检查下载的文本文件时,对于其中的内容倍感惊讶:这分明就是etc/passwd文件的内容。

1.png

现在,既然发现这个网站会加载来自AWS的图像,那么为什么不设法提取内部的AWS元数据呢?

读取AWS EC2元数据

为了对url参数进行相应的修改,需要查看当前运行实例中所有类别的实例元数据,为此,可以借助于下列URI:

http://169.254.169.254/latest/meta-data

所以,最终精心设计的URL变为https://example.com/viewimage/?url=Location。

之后不出所料,它再次下载了一个文本文件,该文件内容如下所示:

1.jpeg

接下来,我们将尝试读取更为机密的信息,具体方法如下所示:

http://169.254.169.254/latest/meta-data/iam/security-credentials/aws-elasticbeanorastalk-ec2-role

这样,我们就能读取秘密访问密钥、令牌等机密信息了。之后,将这些信息导出后,就可以通过AWS客户端访问……这个,你们懂的。

简单来说,利用这里的安全权限,我们就可以通过SSRF漏洞实现RCE了。

获取私有访问秘钥等登陆凭证后进一步渗透测试的详细介绍,请参阅https://www.notsosecure.com/exploiting-ssrf-in-aws-elastic-beanstalk/。

另外,该漏洞我已经提交了相关部门,并且现在已经得到了修复,同时,我还收到了4位数的奖金。希望本文对大家有所帮助!

据微博@互联网的那点事爆料称,哔哩哔哩(B站)整个网站后台工程源码泄露,并且“不少用户名密码被硬编码在代码里面,谁都可以用。”

1.png

2.jpeg

同时,在开源及私有软件项目托管平台GitHub上,出现了名为“哔哩哔哩bilibili网站后台工程源码”的项目。该项目由账号“openbilibili”创建,其中有不少用户名密码被硬编码在代码里面,由于网站的开源性质,登录网站者均可使用。该项目一度获得6597个标星和6050个代码库分支(fork)。

6.jpeg

随后有网民在该项目内留言,请求上传者删除大仓代码。

7.jpeg

据微博@互联网的那点事描述,源代码库中的用户名显示哔哩哔哩的邮箱,密码中则是“Test”+日期,应该是官方测试所用,暂未发现用户的账号密码泄露。受此影响,B站美股盘前一度跌逾5%,截至发稿跌2.92%。

4.jpeg

对此,B站已第一时间报案,并将彻查源头。B站表示:

经内部经济核查,确认该部分代码属于较老的历史版本,目前网站已执行主动防御措施,确保该事件不会影响到网站安全和用户数据安全。

360截图16350917474246.jpg

截至发稿,这一代码库已被GitHub删除。

8.png

https://github.com/openbilibili/go-common

作为目前人气最高的两家国内弹幕式视频网站,它们除了在市场上你争我抢外,在网络安全领域可谓是一对难兄难弟。

“弹幕元素”所包含的即时便捷的互动性,和新奇的体验感,深受年轻一代群众的喜爱。而因着计算机的高度普及,大数据时代下,人们也越来越重视“数据”的安全性,加上全网普及“实名认证体系”更是把每个人生活上的点点滴滴记录在册,数据安全更成为了每个人不可忽视的问题。

早在2018年6月,A站就被曝出遭黑客攻击,近千万条用户数据外泄,泄露的数据主要包括用户ID、昵称、加密储存的密码等。

上一篇文章中,我们已经看到,底层的malloc通过分配内存块来处理内存分配请求。每个块不仅存储程序员将与之交互的malloc返回的“用户数据”区域,还存储与该块关联的元数据。

12.png

我们不仅了解了堆管理器的基本块分配策略是如何工作的,还了解了在没有可用来服务请求的已释放块时,如何从堆的顶部创建新的块。

在这篇文章中,我想谈谈这种块回收策略是如何工作的,即如何将分配发送回free get并最终回收以服务未来的malloc请求。许多堆利用技术都依赖于利用这些内部机制。现在,就让我们看看当堆正常运行时,这些块是如何被回收的?

13.gif

回收过程

当完成来自malloc的分配(或像calloc这样与malloc兼容的分配)时,程序员通过将其发送给free将其释放回堆管理器。C标准将free(NULL)定义为什么都不做,但对于其他free的调用,堆管理器的第一个工作是将指针解析回其相应的块。堆管理器通过从发送给free的指针中减去块元数据的大小来完成此操作。

这种从指针到块的转换之所以有效,是因为用户数据区域位于块内。但是,当然,只有当发送给free的指针确实来自malloc的活动分配时,它才是有效的。如果将其他指针被发送给free,则堆管理器可能会释放或回收一个无效块,从而导致内存损坏问题,进而导致进程崩溃,甚至可能让黑客远程接管进程。

出于这个原因,在尝试处理释放的指针之前,free首先进行一些基本的完整性检查,以查看释放的指针是否明显无效。如果检查失败,程序将中止。检查包括:

1.检查分配是否对齐到8字节或64位上的16字节,因为malloc确保所有分配都对齐。

2.检查块的大小字段是不是太小、太大或没有对齐的大小,甚至会重叠进程的地址空间。

3.检查块是否位于arena的边界内;

4.通过检查位于下一个块开始的元数据中对应的“P”位,来检查块是否已经标记为空闲。

上面所述的堆管理器的检查并不全面,攻击者控制的数据指针可能会绕过这些完整性检查,在进程中触发并损坏内存。

空闲的块元数据

上一篇文章中,我还介绍了活动块如何将元数据与程序员使用的“用户数据”区域一起存储。这些活动分配存储“块大小”,并在其元数据中存储称为“A”,“M”和“P”的三个位。这些位有助于堆管理器记住块是否从非主arena分配的,是否通过mmap在堆外分配,以及前面的块是否是空闲的。

空闲的块也存储元数据,与活动分配类似,它们存储“块大小”、“A”和“P”字段,但是它们不使用“M”字段,因为mmap-ed块总是在空闲期间被munmap-ed,而不是被转换为可回收的空闲块。

14.png

空闲块还使用一种称为“边界标记(boundary tags)”的技术在用户数据区域之后存储信息。这些边界标记在块之前和之后携带大小信息。这允许从任何已知块开始,以任何方向遍历块,从而能够非常快速地合并相邻的空闲块。

这些释放的块存储在作为链表操作的相应“空闲箱”中。这要求每个空闲块还存储指向其他块的指针,由于释放的块中的“用户数据”可供堆管理器免费使用,因此堆管理器将释放的块中的“用户数据”区域重新用作此附加元数据所在的位置。

使用bin回收内存

在内部,堆管理器需要跟踪已释放的块,以便malloc可以在分配请求期间重用它们。在一个简单的实现中,堆管理器可以通过简单地将所有释放的块存储在一个巨大的链表中来实现这一点。这是可行的,但这会使malloc变慢。由于malloc是大多数程序的高利用率组件,因此这种速度会对系统上运行的程序的总体性能产生巨大的影响。

为了提高性能,堆管理器会管理一系列名为“bin”的列表,这些列表的设计目的是最大化分配和释放的速度。

每个线程有5种类型的bin:62个小bin、63个大bin、1个未排序bin、10个快速bin和64个tcachebin。小的、大的和未排序的bin是最古老的bin类型,用于实现堆的基本回收策略。快速bin和tcache bin是分层优化的结果。

令人困惑的是,小的、大的和未排序的bin都位于堆管理器源代码中的同一个数组中。索引0未使用,1为未排序的bin,bin 2-64为小bin,bin65-127为大bin。

15.png

块回收的基本策略

在进行tcache和快速bin优化之前,让我们先看看堆管理器使用的基本回收策略。

回忆一下,free的基本算法如下:

1.如果块在元数据中设置了M位,则分配是在堆外分配的,应该进行munmap;

2.否则,如果此块之前的块是空闲的,则向后合并该块以创建更大的空闲块;

3.类似地,如果这个块之后的块是空闲的,那么这个块将向前合并,以创建一个更大的空闲块。

4.如果这个可能更大的块与堆的“顶部”相邻,那么整个块将被吸收到堆的末尾,而不是存储在“bin”中。

5.否则,块将被标记为空闲,并放置在适当的bin中。

小bin(SMALL BIN)

小bin是最容易理解的基本bin,它共有62个,每个小bin都存储着大小相同的块。在32位系统上,每个小于512字节的块(或者在64位系统上小于1024字节)都有一个对应的小bin。由于每个小bin只存储一种大小的块,所以它们是自动排序的,所以插入和删除这些列表中的条目非常快。

16.png

大bin(LARGE BIN

小bin策略非常好,但是我们不能为每个可能的块都设置一个bin。对于512字节以上的块(64位上是1024字节),堆管理器使用“大bin”。

这63个“大bin”中的每一个都与小bin的操作方式基本相同,但它们不是存储固定大小的块,而是存储在大小范围内的块。每个大bin的大小范围被设计成既不与小bin的块大小重叠,也不与其他大bin的范围重叠。

必须手动对bin中的插入进行排序,并且需要遍历列表中的分配。这使得大bin自然比小bin慢。然而,在大多数程序中,使用大bin的频率较低。这是因为程序倾向于分配并因此释放小的分配,其平均速率远远高于大的分配。出于同样的原因,最小的“大bin”只包含从512字节到576字节的64字节范围,最大的bin中也只有1MB多已释放的块。

17.png

未分类的bin(UNSORTED BIN)

18.png

堆管理器使用名为“未排序的bin”的优化缓存层进一步改进了这个基本算法。例如,释放树或列表的程序通常会同时为每个条目释放许多分配,而更新列表中的一个条目的程序可能会在为其替换分配空间之前释放前一个条目。

在这些情况下,在将结果较大的块放入正确的bin之前,合并这些已释放的块可以避免一些性能消耗,加快整个过程。

堆管理器引入未排序的bin来利用这些结果。堆管理器不会立即将新释放的块放到正确的bin中,而是将其与邻居合并,并将其转储到一个普通的未排序链表中。在malloc期间,将检查未排序的bin上的每一项,以确定它是否“适合”该请求。。如果是这样,malloc可以立即使用它。如果没有,则malloc将块放入相应的小bin或大bin中。

快速bin(FAST BIN)

18.png

快速bin则是进一步的优化,这些bin本质上还是保持了一个“快速周转队列”上最近释放的小bin,其目的就是保持块是活动的,而不是将块与其邻居合并。以便在块释放后不久,如果malloc请求该块大小,它可以立即重新使用。

和小bin一样,每个快速bin只负责一个固定的块大小。有10个这样的快速bin,包括大小为16、24、32、40、48、56、64、72、80和88字节的块以及块元数据。

与它们对应的小bin不同的是,快速bin从不与邻居合并。实际上,堆管理器不会在下一个块的开始处设置“P”位。如果你愿意,你可以从概念上将其看作堆管理器。

和它们的小bin一样,快速bin只包含一个块大小,因此会自动排序,因此插入和删除都非常快。此外,它们也可以存储在单链表中,而不需要存储在双链表中,以便在块合并时从列表中删除它们。

当然,快速bin的缺点是,它不会被真正释放或合并,因为这最终会导致进程的内存无限膨胀。为了解决这个问题,堆管理器定期合并堆。通过刷新快速bin中的每个条目,将其与相邻的空闲块合并,并将生成的空闲块放置到未排序的bin中,供malloc稍后使用。

每当malloc请求大于快速bin所能提供的服务时,即对于64位上超过512字节或1024字节的块,当释放任何超过64KB的块(其中64KB是启发式选择的值),或者当程序调用malloc_trim或mallopt时,就会发生此合并。

TCACHE bin或每线程CACHE bin

堆管理器用于加速分配的最终优化是每线程缓存或“tcache”分配器。首先让我们来看看tcache试图解决的问题。

给定计算机系统上的每个进程都有一个或多个线程同时运行,拥有多个线程允许一个进程执行多个并发操作。例如,一个高容量的web服务器可能具有多个同时传入的请求,并且web服务器可能在自己的线程上为每个传入的请求提供服务,而不是让每个请求排队等待服务。

给定进程中的每个线程会共享相同的地址空间,也就是说,每个线程可以在内存中看到相同的代码和数据。每个线程都有自己的寄存器和堆栈来存储临时本地变量,但是全局变量和堆之类的资源在所有线程之间共享。

20.png

协调对全局资源(比如堆)的访问是一个复杂的主题,如果做错了,可能会导致一个称为“竞态条件”的问题,这会导致难以调试的崩溃,而这些崩溃通常也会被黑客利用。

在本文的示例中,我们假设了一个正在服务于一个线程的web请求试图更新数据库行,而另一个并发web请求试图从同一行读取数据。通常,我们希望确保第二个线程不会在写入的过程中看到该行,因为它被另一个线程覆盖,从而以某种部分或损坏的形式看到该行。数据库通过使读取和写入看起来以原子方式运行来解决此问题:如果两个线程同时尝试访问同一行,则必须在下一个操作开始之前完成一个操作。解决这些竞争条件的一种非常常见的方法是通过使用锁来强制以其他方式同时访问全局资源的请求进入顺序队列。

通常,锁通过一个线程“标记”它在使用之前获取全局资源的所有权,执行其操作后,然后标记该资源不再使用。如果另一个线程出现并想要使用该资源并看到其他一些线程正在使用它,那么该线程将一直等到另一个线程完成。这可确保全局资源一次仅由一个线程使用。但缺点是:这太浪费时间,该过程被称为“锁竞争(Lock Contention)”。

对于许多全局变量,这是可接受的浪费。但是发生锁竞争的话,如果被保护的代码需要完成的工作量越多,那么等待获得锁的线程也数量也变的越来越大。

堆管理器主要通过使用每个线程的arena来解决这个问题,每个线程都有自己的arena,直到达到阈值为止。此外,每个线程的tcache缓存旨在降低锁本身的成本,因为锁指令非常昂贵,最终会占用快速路径中相当大的一部分执行时间。现在,这个特性被添加到glibc 2.26中的malloc内存分配函数中,并在默认情况下启用。

每线程缓存通过为每个线程准备小bin来加快分配速度,这样,当线程请求一个块时,如果线程的tcache上有一个可用的块,那么它就可以为分配提供服务,而不需要等待堆锁(heap lock)。

默认情况下,每个线程有64个单独链接的tcache bin。每个bin最多包含7个大小相同的块,在64位系统上从24到1032字节不等,在32位系统上从12到516字节不等。

的分配如何在TCACHE bin中结束?

当一个块被释放时,堆管理器会查看该块是否适合对应于块大小的tcache bin。与快速bin一样,tcache bin上的块被认为是“正在使用”的,不会与相邻的已释放块合并。

如果用于该块大小的tcache已满或者该块对于tcache bin来说太大,则堆管理器将恢复到我们以前的慢路径策略,即获取堆锁,然后像以前一样处理该块。

相应的tcache分配也非常简单,如果一个块在适当的tcache bin上可用,那么给定一个块的请求,堆将返回该块而不会获得堆锁。如果数据块对于tcache来说太大,我们则采取以前的办法继续。

在我们尝试进行分配的情况下,有一个对应的tcache bin,但是这个bin已经满了,我们执行一个稍微修改过的分配策略。

总结

现在,我们已经充分了解了malloc和free在glibc堆实现中的整个行为,以及算法。让我们回顾一下:

首先,每个分配都作为一个内存块存在,它是对齐的,包含元数据和程序员想要的区域。当程序员从堆中请求内存时,堆管理器首先计算出分配请求对应的块大小,然后按照以下顺序搜索内存:

1.如果大小与tcache bin对应,并且有一个可用的tcache块,那么立即返回。

2.如果请求很大,则通过mmap从堆外分配;

3.否则我们获取arena堆锁,然后执行以下策略:

3.1试试fastbin/smallbin回收策略;

3.2解析所有延迟释放;

3.3默认返回基本的回收策略;

3.4从头创建一个新块;

3.5如果所有操作都失败,则返回NULL。

相应的free策略有:

1.如果指针为NULL,则C标准将行为定义为“不执行任何操作”;

2.否则,通过减去块元数据的大小将指针转换回块;

3.对块执行一些完整性检查,如果完整性检查失败,则中止。

4.如果块适合放在tcache bin中,则将其存储在那里;

5.如果块设置了M位,则通过munmap将其返回给操作系;

6.否则我们将得到arena堆锁;

*本文原创作者:宇宸@默安科技合规研究小组,本文属于FreeBuf原创奖励计划,未经许可禁止转载


本篇为管理要求中系统运维管理的部分,等级保护中内容最多的一个章节。文中内容都是个人观点,如有不对的地方欢迎纠正。文章以等保三级系统为基础,从合规角度解读要求。

前序文章:

等保到底是个啥(一):物理安全部分

等保到底是个啥(二):网络安全部分

等保到底是个啥(三):主机安全部分

等保到底是个啥(三):主机安全部分

等保到底是个啥(四):应用与数据安全部分

等保到底是个啥(五):制度与人员安全部分

等保到底是个啥(六):系统建设管理部分

7-完结撒花S.jpg正文

本部分内容有些地方看起来可能会和技术要求差不多,但是从管理和流程方面来约束的,主要考察管理制度的运行情况。本部分内容较长,建议各位分段看,全部阅读起来可能会需要较长时间。

7.2.5 系统运维管理

7.2.5 系统运维管理
7.2.5.1环境管理(G3)
a)应指定专门的部门或人员定期对机房供配电、空调、温湿度控制等设施进行维护管理;
b)应指定部门负责机房安全,并配备机房安全管理人员,对机房的出入、服务器的开机或关机等工作进行管理;
c)应建立机房安全管理制度,对有关机房物理访问,物品带进、带出机房和机房环境安全等方面的管理作出规定;
d)应加强对办公环境的保密性管理,规范办公环境人员行为,包括工作人员调离办公室应立即交还该办公室钥匙、不在办公区接待来访人员、 工作人员离开座位应确保终端计算机退出登录状态和桌面上没有包含敏感信息的纸档文件等。

这里又把物理安全的内容重新提了一遍,但重点是管理而非技术。

a) 机房的日常巡检,一般是每日,也可以是三日,最多不能超过一周,不然有些说不过去;这类工作大部分是由运维来负责,除了设备状况和信息告警这类常规检查外,环境和设施也要检查,比如温湿度是不是在合理范围内、灭火系统的压力表是否正常(对于指针位于红色区域的灭火器材要及时更换)、强电设备及UPS运行状况有无异常、机房内有无凝露的情况等等。并且要有巡检的记录和巡检人签名。

b) 本条要求一般只要不是配线间级别的机房都会做得差不多,可能有些企业不会去做各种访问记录、操作记录,但检查时看的就是这些记录文档;从管理角度来看,这些基础的工作还是要做起来的,不能偷懒。

c) 小企业可能没有机房制度或者太简单,大中型企业一般都有,这类制度目前大同小异,都是模板式的文档,可以根据自身情况修改一下,做成宣传板挂到机房或管理室的墙上。

d) 这里是一些安全意识细节,也是不好管理的点,毕竟制度和要求可以有,具体能执行的如何没办法有效控制。本条只是举了几个例子,其实目前不是检查,而是督促企业加强安全意识教育和宣传,不要因为缺少相关意识给企业带来损失,不只是资金上,大公司还有企业形象层面的社会影响。

这里提的就是离职人员,一定要第一时间收回各种权限,门禁、钥匙之类物品;工作设备不用时要手动锁屏,或设置自动锁屏;关键区域外人及无关人员不得访问;敏感类资料不要直接放在桌面,纸质材料不能随便至于办公桌,会议讨论的方案或系统架构类的信息要及时清除(在白板上临时写的一些内容)。

这类细节其实很多,标准不可能穷举。

7.2.5 系统运维管理
7.2.5.2资产管理(G3)
a) 应编制并保存与信息系统相关的资产清单,包括资产责任部门、重要程度和所处位置等内容;
b) 应建立资产安全管理制度,规定信息系统资产管理的责任人员或责任部门,并规范资产管理和使用的行为;
c) 应根据资产的重要程度对资产进行标识管理,根据资产的价值选择相应的管理措施;
d) 应对信息分类与标识方法作出规定,并对信息的使用、传输和存储等进行规范化管理。

资产管理一直都是大事,但一直不被重视,大多数公司摸不清自己的家底,存在边缘资产。这种情况无论是管理还是安全层面都是潜在风险,等被人搞了才知道原来自己还有这么个东西在网络中。

a) 由于标准比较老,这里提的还是纸质的资产列表清单,当前来看基本都是电子清单或者是资产统一管理平台。建议如果有预算还是上一套资产管理系统,若是没钱,那就辛苦点进行资产梳理,尽可能避免存在边缘未知资产。这里其实对于自己的东西还好(接触过一个CIO,下边每个员工的鼠标键盘他都清楚,对资产管理非常重视,但是提到外包人员在企业内部新建的资产清单情况,瞬间就心虚了),重点是第三方人员的管理,比如外包开发软件,要利用企业资源搭建开发环境,测试环境,安服方面演练可能还会搭个靶场什么的,这帮人装了多少个虚拟机,建了多少个数据库,开了多少端口,哪些设备连了外网,这些作为甲方坑内不可能全都弄清楚,那么项目交付后,这些临时的资产(硬件、软件、信息类)是否全部下线或卸载,以我的实际经历来看,大多安全主管、运维主管都不清楚,CIO就更不知道了。所以,这里建议由系统去自动发现和管理资产,尽可能减少人为低级别工作的参与度。

b) 有了上述的资产管理系统,制度就相对没那么重要了,但不代表没用,要约束一些基本原则和流程。

c) 资产梳理清晰之后,就要进行分类分级了,和数据类似,有高中低之分,目的是明确哪些是关键资产,重点保护,出现问题时要优先处理;对于硬件资产要粘贴不易撕除的标签,说明资产类别、编号、SN号等信息,检查时候会看。

d) 本条其实应该在c前边,对于资产分类分级的依据说明,有点像应急预案中的安全事件等级定义;此外后边还提到了对信息的访问权限、传输和存储管理,可以归为数据治理范畴,本人没有深入接触,所以只能提一句。等级测评时,主要做到不用级别的数据有访问权限设置,低级用户访问高级数据要提交申请,审批后才允许访问,并对这个过程进行记录(各种日志),敏感信息存储要加密或专业的防护措施,传输过程要对通信加密。一般能做到这几点,本条的要求点也就基本满足了。

7.2.5 系统运维管理
7.2.5.3介质管理(G3)
a) 应建立介质安全管理制度,对介质的存放环境、使用、维护和销毁等方面作出规定;
b) 应确保介质存放在安全的环境中,对各类介质进行控制和保护,并实行存储环境专人管理;
c) 应对介质在物理传输过程中的人员选择、打包、交付等情况进行控制,对介质归档和查询等进行登记记录,并根据存档介质的目录清单定期盘点;
d) 应对存储介质的使用过程、送出维修以及销毁等进行严格的管理,对带出工作环境的存储介质进行内容加密和监控管理,对送出维修或销毁的介质应首先清除介质中的敏感数据,对保密性较高的存储介质未经批准不得自行销毁;
e) 应根据数据备份的需要对某些介质实行异地存储,存储地的环境要求和管理方法应与本地相同;
f) 应对重要介质中的数据和软件采取加密存储,并根据所承载数据和软件的重要程度对介质进行分类和标识管理。

介质管理在检查中算是一个重点,但是很多企业做得都不太好,包括一些大型知名企业,安全防护是做的不错,但一到内部管理流程就一塌糊涂。

a) 介质管理制度一定要求,哪怕简单的十几行也可以;网上素材也有,参考一下结合企业实际情况进行可落地的制度修订。

b) 介质要有专门的存储空间,可以是仓库,可以是保密室、也可以是办公室,只要你能保证存放在此空间的介质得到有效保护就行,并且要制定专人管理(没说不能兼职);一般我看到的多数就是一堆移动硬盘、U盘直接放某人的抽屉里(磁带之类的一般还都有仓库存放),或者放档案柜里。别说保护了,就是当一堆办公用品在管。不过换个角度来看,外人也不知道这些东西会涉密,感觉就是一堆垃圾随便乱堆。

c) 这块我接触到的企业没有做到的,要求介质在运输(物理传输太书面了)中要有相关要求和操作,保证数据安全;介质也算资产,包括里边存储的数据,要盘点列入资产清单,存储敏感信息的介质使用和查看都要有审批和记录。这点我印象比较深刻的好像是亚马逊云的一个产品,好像叫Snowball,主要用于灾备运输,就是这玩意。

7-02.jpg

d) 本条就是对介质使用、维修及销毁的管理,对于环境外介质使用的加密和监控很难管理,这部分更多的还是建议制度和追责相结合,毕竟不是谁都有钱搞DLP的,而且企业越大DLP越难实施。至于送修可能不太可能,除非意外,没做备份,介质里的数据很重要,必须去做数据恢复,那么这又涉及到恢复人员可能读取或窃取企业资料的问题,尽量避免这种情况发生。销毁比较好说了,报废的介质,尽可能彻底销毁,不要叫给外部专业机构去做,除非量特别大,一般可以给员工发个锤子,拿着一堆介质去房间里发泄一下,尤其是研发同学(开玩笑)。测评时重点会看介质管理制度,使用、维修、销毁、外带的约束,此外这些操作的记录要有。

e) 就是异地场外备份,此外某些机密信息(非数据库、用户信息数据)电子档,要异地进行备份;后边还提到了一句备份的方式和要求必须一致,不能异地备份有另外一套策略。

f) 在前边几条我已经一起啰嗦出来了,什么DLP啊,资产分类分级啊;如果难以实施,那么最起码的介质分类要做,这个不费时间,加密实在不行就用bitlocker,一人管理介质,一人管理密钥(虽然不是很靠谱,但没办法穷嘛),或者买个保险柜专门保存介质,一人负责档案室大门钥匙管理,另一人负责保险箱密码和钥匙(不能都由一个人去管,容易出事)。这是最便宜的控制方式了,此外介质上要有标签。

7.2.5 系统运维管理
7.2.5.4设备管理(G3)
a) 应对信息系统相关的各种设备(包括备份和冗余设备)、线路等指定专门的部门或人员定期进行维护管理;
b) 应建立基于申报、审批和专人负责的设备安全管理制度,对信息系统的各种软硬件设备的选型、采购、发放和领用等过程进行规范化管理;
c) 应建立配套设施、软硬件维护方面的管理制度,对其维护进行有效的管理,包括明确维护人员的责任、涉外维修和服务的审批、维修过程的监督控制等;
d) 应对终端计算机、工作站、便携机、系统和网络等设备的操作和使用进行规范化管理,按操作规程实现主要设备(包括备份和冗余设备)的启动/停止、加电/断电等操作;
e) 应确保信息处理设备必须经过审批才能带离机房或办公地点。

偏运维的管理,包括的制度其实比较多,差不多每一条都要有一个对应的制度,而且,最特么烦的就是没个制度都要跟一个流程,都要有过程记录(可以是电子流程),这就很麻烦了,不过大企业标准流程就是这么搞的,真正跑起来之后其实也不错。

a) 机房巡检里包括这些内容,具体分工各企业会有不同,可以在监控平台进行自动化巡检,比当年省心多了。

b) 设备管理制度要求,信息类物品的采购要有规范,一般是采购部的事情,所以这块的制度我没有写过。

c) 这部分和巡检有重叠,定期巡检查看设备状况也算是维护的一个环节,不过从后半句的描述来看,应该是指与供应商之间的设备维修、更换、升级一类的制度,可以简单写几条。一般来说,企业申请设备维修或更换都会提审批,所以流程应该不会少,就看有没有人管理这些记录。;

d) IT部门员工操作规范/手册,呵呵,大多企业都没有,不过也不算重点项,有最好,没有也无妨,如果人手够,事情不太多,可以试着做一下,好处也是有的,记得要添加主要设备的启动/停止、加电/断电操作方法。

e)  这点对于当前的企业来说,我觉得是废话,没有谁会不打招呼直接把核心交换、防火墙或路由器拆走带出去吧。如果真有,那这种公司我觉得就不要搞什么IT了,不适合。检查时,一是看机房制度,二是看审批记录。一般是设备维修或更换才会带离机房。

另外在终端层面,做得好的公司都会有策略,确保外部办公设备的保密性;做得不好的,压根不担心,因为那些东西丢了也无所谓。重点还是制度宣传,追责和惩罚来约束。

7.2.5 系统运维管理
7.2.5.5监控管理和安全管理中心(G3)
a)  应对通信线路、主机、网络设备和应用软件的运行状况、网络流量、用户行为等进行监测和报警,形成记录并妥善保存;
b)  应组织相关人员定期对监测 和报警记录进行分析、评审,发现可疑行为,形成 分析报告,并采取必要的应对措施;
c)  应建立安全管理中心,对设备状态、恶意代码、补丁升级、安全审计等安全相关事项 进行集中管理。

a) 网络监控平台,对机房所有设备进行监控,一般都会自带报表生成功能;没钱买,那就搞开源好了,也可以用,主要好安全防范,不要被当做突破口就好。

b) 流量分析、日志审计、态势感知,配合做好应急预案,基本就差不多了,态势感知可能有点扯,但是前两个应该问题不大。

c) 部署漏洞管理平台,HIDS,可以参考下安骑士的那些功能,很像这种防护软件。如果没预算,那最基本的把补丁管理做好,这块测评时也说得过去。

7.2.5 系统运维管理
7.2.5.6网络安全管理(G3)
a) 应指定专人对网络进行管理,负责运行日志、网络监控记录的日常维护和报警信息分析和处理工作;
b) 应建立网络安全管理制度,对网络安全配置、日志保存时间、安全策略、升级与打补丁、口令更新周期等方面作出规定;
c) 应根据厂家提供的软件升级版本对网络设备进行更新,并在更新前对现有的重要文件进行备份;
d) 应定期对网络系统进行漏洞扫描,对发现的网络系统安全漏洞进行及时的修补;
e) 应实现设备的最小服务配置,并对配置文件进行定期离线备份;
f) 应保证所有与外部系统的连接均得到授权和批准;
g) 应依据安全策略允许或者拒绝便携式和移动式设备的网络接入;h) 应定期检查违反规定拨号上网或其他违反网络安全策略的行为。

管理的东西比技术还多,全做到可能性不大,投入人力和时间太多。中小企业确实搞不起,可以选择性来做,成熟后再逐步完善。

a) 指定网络管理员(AB岗,不可与其他运维职位兼职),做好日常运维工作,做好巡检记录;

b) 已经说的很明白了,制定一份网络安全管理制度,包括配置管理(这里只提到了安全配置,其实网络配置也要一起备份)、日志管理(6个月)、安全策略(根据实际情况来写)、补丁管理(之前说的漏洞管理平台)、密码管理(一般3个月换一次,8位以上,复杂度要求)。这只是为了应付检查来写的内容,如果认真去搞安全运营,还会涉及其他内容。

c) 及时更新设备版本,规则库,后边很良心的还提示了,升级前做好备份工作。

d) 同样是补丁管理的一个环节,定期漏扫已经成为常态,不再是问题了。

e) 最小化安装原则,没必要的服务和插件、软件一律不装,对于系统配置文件定期备份(建议不要超过一周),存储到介质或其他存储设备。

f) 本条强调私自连接外网的情况,现在手机都可以开热点,笔记本一联,防不胜防,考技术手段来防,策略设置比较复杂,增加额外成本,建议还是以思想教育为主,提高员工安全意识,这是最靠谱的。我说的有点引申了,其实测评主要考察的就是能够访问外网的设备,必须是经过审批授权的。

g) 同f,加强安全意识,用惩罚来威慑。

h) 拨号上网具体指什么,这么多年一直没有个明确的概念,个人感觉应该就是私自连接外网的行为吧,技术要求中提出要能够检测内部设备私接外网的行为,技术层面可以做,但是费时费力费钱,从个人角度来看,说到底还是加强管理更靠谱一些。

7.2.5 系统运维管理
7.2.5.7系统安全管理(G3)
a)应根据业务需求和系统安全分析确定系统的访问控制策略;
b)应定期进行漏洞扫描,对发现的系统安全漏洞及时进行修补;
c)应安装系统的最新补丁程序,在安装系统补丁前,首先在测试环境中测试通过,并对重要文件进行备份后,方可实施系统补丁程序的安装;
d)应建立系统安全管理制度,对系统安全策略、安全配置、日志管理和日常操作流程等方面作出具体规定;
e)应指定专人对系统进行管理,划分系统管理员角色,明确各个角色的权限、责任和风险,权限设定应当遵循最小授权原则;
f)应依据操作手册对系统进行维护,详细记录操作日志,包括重要的日常操作、运行维护记录、参数的设置和修改等内容,严禁进行未经授权的操作;
g)应定期对运行日志和审计数据进行分析,以便及时发现异常行为。

这里有点重复啰嗦了,不过也逐条说一下。

a) ACL运维层面都会去做,安全层面也会去做,最基本的划好安全域,做好不同组别的逻辑隔离,不同权限的访问限制,基本也就够了,再细的东西要看实际情况。

bc) 漏扫不再重复了。

d) 主机层面的安全管理制度,和前面的网络安全管理制度其实差不多,不再重叙。

e) 指定系统管理员(AB岗,不能兼职其他运维职位),要有岗位职责说明,注意做好权限分离,不要一个人拥有所有权限;系统同样权限最小化原则。

f) 前边说的操作手册,来了吧,这里是检查是否按照操作手册去执行,记录有没有做,对于违规操作有没有惩罚和处置记录。

g) 一般驻场人员会做这个事情,不过现在大多配备日志审计和安全设备,有问题会直接告警。如果这些设备都没有,那要制定一个人定期查看这些日志,工作量较大。

7.2.5 系统运维管理
7.2.5.8恶意代码防范管理(G3)
a)应提高所有用户的防病毒意识,及时告知防病毒软件版本,在读取移动存储设备上的数据以及网络上接收文件或邮件之前,先进行病毒检查,对外来计算机或存储设备接入网络系统之前也应进行病毒检查;
b)应指定专人对网络和主机进行恶意代码检测并保存检测记录;
c)应对防恶意代码软件的授权使用、恶意代码库升级、定期汇报等作出明确规定;
d)应定期检查信息系统内各种产品的恶意代码库的升级情况并进行记录,对主机防病毒产品、防病毒网关和邮件防病毒网关上截获的危险病毒或恶意代码进行及时分析处理,并形成书面的报表和总结汇报。

本节主要讲主机层面的杀软和网络层面的防病毒设备。

a) 这条讲了2点。1注意这里提的是用户,除了员工还包括你的用户;这就有点难搞了,内部人员可以培训,外部人员可以在软件使用帮助中加入安全意识提示标语或描述,或者在用户协议、操作手册中体现防毒意识的内容。2对于新的病毒和漏洞,及时告知用户。

b) 定期杀毒,记得留记录,检查时要看你有没有做过全盘扫描,周期频率多久;对于linux这类的系统,有的单位裸跑,中招了手工处理,如果有这样的团队能搞定这事也可以,若不是,那你想好了现场怎么跟对方解释。这里还隐含了一点,记住不要用破解版,你可以用免费版,但不能盗版,如果发现用盗版,本条直接就GG了。

c) 及时更新病毒库和版本。

d) 前边还好,杀软本身都会记录查杀过的病毒文件,但是对已发现的病毒进行分析和整理,并记录汇总,这点做的其实不多,一般都是中招的病毒才会去分析,如果直接被拦下的可能不太会在意。作为普通甲方来说,确实没必要,但如果有自己的知识库的,可以试着去做,检查的时候不是非要去做,本项非一票否决项。

7.2.5 系统运维管理
7.2.5.9密码管理(G3)
应建立密码使用管理制度,使用符合国家密码管理规定的密码技术和产品。

这个没必要说了,密码学东西很多,这里提到的是符合国家规定的密码技术和产品。另外对于密码管理要有制度。 

7.2.5 系统运维管理
7.2.5.10变更管理(G3)
a) 应确认系统中要发生的变更,并制定变更方案;
b) 应建立变更管理制度,系统发生变更前,向主管领导申请,变更和变更方案经过评审、审批后方可实施变更,并在实施后将变更情况向相关人员通告;
c) 应建立变更控制的申报和审批文件化程序,对变更影响进行分析并文档化,记录变更实施过程,并妥善保存所有文档和记录;
d) 应建立中止变更并从失败变更中恢复的文件化程序,明确过程控制方法和人员职责,必要时对恢复过程进行演练。

变更管理时运维的一个重要流程,企业应该重视,包括乙方在内。

a) 变更要提前申请,提交完整的变更方案,其中包括回滚方案;

b) 变更管理制度,网上很多,目前已经很完善了,很多是针对项目的,运维方面的也有。不多做解释了,模板里写的都不错。

c) 变更要有流程,无论纸质还是电子流程,要适用、合理,不能为了应付弄个简化流程,这样不合适,不利于企业日后管理,留存好记录。

d) 这点就是回滚/恢复详细方案(中止变更流程,包含在变更流程中,是一种特殊情况),对于重要系统的变更,要预先进行演练,确认方案没问题再予以实施。

7.2.5 系统运维管理
7.2.5.11备份与恢复管理(G3)
a)应识别需要定期备份的重要业务信息、系统数据及软件系统等;
b)应建立备份与恢复管理相关的安全管理制度,对备份信息的备份方式、备份频度、存储介质和保存期等进行规范;
c)应根据数据的重要性和数据对系统运行的影响,制定数据的备份策略和恢复策略,备份策略须指明备份数据的放置场所、文件命名规则、介质替换频率和将数据离站运输的方法;
d)应建立控制数据备份和恢复过程的程序,对备份过程进行记录, 所有文件和记录应妥善保存;
e)应定期执行恢复程序,检查和测试备份介质的有效性,确保可以在恢复程序规定的时间内完成备份的恢复。

备份和恢复是重点检查项,总结一下,主要会关注几个点:

首先灾难恢复计划时针对重要关键业务通,不是所有系统,一般系统只要有一定备份恢复可行方案即可,分优先级做备份恢复管理;

制度必要要有;

关键系统实时备份,场外备份,异地备份,6个月至少;

有钱的搞双活,没钱的搞双机双线;

定期进行灾难恢复演练,验证可行性、有效性,一般一年至少一次(建议)。

7.2.5 系统运维管理
7.2.5.12安全事件处置(G3)
a)应报告所发现的安全弱点和可疑事件,但任何情况下用户均不应尝试验证弱点;
b)应制定安全事件报告和处置管理制度,明确安全事件的类型,规定安全事件的现场处理、事件报告和后期恢复的管理职责;
c)应根据国家相关管理部门对计算机安全事件等级划分方法和安全事件对本系统产生的影响,对本系统计算机安全事件进行等级划分;
d)应制定安全事件报告和响应处理程序,确定事件的报告流程,响应和处置的范围、程度,以及处理方法等;
e)应在安全事件报告和响应处理过程中, 分析和鉴定事件产生的原因,收集证据,记录处理过程,总结经验教训,制定防止再次发生的补救措施,过程形成的所有文件和记录均应妥善保存;
f)对造成系统中断和造成信息泄密的安全事件应采用不同的处理程序和报告程序。

应急响应管理,目前外包形式较多,有自己应急团队的没多少。大多企业关心的还是出事了尽快搞定,别影响有任务,别影响形象,不出事的情况:跟我有毛关系,搞它干毛。

a) 渗透或无意中发现的漏洞,及时上报,不要去搞事,现在是要关小黑屋的。有些乙方的工程师,水平还可以,没事喜欢去挖洞,挖到了还喜欢去搞事,以前可能还好,最近如果还这么玩,估计很快会被公安请去喝茶。还有一点,没有授权,不要去搞政府网站,不要作死。

bc) 应急预案,不是那种随便写写的,看后边的要求,要包括事件分级、报告流程、应急流程、还有操作方法等。可以参考《国家网络安全事件应急预案》。

de) 应急预案的细节,对于一些常见的已知攻击或病的,要在附件形式附上不同状况的操作指导手册。至于应急响应的流程和每步操作,建议去看下ISCCC的应急处理资质的要求,里边虽然都是要求的内容,但是对于每步该走什么,该留存什么都很详细。官网可以下载《信息安全服务资质自评估表-应急处理类填写指南》。

f) 这条说的有点不太理解,中断和泄露为什么要用不同的预案和流程,这类可以定义为重大或特别重大安全事故,其实处理起来的过程差不多,只是造成的影响比较严重。有了解的同学可以解释一下。

7.2.5 系统运维管理
7.2.5.13应急预案管理(G3)
a)应在统一的应急预案框架下制定不同事件的应急预案,应急预案框架应包括启动应急预案的条件、应急处理流程、系统恢复流程、事后教育和培训等内容;
b)应从人力、设备、技术和财务等方面确保应急预案的执行有足够的资源保障;
c)应对系统相关的人员进行应急预案培训,应急预案的培训应至少每年举办一次;
d)应定期对应急预案进行演练,根据不同的应急恢复内容,确定演练的周期;
e) 应规定应急预案需要定期审查和根据实际情况更新的内容,并按照执行。

和应急响应章节相关,属于安全事件未发生前的预防措施。

a) 这条我是凭个人理解,针对不同的关键系统或基础设置制定不同的应急预案,由于业务原因应急方式可能会存在不同,所以不是一套预案就能使用所有系统。一般是指较大的企业。

b) 这条纯属屁话,众所周知,安全的预算你懂的。不过网安法出台了,等保强制了,也可以拿这条规定试着跟领导提要求,万一同意了呢。O(* ̄__, ̄*)o

cd) 已经白话了,不用再解释了;都是每年至少一次,没如果不做,测评时会扣分,印象中。

e) 建议和演练一起搞,每年修订一次,1-2年可能不变,要说3-5年还不变,就是扯了,一定要考虑预案的适用性,随着系统和业务的变化也要及时变更。不只是应急预案,同时也包括其他在行的制度和流程。安全是一个不断循环的过程。

结尾

到此为止,等保到底是个啥系列全部更新完成(说实话,我都不信我能写完这个系列)。希望能够帮到有需要的人,文中大多是个人经验和理解,肯定会有不当的地方,欢迎随意吐槽和纠正。感谢FB和各位的支持。

*本文原创作者:宇宸@默安科技合规研究小组,本文属于FreeBuf原创奖励计划,未经许可禁止转载

*本文作者:rebeyond,本文属FreeBuf原创奖励计划,未经许可禁止转载。

前言

近两年公司端侧发现的漏洞很大一部分都出在WebView白名单上,针对这类漏洞安全编码团队也组织过多次培训,但是这种漏洞还是屡见不鲜。下面本人就结合产品中容易出现问题的地方,用实例的方式来总结一下如何正确使用WebView白名单,给开发的兄弟们作为参考。

在Android SDK中封装了一个可以很方便的加载、显示网页的控件,叫做WebView,全限定名为:android.webkit.WebView。WebView是SDK层的一个封装,底层实现是Chromium(Android 4.4之前是webkit)。由于WebView功能非常强大,目前很多公司的 App 就只使用一个WebView 作为整体框架,App中的所有内容全部使用HTML5进行展示,这样只需要写一次HTML5代码,就可以在多个平台上运行,而不需要更新端侧APP本身。

WebView只是Android SDK中的一个控件,其本身就像一个与APP隔离开的容器,在WebView中加载的所有页面都运行在这个容器中,无法与APP Java(或者Kotlin)层或者native层交互。为了使H5页面更方便地与APP进行交互,Webview提供了一个addJavascriptInterface方法,该方法可以把一个Java类注入到当前WebView的实例中,这样利用该Webview实例加载的页面就可以方便地利用Javascript与Java层通信了。

一个例子

首先我们先写一个极简demo APP,这个APP只有一个全屏的webview控件在MAinActivity中,webview中通过addJavascriptInterface注入了一个名为myObj的Java对象,myObj为该对象在Javascript世界中的名字,其在Java中对应的类名为JsObject。APP打开的时候会加载https://www.rebeyond.net/poc.htm,poc.htm中的js代码会调用Java世界中的getToken方法,并把getToken的返回值通过alert弹框显示。

demo APP代码如下:

package rebeyond.net.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    class JsObject {
        @JavascriptInterface
        public String getToken() { return "{\"token\":\"1234567890abcdefg\"}"; }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView webView = (WebView) findViewById(R.id.myWebview);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient());
        webView.setWebChromeClient(new WebChromeClient());
        webView.addJavascriptInterface(new JsObject(),"myObj");
        webView.loadUrl("https://www.rebeyond.net/poc.htm");
    }
}

poc.htm代码如下:

<script>
    alert(window.myObj.getToken());
</script>

APP运行效果:

一文彻底搞懂安卓WebView白名单校验

OK,上面是JavaScriptInterface的一个简单功能演示,下文随着案例深入我们会逐渐扩充这段代码,下面言归正传。

如何正确校验白名单

下面我们预设一个场景:该demo APP开发人员小A认为getToken这个方法返回的字符串是一个用户会话标识,属于敏感信息,不应该就这样完全暴露出去,只有白名单中的域名及其子域名才允许调用该方法。所以配置了一个白名单列表,如下:

String[] whiteList=new String[]{"huawei.com","hicloud.com"};

并实现了校验逻辑来判断调用方的域名是否在白名单内,不过这个校验逻辑并没有他当初想象的那么简单,里面有很多坑,下面我们围观下他的心路历程:

Round 1

对用户输入的URL进行域名白名单校验,小A首先想到的是用indexOf来判断,代码如下:

package rebeyond.net.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    class JsObject {
        @JavascriptInterface
        public String getToken() { return "{\"token\":\"1234567890abcdefg\"}"; }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView webView = (WebView) findViewById(R.id.myWebview);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient());
        webView.setWebChromeClient(new WebChromeClient());
        webView.addJavascriptInterface(new JsObject(),"myObj");

        String inputUrl="https://www.rebeyond.net/poc.htm";

        if (checkDomain(inputUrl))
        {
            webView.loadUrl(inputUrl);
        }
    }

    private static boolean checkDomain(String inputUrl)
    {
        String[] whiteList=new String[]{"huawei.com","hicloud.com"};
        for (String whiteDomain:whiteList)
        {
            if (inputUrl.indexOf(whiteDomain)>0)
                return true;
        }
        return  false;
    }
}

绕过

这个校验逻辑错误比较低级,攻击者直接输入http://www.rebeyond.net/poc.htm?huawei.com就可以绕过了。因为URL中除了代表域名的字段外,还有路径、参数等和域名无关的字段,因此直接判断整个URL是不安全的。虽然直接用indexOf来判断用户输入的URL是否在域名白名单内这种错误看上去比较low,但是现实中仍然有不少缺乏安全意识的开发人员在使用。

Round 2

当然小A作为一个资深开发,很快自己便发现了问题所在,他意识到想要匹配白名单中的域名,首先应该把用户输入的URL中的域名提取出来再进行校验才对,于是他自己做了一个升级版,代码如下:

private static boolean checkDomain(String inputUrl)
{
    String[] whiteList=new String[]{"huawei.com","hicloud.com"};
    String tempStr=inputUrl.replace("://","");
    String inputDomain=tempStr.substring(0,tempStr.indexOf("/")); //提取host
    for (String whiteDomain:whiteList)
    {
        if (inputDomain.indexOf(whiteDomain)>0)
            return true;
    }
    return  false;
}

绕过

首先我们看一下RFC中对URL格式的描述:

<protocol>://<user>:<password>@<host>:<port>/<url-path>

小A由于缺乏对URL语法的了解,错误的认为://和第一个/之间的字符串即为域名(host),导致了这个检测逻辑可以通过如下payload绕过:

http://[email protected]/poc.htm

攻击者利用URL不常见的语法,在URL中加入了Authority字段即绕过了这段校验。Authority字段是用来向所请求的访问受限资源提供用户凭证的,比如访问一个需要认证的ftp资源,用户名为test,密码为123456,可以直接在浏览器中输入URL:ftp://test:[email protected]/。

Round 3

小A意识到通过字符串截取的方式来获取host可能不太安全,于是去翻了一下Java文档,发现有个java.net.URL类可以实现URL的格式化,于是他又写了一个改进版:

private static boolean checkDomain(String inputUrl) throws MalformedURLException {
    String[] whiteList=new String[]{"huawei.com","hicloud.com"};
    java.net.URL url=new java.net.URL(inputUrl);
    String inputDomain=url.getHost(); //提取host
    for (String whiteDomain:whiteList)
    {
        if (inputDomain.indexOf(whiteDomain)) //www.huawei.com      app.hicloud.com
            return true;
    }
    return  false;
}

绕过

使用java.net.URL确实可以得到比较准确的host,但是小A仍然使用了indexOf来判断,所以还是可以很简单的通过如下payload绕过:

http://www.hicloud.com.rebeyond.net/poc.htm

上述URL的host中包含hicloud.com字符串,但是www.hicloud.com并不是域名,而是rebeyond.net这个域名的一个子域名,所以最终还是指向了攻击者控制的服务器。

Round 4

既然indexOf不能用,那还可以选择startsWith、endsWith或者equals,不过一般白名单匹配的时候都要匹配子域名,所以equals和startsWith被排除,于是小A用endWith又写了一个版本:

private static boolean checkDomain(String inputUrl) throws MalformedURLException {
    String[] whiteList=new String[]{"huawei.com","hicloud.com"};
    java.net.URL url=new java.net.URL(inputUrl);
    String inputDomain=url.getHost(); //提取host
    for (String whiteDomain:whiteList)
    {
        if (inputDomain.endsWith(whiteDomain)) //www.huawei.com      app.hicloud.com
            return true;
    }
    return  false;
}

绕过

通过java.net.URL提取域名,然后通过endWith来匹配白名单,聪明的你一定想到了如下payload来绕过endsWith的匹配:

http://rebeyondhuawei.com/poc.htm

只要注册一个以huawei结尾的顶级域名就可以绕过白名单了,我查了一下rebeyondhuawei.com这个域名可以注册,一年只要60块钱:)

一文彻底搞懂安卓WebView白名单校验

Round 5

小A现在知道问题出在哪了,只要在endsWith的时候,在白名单前面加个点,就可以避免这种情况了,于是又改进一个版本:

private static boolean checkDomain(String inputUrl) throws MalformedURLException {
    String[] whiteList=new String[]{"huawei.com","hicloud.com"};
    java.net.URL url=new java.net.URL(inputUrl);
    String inputDomain=url.getHost(); //提取host
    for (String whiteDomain:whiteList)
    {
        if (inputDomain.endsWith("."+whiteDomain)) //www.huawei.com      app.hicloud.com
            return true;
    }
    return  false;
}

绕过

经过了上面几轮错误的修正,目前这个版本看上去应该没什么问题了。真的没问题了么?如果java.net.URL可以得到绝对准确的host,那确实没问题了,但事实上,java.net.URL并不是完全可信,比如下图:

一文彻底搞懂安卓WebView白名单校验

https://www.rebeyond.net\@www.huawei.com/poc.htm上述URL通过java.net.URL的getHost方法得到的host是www.huawei.com,但实际上访问的确是www.rebeyond.net服务器,可以看到www.rebeyond.net服务器上收到了如下这条访问日志:

一文彻底搞懂安卓WebView白名单校验

只要在www.rebeyond.net这个攻击者服务器上放置/@.huawei.com/poc.htm这样一个文件,就可以绕过白名单调用JavaScriptInterface里的getToken了。

当然除了上面那种用@符号绕过的方法外,还有另外一种:

https://www.rebeyond.net\\.huawei.com

上述URL经过java.net.URL的getHost方法提取得到的是www.rebeyond.net.huawei.com,可以绕过白名单域名的endsWith匹配,但是实际访问的确是www.rebeyond.net服务器,访问日志如下图:

一文彻底搞懂安卓WebView白名单校验

该问题在最新的Java10仍然存在,现已提交至Oracle官方修复。另外,android.net.Uri存在同样的问题,不过在18年1月和4月分别修复了这两个bug,git commit见文末参考链接。

Round 6

连JDK自带的java.net.URL都有问题,那还有什么安全的方法么?有的,那就是java.net.URI。如下是小A用java.net.URI对Round5中的绕过payload进行的测试结果:

一文彻底搞懂安卓WebView白名单校验一文彻底搞懂安卓WebView白名单校验

可以看到畸形的URL会直接抛异常。小A痛定思痛,写下了下面这个版本,用java.net.URI代替java.net.URL:

private static boolean checkDomain(String inputUrl) throws  URISyntaxException {
    String[] whiteList=new String[]{"huawei.com","hicloud.com"};
    java.net.URI url=new java.net.URI(inputUrl);
    String inputDomain=url.getHost(); //提取host
    for (String whiteDomain:whiteList)
    {
        if (inputDomain.endsWith("."+whiteDomain)) //www.huawei.com      app.hicloud.com
            return true;
    }
    return  false;
}

绕过

上面这种写法,对于单纯的host的校验来说,确实没有问题了,但是如果攻击者在协议名称上动点手脚,还是可以绕过。在讲绕过方法之前,我们先看一段代码:

package rebeyond.net.myapplication;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView webView = (WebView) findViewById(R.id.myWebview);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient());
        webView.setWebChromeClient(new WebChromeClient());

        String inputUrl="javascript:alert('hello world');";
        webView.loadUrl(inputUrl);
    }
}

执行结果如下图:

一文彻底搞懂安卓WebView白名单校验一文彻底搞懂安卓WebView白名单校验

可以看到,webview的loadUrl方法可以直接执行JavaScript伪协议中的代码,于是构造如下URL,即可绕过java.net.URI的检测:

JavaScript://http://www.rebeyond.net/poc.htm

上述URL的getHost结果为www.huawei.com,如下图:

一文彻底搞懂安卓WebView白名单校验一文彻底搞懂安卓WebView白名单校验

但是webview实际执行的是如下两行JavaScript代码:

//www.huawei.com/ 
window.location.href='http://www.rebeyond.net/poc.htm'

第一行通过//符号来骗过java.net.URI获取到值为www.huawei.com的host,恰好//符号在Javascript的世界里是行注释符号,所以第一行实际并没有执行;然后通过%0d%0a换行,继续执行window.location.href=’http://www.rebeyond.net/poc.htm’请求我们的poc页面,最终可以成功绕过白名单限制调用getToken接口,执行效果如下:

一文彻底搞懂安卓WebView白名单校验

Round 7

小A恍然大悟:看来坏人在协议上面也能做手脚,那我只要再加个协议名称校验就可以了,三下五除二写了个最终版:

private static boolean checkDomain(String inputUrl) throws  URISyntaxException {
    if (!inputUrl.startsWith("http://")&&!inputUrl.startsWith("https://"))
    {
        return false;
    }
    String[] whiteList=new String[]{"huawei.com","hicloud.com"};
    java.net.URI url=new java.net.URI(inputUrl);
    String inputDomain=url.getHost(); //提取host
    for (String whiteDomain:whiteList)
    {
        if (inputDomain.endsWith("."+whiteDomain)) //www.huawei.com      app.hicloud.com
            return true;
    }
    return  false;
}

绕不过

域名白名单校验逻辑经过上述7个小版本的迭代,终于得到了一个比较完善的版本。如果不考虑白名单域名服务器自身有安全问题的情况,这个校验逻辑目前是安全的,推荐大家采用。

在哪里校验白名单

上面我们得到了一个安全的白名单校验方法,然后问题来了,应该在哪个地方调用这个校验方法呢?前面我们只在loadUrl之前校验了一次,这样够么?不够。

URL跳转绕过

上述“最终版”的校验逻辑确实可以安全地校验域名白名单,但是整体的校验方案仍然不是最优,下面继续来看个例子:

https://www.huawei.com/redirect.php?url=http://login.huawei.com

这是我虚构的一个URL,该URL的功能是跳转至SSO登录页面。打开这个URL后,服务器会返回一个302响应:

一文彻底搞懂安卓WebView白名单校验

然后浏览器侧会再次请求Location中指定的URL。对于大型网站而言,特别是有单点登录功能的网站,这种类型的接口很常见。如果攻击者构造如下URL,是不是就可以绕过域名白名单了呢?答案是可以绕过。

https://www.huawei.com/redirect.php?url=https://www.rebeyond.net/poc.htm

我们来测试一下,把demo APP稍作修改,加一些log,完整代码如下:

package rebeyond.net.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;

public class MainActivity extends AppCompatActivity {
    class JsObject {
        @JavascriptInterface
        public String getToken() {
            Log.e("rebeyond","i am in getToken");
            return "{\"token\":\"1234567890abcdefg\"}";
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView webView = (WebView) findViewById(R.id.myWebview);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient());
        webView.setWebChromeClient(new WebChromeClient());
        webView.addJavascriptInterface(new JsObject(),"myObj");

        String inputUrl="https://www.huawei.com/redirect.php?url=https://www.rebeyond.net/poc.htm";
        try {
            if (checkDomain(inputUrl))
            {
                Log.e("rebeyond","i am a white domain");
                webView.loadUrl(inputUrl);
            }
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }
    private static boolean checkDomain(String inputUrl) throws  URISyntaxException {
        if (!inputUrl.startsWith("http://")&&!inputUrl.startsWith("https://"))
        {
            return false;
        }
        String[] whiteList=new String[]{"huawei.com","hicloud.com"};
        java.net.URI url=new java.net.URI(inputUrl);
        String inputDomain=url.getHost(); //提取host
        for (String whiteDomain:whiteList)
        {
            if (inputDomain.endsWith("."+whiteDomain)) //www.huawei.com      app.hicloud.com
                return true;
        }
        return  false;
    }
}

我们在checkDomain校验返回true的时候和调用JavascriptInterface getToken的时候,分别打印一条日志。APP 运行日志如下:

一文彻底搞懂安卓WebView白名单校验

可以看到我们通过一个URL跳转顺利绕过了域名白名单校验。

解决方案

根据上面的分析可以得出,Webview在请求https://www.huawei.com/redirect.php?url=https://www.rebeyond.net/poc.htm的时候,实际是发出了两次请求,第一次是在loadUrl中请求https://www.huawei.com/redirect.php?url=https://www.rebeyond.net/poc.htm,第二次是请求https://www.rebeyond.net/poc.htm,但是第二次请求发生在loadUrl之后,而我们的白名单校验逻辑在loadUrl之前,才导致了绕过。有什么方法可以在请求每个URL的时候都插入校验逻辑呢?那就是重写webview的shouldOverrideUrlLoading方法,该方法会在webview后续加载其他url的时候回调:

package rebeyond.net.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;

public class MainActivity extends AppCompatActivity {
    class JsObject {
        @JavascriptInterface
        public String getToken() {
            Log.e("rebeyond","i am in getToken");
            return "{\"token\":\"1234567890abcdefg\"}";
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView webView = (WebView) findViewById(R.id.myWebview);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                Log.e("rebeyond","start to shouldOverrideUrlLoading url:"+request.getUrl());
                return super.shouldOverrideUrlLoading(view, request);
            }
        });
        webView.setWebChromeClient(new WebChromeClient());
        webView.addJavascriptInterface(new JsObject(),"myObj");

        String inputUrl="https://www.huawei.com/redirect.php?url=https://www.rebeyond.net/poc.htm";
        try {
            if (checkDomain(inputUrl))
            {
                Log.e("rebeyond","start to loadUrl:"+inputUrl);
                Log.e("rebeyond","i am a white domain");
                webView.loadUrl(inputUrl);
            }
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }
    private static boolean checkDomain(String inputUrl) throws  URISyntaxException {
        if (!inputUrl.startsWith("http://")&&!inputUrl.startsWith("https://"))
        {
            return false;
        }
        String[] whiteList=new String[]{"huawei.com","hicloud.com"};
        java.net.URI url=new java.net.URI(inputUrl);
        String inputDomain=url.getHost(); //提取host
        for (String whiteDomain:whiteList)
        {
            if (inputDomain.endsWith("."+whiteDomain)) //www.huawei.com      app.hicloud.com
                return true;
        }
        return  false;
    }
}

看一下APP的logcat:

13.png

可以看到webview的第二次请求被shouldOverrideUrlLoading拦截到,因此除了在loadUrl之前校验白名单之外,还要在shouldOverrideUrlLoading中再校验一次,如下为改进版:

package rebeyond.net.myapplication;

public class MainActivity extends AppCompatActivity {
    class JsObject {
        @JavascriptInterface
        public String getToken() {
            Log.e("rebeyond","i am in getToken");
            return "{\"token\":\"1234567890abcdefg\"}";
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView webView = (WebView) findViewById(R.id.myWebview);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                Log.e("rebeyond","start to shouldOverrideUrlLoading url:"+request.getUrl());
                String inputUrl=request.getUrl().toString();
                if (checkDomain(inputUrl))
                {
                    return false; //域名校验通过,允许请求
                }
                return true; //域名校验失败,终止请求
            }
        });
        webView.setWebChromeClient(new WebChromeClient());
        webView.addJavascriptInterface(new JsObject(),"myObj");

        String inputUrl="http://www.huawei.com/redirect.php?url=https://www.rebeyond.net/poc.htm";
            if (checkDomain(inputUrl))
            {
                Log.e("rebeyond","start to loadUrl:"+inputUrl);
                Log.e("rebeyond","i am a white domain");
                webView.loadUrl(inputUrl);
            }
    }
    private static boolean checkDomain(String inputUrl)  {
        if (!inputUrl.startsWith("http://")&&!inputUrl.startsWith("https://"))
        {
            return false;
        }
        String[] whiteList=new String[]{"huawei.com","hicloud.com"};
        java.net.URI url= null;
        try {
            url = new java.net.URI(inputUrl);
        } catch (URISyntaxException e) {
            return false;
        }
        String inputDomain=url.getHost(); //提取host
        for (String whiteDomain:whiteList)
        {
            if (inputDomain.endsWith("."+whiteDomain)) //www.huawei.com      app.hicloud.com
                return true;
        }
        return  false;
    }
}

通过在shouldOverrideUrlLoading中加入白名单校验逻辑就可以保证webview所有加载的页面不会超出白名单的范围。这个解决方案一句话总结就是:只在loadUrl之前校验白名单还不够,还要在shouldOverrideUrlLoading中再校验一次。

JavaInterface接口安全分级

我们继续回到小A的心路历程里来,假如小A开发的所有JavascriptInterface接口都是同一个安全等级,那上述的方案已是最佳校验方案。但是小A接到了另外一个需求:该APP需要和多家第三方公司合作,需要提供一些不包含敏感信息的接口给第三方H5页面调用。要求在JsObject中增加一个方法getUsername。之前的getToken方法只开放给 .huawei.com,getUsername方法同时开放给.hicloud.com和*.huawei.com。小A心想:这个简单,把checkDomain方法修改一下,在白名单内部做个等级划分,hicloud.com和huawei.com为0级,代表低安全等级;huawei.com为1级,代表高安全等级,然后只要在JavascriptInterface方法中再加一个校验逻辑即可,于是一鼓作气写下了如下代码:

package rebeyond.net.myapplication;

public class MainActivity extends AppCompatActivity {
    class JsObject {
        private String currentHost;
        @JavascriptInterface
        public String getToken() {
            Log.e("rebeyond","i am in getToken under host:"+currentHost);
            if (checkDomain(currentHost,1)) //安全等级高,只信任huawei.com
            {
                Log.e("rebeyond","allowed to call getToken");
                return "{\"token\":\"1234567890abcdefg\"}";
            }
            else
            {
                Log.e("rebeyond","not allowed to call getToken");
                return "";
            }
        }
        @JavascriptInterface
        public String getUsername() {
            Log.e("rebeyond","i am in getUsername under host:"+currentHost);
            if (checkDomain(currentHost,0)) //安全等级低,信任huawei.com和hicloud.com
            {
                return "{\"userName\":\"root\"}";
            }
            else
                return "";
        }

        public void setCurrentHost(String currentHost) {
            this.currentHost = currentHost;
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final JsObject jsObject=new JsObject();
        WebView webView = (WebView) findViewById(R.id.myWebview);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                String inputUrl=request.getUrl().toString();
                Log.e("rebeyond","override url :"+inputUrl);
                jsObject.setCurrentHost(inputUrl); //把webview将要加载的URL传递给JsObject
                if (checkDomain(inputUrl,0))
                {
                    return false; //域名校验通过,允许请求
                }
                return true; //域名校验失败,终止请求
            }
        });
        webView.setWebChromeClient(new WebChromeClient());

        webView.addJavascriptInterface(jsObject,"myObj");

        String inputUrl="https://www.hicloud.com/poc.htm";

            if (checkDomain(inputUrl,0))
            {
                Log.e("rebeyond","start to loadUrl:"+inputUrl);
                Log.e("rebeyond","i am a white domain");
                webView.loadUrl(inputUrl);
            }
    }
    private static boolean checkDomain(String inputUrl,int securityLevel)  {
        if (!inputUrl.startsWith("http://")&&!inputUrl.startsWith("https://"))
        {
            return false;
        }
        String[] whiteList=new String[]{"huawei.com","hicloud.com"};
        if (securityLevel==0) //低安全等级,该等级下信任huawei.com和hicloud.com
        {
            whiteList=new String[]{"huawei.com","hicloud.com"};
        }
        else if (securityLevel==1)  //高安全等级,该等级下只信任huawei.com
        {
            whiteList=new String[]{"huawei.com"};
        }
        java.net.URI url= null;
        try {
            url = new java.net.URI(inputUrl);
        } catch (URISyntaxException e) {
            return false;
        }
        String inputDomain=url.getHost(); //提取host
        for (String whiteDomain:whiteList)
        {
            if (inputDomain.endsWith("."+whiteDomain)) //www.huawei.com      app.hicloud.com
                return true;
        }
        return  false;
    }
}

越权绕过

上面这代码咋看貌似没什么问题,严格的白名单校验方法checkDomain;考虑到了URL重定向的情况重写了shouldOverrideUrlLoading。每一次shouldOverrideUrlLoading的时候都把新的URL传给JsObject中以备在JavascriptInterface中检测。

是的,这种情况如果想用白名单外的域名来绕过暂时是没有可能了,但是如果是白名单内的一个安全等级比较低的域名(比如APP开放给第三方合作伙伴的低权限白名单)想要越权访问安全等级比较高的JavascriptInterface接口,这段代码实现还是可以被绕过的。问题就出在这个shouldOverrideUrlLoading上,攻击者可以通过操纵shouldOverrideUrlLoading的URL来实现低安全级别的域名调用高安全级别的JavascriptInterface。怎么实现呢?大家如果研究过前端hack技术的话一定听说过一个常用的技术叫“Load and Overwrite Race Conditions”,就是利用竞态条件来欺骗浏览器,这种利用方法在地址栏欺骗这类攻击中非常多见,下面我们可以把这个思想借鉴到白名单的绕过中。

下面我们假设hicloud.com为第三方合作伙伴的域名,而且这个域名现在已经可以被攻击者控制(这个攻击者可以是第三方自己,也可以是渗透到第三方网络内部的其他人),先看一下我们之前的poc.htm:

<script>
    alert(window.myObj.getToken());
</script>

运行APP,加载https://www.hicloud.com/poc.htm,logcat如下:

一文彻底搞懂安卓WebView白名单校验

getToken方法没有被调用,这在我们意料之中,下面把poc.htm的代码稍作修改:

<script>
    var test=function (){alert(window.myObj.getToken());};
    setTimeout(test,500);
    document.location.href="https://www.huawei.com";
</script>

运行APP,加载https://www.hicloud.com/poc.htm,logcat如下:

15.png

可以看到我们用存在于hicloud.com域名下的js成功骗过webview,调用了只有huawei.com域名才有权限调用的getToken方法。解释一下POC:

首先hicloud.com是security level为0的普通白名单,可以通过loadUrl之前的checkDomain检测,此时JsObject中的currentHost被赋值为hicloud.com。

webview加载hicloud.com下的poc.htm。

poc第一步先定义一个延迟执行函数test,延迟500ms,test函数中调用getToken。

poc第二步执行document.location.href=”https://www.huawei.com”,此时webview会打开https://www.huawei.com,shouldOverrideUrlLoading方法被回调,这个时候webview会把www.huawei.com赋值给JsObject中的currentHost。

然后poc之前定义的一个延迟执行函数开始执行,getToken被调用,这时getToken中的域名校验函数会对JsObject中的currentHost进行安全等级校验,不过此时的currentHost已经被改写为huawei.com,可以顺利通过校验。

成功在hicloud.com域中调用到huawei.com域才有权限调用的getToken函数,纵向越权绕过成功。

这里我们利用的竞态条件是:当document.location.href=”https://www.huawei.com”刚开始执行,shouldOverrideUrlLoading被回调,但是hicloud.com的DOM还没销毁的间隙,可以让延迟函数成功执行。如果延迟执行设置的时间间隔比较久,可能hicloud.com页面的DOM已经被销毁,setTimeout所设置的延迟执行函数也就不会再执行了,利用就会失败。

另外,据我所知有开发人员只在JavascriptInterface中进行域名校验,这样即使校验逻辑写的再好,也于事无补。

如何防御

这个竞态条件可以成功被利用的根本原因是currentHost的值攻击者完全可控,换句话说就是我们通过shouldOverrideUrlLoading这个回调方法的第二个参数去取URL是不安全的,攻击者可以通过js任意修改shouldOverrideUrlLoading中可获取到的URL值。对于开发人员来讲,只想获取到webview加载的“主URL”,该“主URL”派生的其他攻击者完全可控的URL,特别是跨域的其他URL,不应该被用来作为安全校验的因素。所以需要把获取当前URL的方法改一下,从shouldOverrideUrlLoading的第一个参数webview中获取,利用webview.getUrl方法,该方法不会受js代码的影响,改进版如下:

package rebeyond.net.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.net.URISyntaxException;

public class MainActivity extends AppCompatActivity {
    class JsObject {
        private String currentHost;
        @JavascriptInterface
        public String getToken() {
            Log.e("rebeyond","i am in getToken under host:"+currentHost);
            if (checkDomain(currentHost,1)) //安全等级高,只信任huawei.com
            {
                Log.e("rebeyond","allowed to call getToken");
                return "{\"token\":\"1234567890abcdefg\"}";
            }
            else
            {
                Log.e("rebeyond","not allowed to call getToken");
                return "";
            }

        }
        @JavascriptInterface
        public String getUsername() {
            Log.e("rebeyond","i am in getUsername under host:"+currentHost);
            if (checkDomain(currentHost,0)) //安全等级低,信任huawei.com和hicloud.com
            {
                return "{\"userName\":\"root\"}";
            }
            else
                return "";
        }

        public void setCurrentHost(String currentHost) {
            this.currentHost = currentHost;
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final JsObject jsObject=new JsObject();
        WebView webView = (WebView) findViewById(R.id.myWebview);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                String inputUrl=request.getUrl().toString();
                Log.e("rebeyond","override url :"+inputUrl);
                Log.e("rebeyond","set JsObject currentHost :"+view.getUrl());
                jsObject.setCurrentHost(view.getUrl()); //把webview将要加载的URL传递给JsObject,从webview中取url,不要从request中取url
                if (checkDomain(inputUrl,0))
                {
                    return false; //域名校验通过,允许请求
                }
                return true; //域名校验失败,终止请求
            }
        });
        webView.setWebChromeClient(new WebChromeClient());

        webView.addJavascriptInterface(jsObject,"myObj");

        String inputUrl="https://www.hicloud.com/poc.htm";
            if (checkDomain(inputUrl,0))
            {
                Log.e("rebeyond","start to loadUrl:"+inputUrl);
                Log.e("rebeyond","i am a white domain");
                jsObject.setCurrentHost(inputUrl); //把webview将要加载的URL传递给JsObject
                webView.loadUrl(inputUrl);
            }
    }
    private static boolean checkDomain(String inputUrl,int securityLevel)  {
        if (!inputUrl.startsWith("http://")&&!inputUrl.startsWith("https://"))
        {
            return false;
        }
        String[] whiteList=new String[]{"huawei.com","hicloud.com"};
        if (securityLevel==0) //低安全等级,该等级下信任huawei.com和hicloud.com
        {
            whiteList=new String[]{"huawei.com","hicloud.com"};
        }
        else if (securityLevel==1)  //高安全等级,该等级下只信任huawei.com
        {
            whiteList=new String[]{"huawei.com"};
        }
        java.net.URI url= null;
        try {
            url = new java.net.URI(inputUrl);
        } catch (URISyntaxException e) {
            return false;
        }
        String inputDomain=url.getHost(); //提取host
        for (String whiteDomain:whiteList)
        {
            if (inputDomain.endsWith("."+whiteDomain)) //www.huawei.com      app.hicloud.com
                return true;
        }
        return  false;
    }
}

运行APP,取logcat如下:

16.png

问题完美解决。

总结

前面跟了小A一路的心路历程,略显繁琐,下面给做开发的朋友们做个总结:

白名单校验函数到底该怎么写?

 private static boolean checkDomain(String inputUrl) throws  URISyntaxException {
     if (!inputUrl.startsWith("http://")&&!inputUrl.startsWith("https://")) //重要提醒:建议只使用https协议通信,避免中间人攻击
     {
         return false;
     }
     String[] whiteList=new String[]{"huawei.com","hicloud.com"};
     java.net.URI url=new java.net.URI(inputUrl);
     String inputDomain=url.getHost(); //提取host,如果需要校验Path可以通过url.getPath()获取
     for (String whiteDomain:whiteList)
     {
         if (inputDomain.endsWith("."+whiteDomain)||inputDomain.equals(whiteDomain)) //www.huawei.com      app.hicloud.com
             return true;
     }
     return  false;
 }

可以总结为如下几条开发建议:

1). 不要使用indexOf这种模糊匹配的函数; 2). 不要自己写正则表达式去匹配; 3). 尽量使用Java封装好的获取域名的方法,比如java.net.URI,不要使用java.net.URL; 4). 不仅要给域名设置白名单,还要给协议设置白名单,一般常用HTTP和HTTPS两种协议,不过强烈建议不要使用HTTP协议,因为移动互联网时代,手机被中间人攻击的门槛很低,搭一个恶意WiFi即可劫持手机网络流量; 5). 权限最小化原则,尽量使用更精确的域名或者路径。

当然上述代码可能不完全符合业务开发需求,这里只是给大家一个参考,大家可以参考本文的案例自己开发出更适合的校验方法。

应该把白名单校验函数放在哪个环节校验?

1). loadUrl之前 2). shouldOverrideUrlLoading中 3). 如果需要对白名单进行安全等级划分,还需要在JavascriptInterface中加入校验函数,JavascriptInterface中需要使用webview.getUrl()来获取webview当前所在域。 上面这些都做了,我的JavascriptInterface还有没有可能被攻击?可能。比如白名单中的服务器存在XSS漏洞,或者白名单中的服务器被攻击者控制,或者webview访问没有采用安全的传输通道导致被中间人劫持等,都可以在白名单信任域中注入恶意JavaScript。

参考

https://developer.chrome.com/multidevice/webview/overview 

https://developer.android.com/reference/android/support/test/espresso/web/bridge/JavaScriptBridge 

https://www.bleepingcomputer.com/news/security/apples-safari-falls-for-new-address-bar-spoofing-trick/ 

https://www.blackhat.com/docs/asia-16/materials/asia-16-Baloch-Bypassing-Browser-Security-Policies-For-Fun-And-Profit-wp.pdf

https://android.googlesource.com/platform/frameworks/base/+/4afa0352d6c1046f9e9b67fbf0011bcd751fcbb5

https://android.googlesource.com/platform/frameworks/base/+/0b57631939f5824afef06517df723d2e766e0159

*本文作者:rebeyond,本文属FreeBuf原创奖励计划,未经许可禁止转载。

各位Buffer早上好,今天是2019年4月23日星期二。今天的早餐铺内容主要有:搜Wi-Fi热点Android应用数据泄露:涉200多万WiFi密码;B站网站后台工程源码疑似泄露,内含部分用户名密码;NCSC发布最常被黑客入侵的密码列表,你的是否也在其中?印度支付卡欺诈发案率快速上升,名列全球第二位;Google故意破坏竞争对手的浏览器?特斯拉调查汽车自燃事故。

1473663732618203.jpg

搜Wi-Fi热点Android应用数据泄露:涉200多万WiFi密码

一款流行的Android热点(WiFi)搜寻App“WiFi Finder”暴露了200多万个网络的WiFi密码。

目前已有数千人下载了这款WiFi Finder应用,它允许用户搜索附近的WiFi网络。但同时,这款应用也允许用户将WiFi密码从自己的设备上传到数据库,供其他人使用。

这个包含了200多万个网络密码的数据库并未受到任何保护,允许任何人访问,并批量下载这些内容。网络安全研究人员萨亚姆·杰恩(Sanyam Jain)率先发现了这个数据库,并将其发现报告开发商,但无济于事。最后通过联系数据库托管商DigitalOcean,后者在一天内就关闭了该数据库。

据悉,数据库中的每条记录都包含了WiFi网络名称、精确的地理位置、基本服务集标识符(BSSID)和明文存储的网络密码。[ifeng]

B站网站后台工程源码疑似泄露,内含部分用户名密码

4月22日,据微博@互联网的那点事爆料称,哔哩哔哩(B站)整Google故意破坏竞争对手的浏览器?个网站后台工程源码泄露,并且“不少用户名密码被硬编码在代码里面,谁都可以用。”新浪科技在GitHub上查询后发现,平台上确实存在由一个名叫“openbilibili”的用户创建的“go-common”代码库。目前,该项目已获得6597个标星和6050个代码库分支(fork)。

在昨日晚间7:50左右,哔哩哔哩通过官方微博发布一条针对这次泄露事件的回应声明称,“该部分代码属于较老的历史版本,已执行主动防御措施”。但几分钟之后,这条微博被删除;晚间8:20左右,官博再次发布相同的声明截图,数分钟后又再次删除。两次秒删让人费解,对此,我们期待B站的正式回应。[sina]

NCSC发布最常被黑客入侵的密码列表,你的是否也在其中?

英国国家网络安全中心(NCSC)发布了一份列表,列出了数据泄露中出现的 100000 个最常见的密码,以鼓励用户选择强密码。

到目前为止,数据泄露中显示的最常用密码是 “123456”,有 2320 万个帐户使用此密码。另外,全世界有 770 万用户选择使用 “123456789” 作为密码。紧接着排名靠前的三个密码均被超过 300 万用户使用:“qwerty” 出现 3.8 万次,“password” 出现 3.6 万次,“111111” 出现 310 万次。

许多常用的密码都是由一系列简单的数字构成,或将相同数字重复六到七次。

对此,NCSC 技术总监 Ian Levy 博士给出建议:密码重用是一个可以避免的主要风险,没有人应该用可以猜到的东西保护敏感数据,比如他们的名字、当地足球队或最喜欢的乐队。使用难以猜测的密码是一个强有力的第一步。我们建议结合三个随机但令人难忘的单词,这样人们就无法猜出你的密码。[oschina]

印度支付卡欺诈发案率快速上升,名列全球第二位

根据网络安全公司Gemini Advisory发布的网络犯罪统计数据,2018年,超过320万张印度支付卡记录被泄露,并在网上发布供出售,这与去年相比有了很大的飞跃,当时只有80万张印度支付卡的详细信息被发布在网络犯罪论坛上。

预计到2024年,印度将超过中国,成为世界上人口最多的国家,但是,有效支付卡的突然增长同时,银行业对支付卡安全性投资并没有增长。印度的银行仍然容易被黑客攻击,电子银行解决方案充满了安全漏洞,ATM网络不像其他国家的ATM网络一样受到密切关注,为网络攻击和金融欺诈创造了肥沃的土壤。

由于大多数印度银行不采用现代反欺诈系统,印度的支付卡也很受欢迎,因为它们可以很容易地被克隆并集体兑现。网络犯罪集团这一共识导致印度支付卡销售价格上涨了150%,而印度支付卡现在正处于热销状态。印度支付卡的平均价格现在为17美元(约1177.73印度卢比)。[cnbeta]

Google故意破坏竞争对手的浏览器?

前 Firefox 副总裁 Johnathan Nightingale 早些时候公开指责 Google 通过各种动作破坏 Firefox。他的故事引起了很多人的共鸣。Nightingale 不是第一个提出此类指控的 Firefox 团队成员。

去年 Mozilla 的技术项目经理 Chris Peterson 指责 Google 让 YouTube 在 Edge 和 Firefox 上加载缓慢。前微软 Edge 团队软件工程实习生 Joshua Bakita 也认为,微软放弃 EdgeHTML 渲染引擎切换到 Chromium 的原因之一是为了跟上 Google 推送到其网站上的一些变化。他认为 Google 的这些改变旨在让其它浏览器在 Google 网站上难以正常工作。

Google 被认为正慢慢变成 新的微软,而 Chrome 正变成新 IE。在 Chrome 取代 IE 成为最大浏览器的过程中,Firefox 丢失了 9% 的市场份额,而 IE 丢失了 22%。[solidot]

特斯拉调查汽车自燃事故

4月21日晚,上海徐汇一小区地下车库内,一辆特斯拉轿车突然冒出白烟起火燃烧。网友提供的照片显示,燃烧后的特斯拉面目全非,有附近车辆也遭遇烧毁。

突发情况发生后,住在小区高层建筑内的居民被紧急疏散。因地下车库散发出刺鼻气味,消防员一度无法进入车库,只能通过朝车库内灌水进行扑救,车库入口也被封闭。特斯拉新能源轿车烧损较严重,另两辆车不同程度烧损。现场无人员伤亡。

目前,火灾原因和财产损失正在进一步调查核实之中。今天早上,特斯拉官方微博回应称,在得知这起发生在上海的事故后已第一时间派出团队赶往现场,目前正在积极联络相关部门并配合核实情况。[北京日报]

4月22日下午,哔哩哔哩后台源码在GitHub上“被开源”,引发了很多用户关注,而在暴露长达数小时之后,该项目悉数被屏蔽,包括大量用户Fork的部分也无法访问。而在晚间哔哩哔哩通过官方微博发布针对该事件的回应,却又在几分钟后秒删。这一波操作,笔者是在看不懂了……

下午有用户在微博上爆料B站后台源码被泄露,甚至还包含部分用户名和密码。笔者在知晓该事件之时,泄露原地址已无法访问,而不少用户Fork版本还能够看到些“东西”。

005BYqpgly1g2bh4stjeej30ko10ktem.jpg根据能看到的历史截图,一名“openbilibili”的用户创建了“go-common”的代码库,同时该项目描述为清晰写着“哔哩哔哩 bilibili 网站后台工程 源码”。既然创建了“openbilibili”的用户名以及这项目描述,基本可以断定应该是有意而为之,至于后来网上所传言的什么“实习生失误操作”并不可信。

WX20190422-203818.pngkhzd-hvvuiyn5712419.jpg仔细查看该项目内容,还包含有各部分项目详细的负责人姓名拼音,源码内还包含部分用户名和密码。看来这次事件不大简单……

WX20190422-205346.png

随着事件逐渐发酵,关于该泄露项目所有内容均已被屏蔽,甚至连起初爆料的微博也已经被删除,微博上也很快无法搜索到这次事件的相关内容。看起来B站已经在进行紧急处理了。

WX20190422-200554@2x.png

这次看起来比较严重的事件,外界可能更想看到官方是如何回应,用户账户安全是否受到影响。而让人非常不解的是,在晚间7:50左右,哔哩哔哩通过官方微博发布一条针对这次泄露事件的回应声明称,“该部分代码属于较老的历史版本,已执行主动防御措施”。但几分钟之后,这条微博被删除;晚间8:20左右,官博再次发布相同的声明截图,数分钟后又再次删除。

这波操作,笔者真的看不懂了……

不只是回应不当还是有其他原因,B站的两次秒删让人更加怀疑这次事件的起因。截止稿件发布之时(21:05),哔哩哔哩官博还未发布新的声明。

FreeBuf也会持续关注该事件发展,希望B站能够尽快做好安全措施,尽可能降低此次事件可能造成的威胁。而事件源头,或许也是B站正在竭力调查,期待正式回应,给“这个瓜”做个了结。

彩蛋

部分吃瓜群众,还对这次泄露的源码分析一波,发现不少“彩蛋”……

6856d0b6gy1g2bjwxoi6zj215a0wuq6g.jpg0071ouepgy1g2bk4hpk37j30xk0u078l.jpg:Users:andy:Desktop:0071ouepgy1g2bjxanbgyj30qm0di42e.jpg

截图来自V2EX

*本文作者:Allen别bb,转载请注明来自FreeBuf.COM

bbbbbbbbbb.jpg

Freedom Fighting Mode (FFM)

FFM是一款采用Python开发的开源渗透测试工具,广大研究人员可以将FFM用于红队任务的后渗透测试阶段。

研究人员在SSTIC 2018大会上正式公布了这款工具,感兴趣的用户可以点击【这里】查看当时的工具介绍文稿。

工具安装

git clone git://github.com/JusticeRage/FFM.git

工具使用

该工具的主要目的是为了帮助研究人员自动化实现后渗透利用阶段的常规任务,并通过检测目标环境的安全配置来帮助目标用户增强安全保护等级。

运行“./ffm.py“即可激活FFM,接下来我们就可以开始测试任务了。最常用的两个操作命令如下:

1、 输入“!list“命令可查看该工具提供的所有操作命令;

2、 输入“SHIFT+TAB“命令可在远程主机上实现命令补全;

命令列表

1、“!os“:这个命令非常简单,它会执行“cat /etc/*release*”来查看当前设备运行的操作系统版本,以便研究人员了解目标设备的测试环境。通过SSH连接目标设备后,“!os”命令即可使用,插件位于“commands/replacement_commands.py”。

2、“!download [remotefile] [local path]”:可以从远程主机中获取文件,并将其通过命令行工具拷贝到本地。这个命令有些复杂,因为它有更严格的错误检查,插件位于“commands/download_file.py”。需要注意的是,远程主机需要xxd或od来保证功能的正常运行。

3、“!upload [local file][remote path]”:这个命令的功能跟上面的那个差不多,只不过它负责的是将本地文件发送到远程主机中。

4、“!pty”:该命令会生成一个TTY,不过大多数情况下不建议使用,因为它会在目标主机上留下“痕迹“。当然,某些命令(例如sudo)还是需要TTY的。

5、“!py [local script]“:该命令可以在远程主机上执行本地Python脚本,而且全部在内存中运行。

研究人员可以通过编辑ffm.conf来对插件进行自定义配置。

处理器

上述指令可以生成一些bash命令并将其转发至shell,然后根据shell的输出来生成额外指令并进行更加复杂的操作。其中,当用户输入完数据并按下回车键后,输入处理器便会开始执行;当shell返回处理数据后,输出处理器便会运行。

许可证协议

该项目遵循GPL v3开源许可证协议。

工具地址

FFM:【GitHub传送门

*参考来源:JusticeRage,FB小编Alpha_h4ck编译,转载请注明来自FreeBuf.COM

2019年04月17日,国家信息安全漏洞共享平台(CNVD)官方发布安全公告,称Oracle WebLogic wls9-async组件存在反序列化远程命令执行漏洞,攻击者可利用该漏洞,可在未授权的情况下远程执行命令。

随后知道创宇404实验室启动应急流程,通过分析后复现了该漏洞并确定该漏洞影响启用了wls9_async_response.war及wls-wsat.war组件的所有Weblogic版本(包括最新版本),到本预警发布时,官方仍然没有发布对应修复补丁,属于“0day安全”漏洞。

1.jpg

值得注意的是知道创宇旗下创宇盾监控发现该漏洞最早在4月17日存在漏洞扫描痕迹,另外从知道创宇ZoomEye网络空间搜索引擎总共检索到100671条历史数据,其中中国30,600条,主要分布在北京、广东、上海等省市。

2.png

知道创宇404实验室发出紧急漏洞预警,建议所有使用Oracle WebLogic的用户引起重视,注意防范,经过确定知道创宇旗下云安全防御产品“创宇盾”无需升级即可防御该漏洞。

临时解决方案:

方案1:找到并删除wls9_async_response.war、wls-wsat.war,并重启Weblogic服务;

方案2:通过访问策略控制禁止 /_async/* 及 /wls-wsat/* (注意)路径的URL访问;

方案3:启用部署“创宇盾”。

知道创宇404实验室后续还将发布更多漏洞细节和漏洞应急方案,敬请关注。

*本文作者:Knownsec知道创宇,转载请注明来自FreeBuf.COM