安全研究的价值思考

【注】:纯属个人言论,与公司立场无关!

最近的Black Hat大会议题ppt已提供下载,里面有两个非技术议题,其视角比较有趣,一些问题值得思考,因此才有本文。

这两个议题分别是”Project Zero File Years Of Make 0day Hard”和”Selling 0-days to governments and offensive security companies”,一个讲述5年来Google的Project Zero团队在漏洞研究上的工作效果,一个讲述关于漏洞交易的一些现状、流程。

安全研究都干啥

安全研究并不局限于漏洞领域,但它依然是目前最主流的方向,研究范围也可以包括网络安全、反病毒、大数据安全、业务安全等诸多安全领域。这里主要聊下漏洞领域的研究,看看Project Zero的人主要都在干啥:

image-20190818142847773

总结一下就是(主要指漏洞研究领域,估计很多人只干1、3、4的工作):

  1. 漏洞挖掘与利用
  2. 方法论建设
  3. 技术写作
  4. 行业交流与合作
  5. 软件工程化建设,可能是指DevSecOps,包括安全防御策略建设、libfuzzer自动化测试等的应用

###从招聘职责看研究目的

谈研究价值,不妨先来谈谈研究的目的,我特意从各招聘网站上搜集了一些关于安全研究岗位的招聘信息,主要统计其岗位职责描述的关键词,生成如下词云:

职责

可以看到,当前的研究岗位普遍就是招漏洞挖掘职位的,都是搞主流系统及软件为主,但挖洞的目的又是为什么呢,这种在很少企业会写在招聘帖的。从统计结果看,主要有以下3个研究目的:

  1. 挖掘主流系统/软件漏洞;

  2. 帮助安全产品提升检测能力;

  3. 为业务提供技术支持。

第一点是手段,等于啥也没说,第2点算是做安全产品,第3点是得看是什么业务,如第2点也算业务,但如果是一些非安全产品业务,其实所能提供的技术支持就相对比较局限,可能人家业务也不一定看得上你这研究。

影响力价值

搞安全研究,普遍都是为了影响力公关(PR),国内外均是如此,BlackHat上的Pwnie Awards都有一个“最名不副实漏洞奖”,叫做”most over-hyped bug”,就是用来批评那些过度炒作的漏洞。但是一些确实危害比较大的漏洞,及时负责任地披露反而有利于防御工作的开展。Project Zero议题中讲到一句话,开放的攻击研究相对攻击者而言,对防御者更为有利。之前国内试颁行的某提案,因可能阻碍安全研究者公开研究成果,遭到不少圈内人的反对。这跟古时候,有的国家禁止人民用刀一样,最终只是阻碍生产力的发展而已。其实只要不是急功近利,获得与研究成果相匹配的影响力也是合理的。那获得影响力之后的价值呢?对团队,对个人,可能获取得更多交流与学习的机会,也可能获得更多业务合作机会,直接点,可能找份工作防止中年危机都更有资本了。

商业价值

何为商业价值?就是赚钱嘛!通过安全研究落地为产品,然后拿去卖;也可提供技术服务,比如帮助对IoT、车联网产品等新兴行业产品进行安全测试。产品一般比技术服务更值钱,技术服务经常是一波过,产品却是可以长期收费的,比如IDA一年卖几万刀,用户每年都得交钱,而若只是提供逆向服务,费劲且不持久,赚得还少。如果是漏洞交易,高质量的漏洞利用链也是可以获得不菲的利益。在0day漏洞交易感兴趣的,主要涉及以下3类角色:

image-20190818144822263

  1. 防御型企业、漏洞奖励计划和平台
  2. 黑客比赛举办者,类似漏洞收购中间商,自己可能也会去挖洞,也可能收购poc来自己写exploit,或者直接收购exploit,再转手卖出去赚差价
  3. 攻击型企业、政府、黑产团伙

现在国内已经限制参加Pwn2Own之类的国外比赛,从2018年开始,国内由”天府杯”比赛代替,主要是防止漏洞外流危害国家网络安全。这些比赛也推动了厂商对漏洞处理的态度,以及漏洞奖励计划的建设,使得报告者能够合法地获得相应的奖励和认可。

新型业务安全预防

提前研究一些公司业务可能涉足的新领域,避免新兴业务产品出来后,无能力解决上面的安全问题。但整个的前提是,该新产品能活下来,否则一切都是白搭。

行业贡献

在安全行业贡献榜上,Project Zero无疑是佼佼者。在5年内,他们共贡献1500+个主流系统/软件漏洞,推动很多安全防御机制的诞生,甚至影响漏洞在市场上的价格。漏洞研究者有时担心手上的漏洞被撞掉,会直接报给厂商,混个致谢,搞不好年底还能混个”MSRC Top 100”,今年开始它改名叫”最具价值安全研究员”,更高大上了。这个月,我就被Project Zero的人撞掉了一个微软漏洞。在PZ分享的议题里面,提出一些衡量”make 0day hard”的标准,但毕竟是相对概念,仅当作参考:

  1. 挖到品相优秀的漏洞所花费的时间;

  2. 漏洞平均生存时间;

  3. 撞洞数量;

  4. 漏洞利用链的长度;

  5. 新型高质量的攻击面的发现概率。

个人保值防老

研究本身就是一种学习方式。相信爱学习的人,最终运气都不会太差。尤其是现在鼓吹35岁中年危机的互联网时代,保持学习是最靠谱的个人保值防老方式。如果保持工作内容不变,那么通常头一年所积累的技术与工作方法足够应付绝大部分工作。若再不搞点有挑战的新工作内容,或者业余做点研究,那就要成为拿着一套技术吃N年的”老白兔”了。持续学习,保持或者超越与年龄相符的技术能力才是王道。

一些值得学习的Fuzzer开源项目

之前GitHub上有人整理过一个叫Awesome-Fuzzing的资料,整理了关于Fuzzing技术的电子书、视频、工具、教程以及用于练习的漏洞程序。整体上不错,但工具上还是不够全,有些不错且希望阅读代码学习的工具,发现未在其中,因此重新整理出下面这一份资源,其中有些还曾二次开发过,有些是还未来得及学习的,写出来权且当作学习计划。

  1. AFL——支持源码插桩的代码覆盖引导的Fuzzer,绝对是fuzzer领域的一大里程碑,虽然它也支持基于QEMU的闭源程序,但效果不好,且容易出错,由它衍生出来非常多afl分支版本,借助它已经被挖出非常多的漏洞,但它的变异策略其实有待提高。

    http://lcamtuf.coredump.cx/afl/

  2. WinAFL——windows版本的afl,使用DynamoRIO去插桩闭源程序以获取代码覆盖率信息,同时支持硬件PT获取覆盖率信息,但PT获取覆盖率其实并没有插桩获取得全,但速度可能会快一些。

    https://github.com/googleprojectzero/winafl

  3. AFLFast——加速版的AFL,Fuzzing速度确实会比原版快一些。

    https://github.com/mboehme/aflfast

  4. Vuzzer——支持闭源程序的覆盖引导Fuzzer,使用LibDFT的pin工具实现数据流追踪,结合动静态分析,以获取更多的代码路径,比如比较语句中的比较值,它会先作记录,再未来变异时使用。

    https://github.com/vusec/vuzzer

  5. PTfuzzer——Linux平台下的采用 Interl PT硬件支持的覆盖引导Fuzzer,所以它支持闭源程序。

    https://github.com/hunter-ht-2018/ptfuzzer

  6. afl-unicorn——采用Unicorn模拟指令的AFL,支持Linux闭源程序

    https://github.com/tigerpuma/Afl_unicorn

  7. pe-afl——通过静态插桩实现针对Windows闭源程序的覆盖引导的AFL Fuzzer,支持用户层应用和内核驱动

    https://github.com/wmliang/pe-afl

  8. kAFL——支持QEMU虚拟机下的系统内核Fuzzing的AFL,适用于Linux、macOS与Windows

    https://github.com/RUB-SysSec/kAFL/

  9. TriforceAFL——基于QEMU全系统模拟的AFL,借助系统仿真器实现分支信息跟踪,支持Linux内核Fuzzing

    https://github.com/nccgroup/TriforceAFL

  10. ClusterFuzzer——Google开源的可扩展的Fuzzing基础设施

    https://github.com/google/clusterfuzz

  11. LibFuzzer——进程内覆盖率引导的开源的fuzz引擎库,属于llvm的一部分,在各大主流开源库中,以及Google内部最经常用的安全测试工具

    https://llvm.org/docs/LibFuzzer.html

  12. OSS-Fuzz——基于LibFuzzer的开源软件Fuzzer集合,实现docker下自动下载、编译安装及运行

    https://github.com/google/oss-fuzz

  13. honggfuzz——Google开发的基于软硬件的覆盖驱动型Fuzzer,单纯暴力Fuzz的效果也挺好的,支持多平台,包括Linux\macOS\Windows\Android

    https://github.com/google/honggfuzz

  14. KernelFuzzer——跨平台内核Fuzzer框架,不开源策略,只在其paper中提及变异策略,需要自己实现,支持Windows、OSX和QNX系统,但只提供Windows编译脚本

    https://github.com/mwrlabs/KernelFuzzer

  15. OSXFuzzer——基于Kernel Fuzzer的macOS内核Fuzzer

    https://github.com/mwrlabs/OSXFuzz.git

  16. PassiveFuzzFrameworkOSX——通过Hook实现被动式的OSX内核Fuzzer

    https://github.com/SilverMoonSecurity/PassiveFuzzFrameworkOSX

  17. Bochspwn——基于Boch插桩API实现Double Fetches内核漏洞的检测

    https://github.com/googleprojectzero/bochspwn

  18. Bochspwn-reloaded——基于Boch插桩API实现内核信息泄露的检测

    https://github.com/googleprojectzero/bochspwn-reloaded

  19. syzkaller——基于覆盖率引导的Linux内核Fuzzer,需要基于其模板语法实现API调用模板,提供给syzkaller进行数据变异,也曾被移植到其它平台

    https://github.com/google/syzkaller

  20. dharma——基于语法模板生成的Fuzzer,由Mozilla开源的用于Fuzz Firefox JS引擎

    https://github.com/MozillaSecurity/dharma

  21. domator——Project Zero团队开源的DOM Fuzzer,用python实现基于模板生成的Fuzzer

    https://github.com/googleprojectzero/domato

  22. Fuzzilli——基于语法变异的JavaScript引擎Fuzzer,先通过语法模板生成测试用例,再生成中间语法进行变异,结合覆盖率引导以触发更多代码路径

    https://github.com/googleprojectzero/fuzzilli

  23. Razzer——内核竞争条件漏洞Fuzzer

    https://github.com/compsec-snu/razzer

  24. ViridianFuzzer——用于Fuzzing Hyper-V hypercalls的内核驱动,由MWRLabs公司出品

    https://github.com/mwrlabs/ViridianFuzzer

  25. ChromeFuzzer——基于grinder语法生成器改装的Chrome浏览器Fuzzer

    https://github.com/demi6od/ChromeFuzzer

  26. funfuzz——Mozilla开源的JS fuzzer工具集合,主要用于Fuzz SpiderMonkey

    https://github.com/MozillaSecurity/funfuzz

Infiltrate2019议题学习

Infiltrate2019安全大会是在5月初举办的,会议资料收集后放在电脑上1个多月了,连续几个周末都有事,一直没来得及学习,今天刚好学习下,有些议题其实跟MOSEC上有重复。

重点聊几个个人感兴趣的议题,并最后附上10个议题ppt资料下载。

2PAC 2Furious Envisioning an iOS

科恩出品,分两部分:PAC绕过与基带研究,刚好在MOSEC上project zero的人讲了5种PAC绕过方法,议题名叫”A study in PAC”,涵盖了其中的方法,而基带研究部分也作为独立议题在MOSEC上分享过,介绍 基带攻击方法、逆向分析固件的方法。

之前在MOSEC上,我对5种PAC绕过方法作了学习笔记,直接上图:

image-20190629104044528

image-20190629104108330

image-20190629104158997

image-20190629104215919

image-20190629104249591

EL3 Tour - Get The Ultimate Privilege of Android Phone

盘古出品,拿华为P20开刀,应该是手工逆向分析TEE相关代码,挖到一个代码执行漏洞攻击EL3的过程。

image-20190629104554838

通过VBAR_EL+0x400的异常处理例程来定位SMC处理例程:

image-20190629104757284

漏洞代码:

image-20190629104832507

对方的漏洞利用思路:

  1. 通过漏洞实现任意内存读写

  2. 布署 Shellcode 于地址 0x209F8000(EL1下可访问,属于共享内存)

  3. 篡改 Page Descriptior : 0x209F8627 => 0x209F8783(可执行)

  4. TLBI ALLEL3:清除TLB缓存,保持数据一致,使页表修改可被CPU感知到

  5. 调用 0x209F8000,触发shellcode执行

最后演示如何利用该漏洞绕过华为手机的人脸验证,包括篡改人脸匹配分值、活体检测结果。

Adventures in Video Conferencing

Project Zero以前在其博客上分享过,看博文会更清晰一些,详见:

Adventures in Video Conferencing Part 1: The Wild World of WebRTC

Adventures in Video Conferencing Part 2: Fun with FaceTime

Adventures in Video Conferencing Part 3: The Even Wilder World of WhatsApp

Adventures in Video Conferencing Part 4: What Didn’t Work Out with WhatsApp

Adventures in Video Conferencing Part 5: Where Do We Go from Here?

Natalie Silvanovich 作为PZ的头牌女黑客,在此议题的厉害之处就是用了几行fuzz代码挖了包括浏览器、FaceTime、WhatsApp在内的主流应用10多个CVE远程漏洞。就是下面这段代码:

image-20190629110057733

通过分析视频交互过程,找到外部数据传递的关键点,开源的改代码插入fuzz,闭源的写Hook去实现fuzz,相关的工具也已在GitHub上开源:https://github.com/googleprojectzero/Street-Party。之前看到国内也有人顺势搞到几个FaceTime的漏洞。

TEE Exploitation: Exploiting Trusted Apps on Samsung’s TEE

Blue Frost Security出品,举了几个三星漏洞的例子:

  1. TA的栈溢出案例:由于只有NX(没有栈保护和ASLR),所以直接上ROP搞定的
  2. 共享内存Double Fectch漏洞:TA在验证和使用命令数据的时间窗口内,可能被篡改数据,实现任意读写

image-20190629112004922

由于缺乏一些常见的内存保护机制(仅有NX),在TA利用上反而更加容易。TA攻破后,对于厂商最大的影响可能是DRM版权与支付密钥等问题;而对于用户而言,主要是用户数据的窃取问题。

资料打包下载

下载链接:https://github.com/riusksk/SecConArchive/tree/master/Infiltrate2019

image-20190629114646339

2019年哪些安全大会的议题值得学习

“2019年哪些安全大会值得参加?”或者这更符合多数人心中的标题,但为何不这么写呢?

因为有些拥有好议题的大会一般都会公开PPT,尤其是国外会议,来回参会成本比较高,如果有现成的PPT供学习,自然不用每次都参加。当然,也有因作者拒绝公开的议题,这种只能现场听了。

评价安全大会的好坏,是多方面的,绝不是单纯的议题质量这一维度。但这里我主要想从技术者的角度来看评价,所以后面你发现很多知名大会未在此列,请不要惊讶。

即使是同一举办方,也无法保证每年的议题质量呈上升状态,有些会议也开始没落了,所以这里以2019年为时间点来点评。

下面来聊聊2019年哪些安全大会的议题值得学习,有些已经举办过,有些尚未开始。

推荐的安全会议

1、BlackHat

image-20190511125059375

官网https://www.blackhat.com

如果你不知道BlackHat,说明你不在安全圈混。

USA是主会场,议题质量和数量也是最高的,议题类型覆盖面也很广,除此之外还有欧洲和亚洲等分会场,质量相对次一些。

这次BlackHat USA的议题也陆续公开了:https://www.blackhat.com/us-19/briefings/schedule/index.html

早几年的议题水平参差不齐,很水的也有,打广告也有。最近几年反而议题质量提高,不少华人面孔出现,为了PR效果而竞争,促进大家都拿出干货来分享,这也是其有利的一面。

每年都有几千个议题投稿,竞争很大,但这很好地促进议题质量的提高。

每年会后,官方都会放出PPT与视频,非常开放地分享知识。

所以,首推BlackHat,自然无疑。

但如果你以为接下我会写Defcon,那我会告诉你:No!

2、OffensiveCon

offensivecon

官网https://www.offensivecon.org/

我之前还专门写了篇文章《今年的OffensiveCon大会议题质量不错》介绍2019年大会中一些不错的议题。

虽然OffensiveCon是从2018年才开始举办的,但议题质量一直保持不错,演讲者中包括Project Zero、Google syzkaller作者、Pwn2Own与Hack2Win获奖者等等。

会后,一般是由演讲者选择是否公开ppt,多数人是在Twitter上公开的,官网上我没找到资源(https://github.com/riusksk/SecConArchive/tree/master/OffensiveCon2019),所以之前收集的ppt都是从twitter上扒下来的。

3、HITB (Hack In The Box)

image-20190511133408850

官网:https://conference.hitb.org/

这几天HITB刚在荷兰阿姆斯特丹举办完,议题PPT也一并公开(https://conference.hitb.org/hitbsecconf2019ams/materials/)。

如果要说国内外安全会议中,哪个公开PPT最快的,一定是HITB,他们一般是现场演讲完,就直接扔官网下载。然后过一段时间,也同样发布演讲视频。

他们有时会同时搞两个演讲会场,一个是收费的主会场,议题质量高一些,一个是免费的,叫CommSec,用来提携新人,议题质量相对比较次,每个议题分享时间也比较短,最多半小时。

之前去新加坡参加过一次HITB,人数不多,场地也不大,但可以感受到与国内安全会议的区别:更注重技术交流,而非搞关系。

2018年开始,HITB也开始与京东合作,在北京举办分会场,没去过,不作评价,但国际会议本土化,总会产生一些差异的。

4、InfiltrateCon

image-20190511134410680

官网:https://infiltratecon.com

从2011年开始举办的,已经走过8个年头。

看了今年的议题,还是有干货的,但只有4个议题ppt在twitter上公开。

以前,会后都会在官网上公开PPT和视频,但目前官方还没公开。

今年的议题涉及Chrome RCE、iOS与Android提权、Pwn TEE、浏览器JS Fuzzing等等,只能坐等官方公开PPT了。

5、Chaos Communication Congress(C3)

image-20190511141920353

官网:https://www.ccc.de/

德国混淆黑客大会,常叫C3会议,常在C3前面加上第几届,比如今年第35届,所以叫35C3,历史非常悠久。

以前大多是聚焦在无线电安全,所以一些什么2G\3G\4G短信、电话窃听经常出自该会议。熟悉无线电安全的同学,应该都听过。2018年也有一些不错的软件安全相关的议题,这些在之前写的文章《推荐今年C3黑客大会上的几个议题》介绍过了。

除了大会议题,不得不提下他们的CTF,非常具有实战价值,比如2018年的题目,直接拿pwn2own漏洞当比赛,从safari代码执行到提权,还有VisualBox沙盒逃逸题目,需要利用到0Dday,出题者是ProjectZero的人,早就将其卖给ZDI,刷了不少VBox漏洞。这些CTF题目在网上都有相应的WriteUp可供学习。

这些议题只有演讲视频公开,没有PPT,官方会放在https://media/ccc.de,可在线或下载观看。

都是在每年的12月份举办,2019的还有半年呢……

6、CanSecWest

image-20190511143022033

官网:https://cansecwest.com

CanSecWest都是与Pwn2Own一块出现的,以前议题PPT都是放在https://www.slideshare.net/上分享,但从2018年开始又不搞了。

每年议题不多,但质量还是可以的,不过感觉这两年的质量略有下降。

今年3月的议题也没看到有下载,也是混Twitter找ppt的,只看了《vs com.apple.security.sandbox》这个议题,今年我感兴趣的议题没几个,大家根据自己喜好选择吧。

如果你各个议题PPT,也欢迎分享下。

7、MOSEC 移动安全技术峰会

image-20190511145201148

官网:http://mosec.org

MOSEC是从2015年开始举办的,由盘古与韩国POC联合举办,聚集移动安全领域,包括Android、iOS、IoT以及无线电等领域。虽然起步晚,但议题干货满满的,应该是目前国内最好的安全会议了。

今年的议题也已经陆续公开了,包括iOS越狱、Android提权、LTE、基带、卫星系统等等。

官网是不公开大会的议题PPT,由演讲者选择,所以想学习的同学,可能还是得去参会。

从2015年第一届我就开始参加,本月底还会去。去年参会,早上出酒店一辆车都打不到,又不在地铁口,最后骑了1个多小时的单车到会场,不容易啊……

8、POC

img

官网:http://powerofcommunity.net/

POC(PowerOfCommunity)起始于2006年,在韩国举办的。单从议题质量看,确实不错,大多漏洞研究领域的前沿技术,但它经常是”二手货”,也就是在其它安全会议讲过后,但去韩国观光旅游顺便讲下。

还有个意思的现象就是,每年的议题超过一半是中国人讲的。

所以,你推荐也对,你不推荐也没错。

不过,有个好处就是,POC议题PPT都是提供下载的。有时在其它会议找不到PPT时,到POC官网翻下,偶有小惊喜。

另外还有个会议叫ZeroNights,同一举办方,更多是面向老外的。

那些未提及的知名大会

1、Defcon

很多时候,Defcon议题都是BlackHat挑剩的,有的人也会直接议题双投。上面的议题质量更是参差不齐,相对BlackHat要求更低,更开放。我很少看Defcon议题,偶而网上有人发才看。

2、RSA

RSA是一个充满商业气息的大会,如果你看过官网的PPT,就会发现里面充满诸多广告,有的议题可能就几页图片,所以从技术角度来看,是没有多少学习的价值。

但是,RSA有时也反应出的安全的风向标,虽有炒作的成分,但显然PR得甚是成功。比如当年的APT、数据可视化、威胁情报等等

RSA的创新沙盒是一项不错的活动,很多创业公司把他们研发的新产品拿出来比赛,从中可以反映出一些行业发展的方向。

所以,RSA比较适合管理者、创业者以及产品运营者。

3、XCon

以前国内安全会议很少,基本唯XCon为首。以前参加都是为了跟圈内朋友相聚聊天,有时场外比场内还热闹。

但这几年开始,XCon逐渐没落了。如果你参加过XCon2018,相信会有很大的体会,会场已经没几个人,有时在场人数可能达到个位数,找个同行聊天都难,且门票还是国内同类会议最贵的。

4、KCon

KCon应该算是国内比较有自己特色的会议,2018年的议题质量也还可以,中场休息的摇滚音乐很赞,场地与音效很好。

之前几届的议题质量忽上忽下,2019年的议题还没出来,大家可以关注下先。

若是在2018年,我还是会给个推荐的。

5、BlueHat

以前微软的闭门邀请制会议,从今年开始在上海举办国内版,议题列表已经放出,感觉质量一般。但跟MOSEC时间联着,可以考虑一并参加下。

6、RECON

因为多数议题自己不感兴趣,它比较偏向于逆向工程,以及系统底层、硬件、固件等方向,对此方向感兴趣的话,依然可以看看。

7、Syscan/Syscan360

官网已经打不开了,聊啥……

后话

评价一个会议的好坏真是很容易,但要举办一个好的会议却是不容易,影响的因素特别多,且非一人之力可以搞定。

无论最终质量如何,对于为行业提供沟通交流平台的一些会议,还是值得点赞的。

读《一本小小的蓝色逻辑书》:识别常见的逻辑漏洞

最近读了一本书叫《一本小小的蓝色逻辑书》,算是逻辑推理入门书籍,觉得不错,推荐给大家。

这本书在微信读书上可以找到,大概需要4个多小时的阅读时间。

img

什么是逻辑推理

在生活、学习与工作中,我们总是要运用到逻辑推理能力,甚至我们自己也经常挂在嘴边,但若问什么是逻辑推理呢,估计没多少人能说清。

所谓”逻辑推理”,在广义上被定义为”我们评估信息的过程”。要想做出正确的决定,我们首先要占有充分的信息,而要想占有充分的信息,就必须提出正确的问题。所以那些擅长逻辑推理的人,往往也比较善于提出问题,搜集相关信息,用”正确的”方式对这些信息进行评估。最重要提,他们可以在不受他人干扰的情况下独立完成这一过程。

在我记忆中,整个学生时代,几乎没有过这门课程,大部分的逻辑推理能力都是基于以往的中学数学课程训练,比如真假命题、逆命题、证明题等等,本书中也有讲到这些。

也许我们的日常数学真的只需要加减乘除的运算,但以前的数学课程培养出来的逻辑思维,却可以运用一生。

推翻前提找答案

这里说的”推翻前提找答案”,其实是想说”水平思考法“,一种摆脱前提设想而进行创意思考的方式,不走寻常路,换个角度看待问题,而不是接受他人提出的前提条件。我们多数人一般都是使用“垂直思考法”,却沿着原定的逻辑路线思考下去,就是我们俗语常说的“直脑筋”,多少略带有点贬义。

下面是两种思考方法的对比表:

image-20190503101519293

可能还是太抽象了,因此作者讲了一个故事:

许多年前,一个倒霉的商人欠了别人一大笔钱。由于没钱还债,商人很可能会被债主投进大牢。

债主是个脾气又坏又丑的糟老头,但他却看上了商人年轻貌美的女儿。于是他告诉商人:”我有个办法,不仅可以把你的债务一笔勾销,还能让你的女儿免于因为你入狱而流落街头。”

具体办法是:债主把一黑一白的小石头放进空袋子,让商人女儿摸一块。如果摸到白石头,则她父亲的债一笔勾销,她也无须嫁给债主;如果摸到的是黑石头,债务仍然可以一笔勾销,但她必须嫁给债主。如果她不答应这个游戏,那么她父亲会被立刻投进监狱。

商人父女别无选择,只好答应。

于是三人来到债主花园内铺满鹅卵石的小路上,债主俯身捡两块黑石头扔进袋子里,他自以为神不知鬼不觉,却不知这一切都被商人女儿看在眼里。

如果是个”直脑筋”的人,可能就想到下面两种做法:

  1. 当场揭穿糟老头的阴谋,然后商人进监狱;
  2. 女儿认命,抽到黑石头,嫁给糟老头,债务一笔勾销。

最后的结局是这样:

商人的女儿摸出一块石头,但故意把它掉到地上,跟一堆鹅卵石混到一起。然后一边假装寻找石头,一边若有所思地说道,”但没关系,只要看看袋子里的那块石头是什么颜色,就可以判断我刚才摸出的那块石头是什么颜色了。”

债主一时愣住了,不知道该说什么,只好让那个女孩拿出袋子里的石头,结果可想而知。

这就是水平思考法,我们可以回顾下这个故事,若要想免债的话,其中的”前提条件”是:

前提条件:从袋子中摸出白石头。

现在通过改变该前提条件来思考,比如这样:

  1. 从袋子中摸出石头:现场改变规则,摸出黑石头可免债。
  2. 摸出石头后,根据剩下的石头颜色来判断是否摸到的是白石头,正如故事中所做的。

日常生活中,阻碍我们进行创意思考的是,不假思索的程序性反应,就是不费脑子的事情,比如商店购物、开车等等,但有时遇到一些新情况,这些程序性反应就不灵了,这时就需要启动非程序性反应。

书中还给了一道训练”水平思考法”的题目,大家可以先试着做下,一开始我也没做出来(答案见文末附录):

用最多4条直线(笔尖不离纸)把下面的9个点连接起来。

image-20190503105014426

还有另一道题,是当年面试微信支付时被问到的类似题目,但微信的更难一点(拿3个桶倒出想要的重量),答案亦见文末附录:

马戏团老板派小丑去附近河边打水给大象喝。因为想在里面加入一种特殊健康浓缩剂。所以需要整整7加仑水,不能多,也不能少。他给了小丑两个水桶,一个5加仑,一个3加仑,让小丑去打整整7加仑水。请问小丑该怎么办?

效用概率做决策

如果大家经常逛知乎的话,会发现很多人在问:

  1. 做安全需不需要考研?
  2. 选择什么样的学校和专业好就业?
  3. 选择什么样的职业更适合自己?
  4. 其它…….

相信很多人做决策的时候,多会先分析出各项选择的优缺点再打分对比,选择出最佳方案,这叫利弊分析法

有时,我们又会先列出在意的点,根据重要程度作个加权值,然后给个选择打分,根据分数高低来排序选择,这叫加权排序法

还有决策法、矩阵分析法、概率树等等多种决策分析方法,在我们在决策时,能够给我们提供很大的帮助。

不过我在这里,重点是想介绍下效用分析法,即分析某个结果对我们的价值,通常跟概率一块使用。

打个比方,一名大四学生在规划自己的人生。摆在他面前的有三种选择:

  1. 成为旅行作家;
  2. 加入外交部;
  3. 成为公司销售人员。

这里肯定不能只从金钱回报来考虑这个问题,因为这名学生真正看重的并不是赚多少钱,而是自己从事这份工作时的内心感受。

若是以前,我可能会列出收入、职业前景、兴趣、工作环境等多个维度来考虑。但是某些场景下,我们常常忽略实现这一结果的概率,比如我想当皇帝,这种不是靠努力就能实现的。

因此这里最好的办法就是去计算每份职业的期望值(Expected Value, 简称EV)。EV计算公式:

EV = 效用(某种结果带给我们的心理满足度) x 出现这种结果的概率

根据上述公式,我们得到:

image-20190503120444342

这里每种结果存在实现概率,是因为该结果要求一定的技能,而这名学生此时并不完全具备这些技能。

根据上面的分析,该学生选择加入外部部的期望值最高,所以理性地说,他应该选择这份工作。

五大常见推理漏洞

通常说的推理漏洞,大多是指那些跟我们所做假设相关的漏洞。书中列举出五大常见推理漏洞:

  1. 比较和类比假设漏洞:偷换概念

把两个虽然不同,但逻辑上却相等的事物进行对比。

比如拿橘子和苹果作比较。再比如说,医学院校经常拿小白鼠做实验,然后把在动物身上得到的实验结果当作参考,但是若因小白鼠身上实验某种药物时发生某种并发症,就认为人类在使用这种药物时也会出现同样的并发症,就是错误的。

  1. 代表性假设漏洞:以偏概全

不能拿特殊案例来代表整体,统计的样本要足够多才行,否则它就会弱化我们的论断。

比如《思考,快与慢》中曾举过一个例子:

最近,某大医院出生婴儿1000人, 某小医院出生婴儿50人, 问哪家医院生男婴的比例大于60%的可能性较大?

我们都知道生男生女的概率分别是50%,统计样本越多就越会接进这个数值,但如果你若去小医院,它的波动概率就很大,可能生男80%,也可以40%,所以小医院生男婴的比例就越有可能大于60%。

  1. “好证据”假设漏洞:对相关证据视而不见

当我们不经验证就想当然地认为自己的证据有效时,就很容易犯此错误。那些比较客观、相关、精确、真实的证据有利于强化我们的论述;而主观、不具代表性、不精确的论据则只会弱化我们的论述。

比如,一个不愿意戒烟的人总是会看到吸烟有利的一面,而对那些支持戒烟的事实会视而不见。

  1. 因果假设漏洞:混淆因果关系

当我们错误地做出因果假设,或者在没有证据的情况下就认定一件事会导致另外一件事时,就会犯这种错误。

比如,每个活过百岁的人都喝过白开水,所以就认定经常喝白开水就能长命百岁,这显然是错误,它们不存在直接的因果关系。很多学术界的社会/生物健康调查相关的报导就经常出现这种错误。

  1. 实施假设漏洞:在执行计划时没有提前考虑可能出现的瓶颈

当我们没有预料到计划实施过程可能出一的瓶颈,或者盲目地相信自己的计划会轻而易举地得到落实时,我们就会犯这种错误。

比如,几年前,西方某旅行杂志曾登过一篇文章说:”因为如今搭乘飞机很方便,而且人们手头余钱也越来越多,所以很快大家都会去非洲看狮子了。”

这显然就是错误的,去不去非洲看狮子,并非单纯考虑金钱和交通就行,比如先问问你有没有年假再说吧!

识别常见的逻辑漏洞

根据书中列举的常见逻辑漏洞,我画了张思维导图:

常见逻辑漏洞识别

附录

1、9点连线的答案:多数人会受限于前提条件:只能在9个点内形成的长方形之内画线,如果能够摆脱该前提条件,那么答案就会有很多种:

image-20190503110749773image-20190503113506286image-20190503113329718

2、水桶题目的答案:先倒满5加仑的水桶,再把它倒进3加仑水桶,把3加仑水桶里的水倒掉,把5加仑水桶里剩下的2加仑水倒进3加仑水桶里,重新装满5加仑水桶(5加仑 + 2加仑 = 7加仓)。

RSS: 优秀的个人情报来源

早些年,关注了一些技术博客,但不知道它是否更新文章,就只好偶而去翻翻看。关注的博客量少的时候,还应付得来,一旦多了,就觉得甚是费时间。

后来才发现有RSS(聚合内容)这款神器,大大地节省时间,不用再不停地翻看别人的博客,一有更新,在自己的订阅网站或app上就可以查看到。

image-20190330095736471

以前用Google Reader,现在用Inoreader(https://www.inoreader.com),还提供手机APP,特别适合利用碎片时间查看和学习。

我现在基本保持每天翻看,一定要把未读消息消灭掉。

由于多年订阅源的收集积累,每天都有不少更新的消息,即使前天刚把失效的订阅源清理掉,也还有700多个。

以前曾看到某同事的RSS订阅,上千条未读消息,平时看得少,结果越堆越多,导致未能发挥RSS应有的价值。

我觉得RSS应该是每个想保持学习进步的人应该必备的工具。

对于我个人而言,RSS有以下作用:

  1. 应急响应:及时获取外部曝光的漏洞,包括公司产品漏洞,以及可能影响公司产品的第三方通用组件/开源项目漏洞,以便能够及时响应处理;
  2. 刷CVE:及时获知某些主流软件的攻击面,或者一些漏洞挖掘技巧,然后主动尝试去挖掘。以前有不少人在乌云上曝光某一通用漏洞,就经常有一大堆去刷SRC,或者在乌云上不停地刷别人的漏洞,这种行为我觉得挺无聊的。这里刷CVE主要是指Microsoft、Apple、Adobe等一系列主流厂商的产品的0day,而不是以往乌云上这种相同漏洞在不同平台刷洞的行为。
  3. 技术与工具的收集:包括技术文章和工具的收集与学习,对于好的工具,会下载学习其源码,并应用实践;对于好的文章,会保存到印象笔记,方便以后查询复习。
  4. 安全资讯:看看一些发安全界发生哪些安全事件,比如入侵事件、facebook信息泄露事件等等,也学习下别人如何就应对此类事件。除此之外,当然也包括安全界的一些八卦。
  5. 新书资讯:专门订阅一些出版社的相关博客/官网,以便能够及时获取即将出版的新书。
  6. 其它:更多的用途靠自己去挖掘,毕竟每个人的期望的目标不一样。

对于那些未提供RSS功能,又非常不错的网站,推荐使用Feed43(https://feed43.com/feed.html?action=new)自定义规则来生成RSS,可直接导入到Inoreader,使用教程参考:《利用 Feed43,将任意网页制作成 RSS 订阅源》https://sspai.com/post/34320

最后分享一下个人收集的RSS,共731个,Inoreader免费版最多支持500个,不想付费的可以找下其它免费RSS工具,或者选择部分订阅。

下载地址:http://riusksk.me/media/riusksk_RSS_20190330.xml

image-20190330103252935

今年的OffensiveCon大会议题质量不错

年前曾在微博上推荐过OffensiveCon 2019大会议题,议题列表与介绍可参见官网(https://www.offensivecon.org/agenda/),很多专注于漏洞挖掘与利用的干货分享,目前只有部分议题ppt公开,文末附打包下载链接(包含8个议题),包括ppt、paper和code。

会议结束后,Twitter上赞声一片,议题质量很赞。

本文主要聊聊已公开的一些议题,学习下思路。

议题列表

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    1. Modern Source Fuzzing
    2. IPC You Outside the Sandbox: One bug to Rule the Chrome Broker
    3. 3D Accelerated Exploitation
    4. Bugs so Nice they Patched them Twice! A (Continuing)? Story About Failed Patches
    5. Attacking Hardware Root of Trust from UEFI Firmware
    6. OSX XPC Revisited - 3rd Party Application Flaws
    7. Growing Hypervisor 0day with Hyperseed
    8. Attacking Edge Through the JavaScript Just-In-Time compiler
    9. Coverage-Guided USB Fuzzing with Syzkaller
    10. Updated Analysis of PatchGuard on Windows RS4: Is the Mouse Finally Caught?
    11. iOS Dual Booting Demystified
    12. macOS: How to Gain Root with CVE-2018-4193 in < 10s
    13. Reverse Engineering of Error-Correcting Codes
    14. Glitch in the Matrix: Exploiting Bitcoin Hardware Wallets
    15. Attack Surface of a Connected Vehicle
    16. Bypass Windows Exploit Guard ASR
    17. FuzzIL: Guided Fuzzing for JavaScript Engines

Modern Source Fuzzing

这是作者Ned Willliamson在353c大会上的《Attack Chrome IPC》议题的扩展补充,我之前还写过《安全研究者的自我修养》一文,里面介绍的就是作者提及的二进制漏洞研究的学习思路。

目前作者没公开这次会议的ppt,大家还是看353C的演讲视频吧:https://media.ccc.de/v/35c3-9579-attacking_chrome_ipc

##IPC You Outside the Sandbox: One bug to Rule the Chrome Broker

作者已经在github上公布此漏洞的利用代码hack2win-chrome,点击”阅读原文“可下载到。

本议题讲的是Chrome沙盒逃逸漏洞,漏洞位于应用缓存(AppCache)子系统上,主要方便从本地读取数据进行离线浏览,访问速度更快,还能缓解服务器压力。

img

AppCache位于沙盒之外的高权限进程browser,沙盒内低权限的renderer进程通过发送IPC消息与browser进程交互的,当AppCache出漏洞时,就有可能逃逸出沙盒。

漏洞成因

这次的ppt写得比较模糊,没那么清楚,还是直接看patch diff:

image-20190223095905435

移动CancelUpdate()函数到newest_complete_cache_=nullptr;之后,直接看看CancelUpdate里面的逻辑:

img

在调用AppCacheGroup::RemoveCache清除缓存时,newest_complete_cache_指向的是被销毁的对象,所以后面才要把它置空,但在销毁之前调用了CancelUpdate =>

AppCacheUPdateJob::~AppCacheUpdateJob => AppCacheGroup::SetUpdateAppCacheStatus => AppCacheHost::OnupdateComplete =>

SetSwappableCache

最后的SetSwappableCache用于设置新的交换缓存(swap cache),会引用到newest_complete_cache_,而此时它还未被置NULL,导致出现Use After Free漏洞。

####漏洞利用:

  1. 【泄露地址】:使用与AppCache对象大小相同的net::CanonicalCookie对象来占用释放对象的内存,而CanonicalCookie对象开头是个cookie名称,即字符串指针,再从浏览器中读取cookie信息来达到信息泄露的目的,从而拿到可控数据的堆地址绕过ASLR。

  2. 【代码执行】:使用与AppCache对象大小相同的Blob对象对占用释放内存,再伪造AppCacheGroup对象,当它析构释放时,在~AppCacheGroup中会调用到已被填充控制的虚函数指针,再结合ROP绕过DEP,从而达到代码执行。

整个过程还是需要自己动手调试一遍才比较清楚,估计足够调上几天了,国内似乎也没有一遍完整的文章分析过该漏洞的利用细节,期待有人分享。

3D Accelerated Exploitation

image-20190223123509759

image-20190223123527727

该议题主要介绍VirsualBox 3D加速器的攻击面和漏洞利用,由于VBox是开源的,因此可以直接使用AFL 去Fuzzing,fuzz目标就是通过发送畸形chromium messages来触发漏洞。他们应该是自己写个构造发送消息的程序,输入文件即chromium messages内容,样本可能是收集550操作码的信息去构造,也可能通过hook去直接抓取真实数据作为样本,然后用 afl去跑。更具体的实现方式,作者也没细说。

MWR Labs这几年经常曝光一些Pwn2Own级别的漏洞,分享很多经典文章,还开源了不少Fuzzer工具,连ppt都做得非常工整,具有独特风格,哪怕没logo,你看一眼都能猜出是他们写的。具备牛X的技能能力,又乐分享,这点是比较难得的。

Attacking Edge Through the JavaScript Just-In-Time compiler

一直以来,chakra被曝的漏洞非常多,导致微软最终还是放弃了。

从今年开始,微软将打算把Edge的Chakra引擎改用Google Chromium引擎,估计最近这两个月就会发布,以后就可能没什么人再搞Chakra内核了。

这议题里面讲了很多chakra的js对象内存结构等基础知识,重点讲了JIT优化编译器的漏洞原理与利用技巧,整个ppt有120页,很多。

我没搞过chakra,未来可能也用不上了,有兴趣的同学可以看下,作者把exploit代码也公布了,我已附在本文的打包资料里面。

Coverage-Guided USB Fuzzing with Syzkaller

image-20190223130009940

搞过Linux/Android内核漏洞挖掘的人,应该都知道Syzkaller这款神器,发现超过2500个内核bug,它是基于代码覆盖率+API调用模板来Fuzzing内核的工具,对于发现崩溃的漏洞,还能自动生成C代码帮助复现,是由Google的Dmitry Vyukov开发的,已在Github上开源多年(https://github.com/google/syzkaller)。

这次作者用syzkaller fuzz USB驱动共发现了80+个bug,它先开启kcov去收集代码覆盖率信息,写了两个usb操作的描述模板(vusb.txt用来生成usb消息,vusb_ids.txt用于提取与USB设备驱动相匹配的USB ID列表),ppt里面有链接,所有的usb fuzzer代码都已经嵌入到syzkaller项目里面了

image-20190223130949191

整个syzkaller的使用过程就是先去寻找内核的攻击面,然后构造api调用模板,剩下交由syzkaller基于代码覆盖驱动的方式去Fuzzing,有点类似api fuzzing。只是这里作者又写了个USB内核模块,方便通过用户层发送USB消息去测试。

作者还专门搞了个树莓派来重现漏洞,演示通过USB去让Windows/Linux系统崩溃。

FuzzIL: Guided Fuzzing for JavaScript Engines

image-20190223135223721

这议题最大的亮点在于:自定义一套中间语言IL,通过IL可以翻译成JS代码,然后通过变异IL来生成JS代码,与以往基于JS语法模板生成代码的方式不同。

image-20190223135445955

直接通过一行行删除IL的方式来验证是否崩溃或产生新路径,以此用来精简样本。

整个Fuzzing过程如下:

image-20190223140416987

作者未来会在github上开源(https://github.com/googleprojectzero/fuzzilli),拭目以待。

结语

访问 https://github.com/riusksk/SecConArchive可获取议题的打包资料,除上述推荐的议题资料外,还有3个议题,包括”Bypass_Windows_Defender_ASR“、”macOS-How to Gain Root with CVE-2018-4193“,以及”OSX Privileged Helper Tool“,有兴趣的同学自行下载阅读。

image-20190223141538809

winafl中基于插桩的覆盖率反馈原理

最近winafl增加支持对Intel PT的支持的,但是只支持x64,且覆盖率计算不全,比如条件跳转等,所以它现在还是不如直接用插桩去hook的方式来得准确完整,这里主要想分析也是基于 DynamoRIO插桩的覆盖率反馈原理。

之前曾有人在《初识 Fuzzing 工具 WinAFL》(https://paper.seebug.org/323/#32)中“3.2.2 插桩模块”一节中简单分析过其插桩原理,但没有找到我想要的答案,因此只好自动动手分析下源码。

比如,我想知道:

  1. 通过循环调用fuzzing的目标函数来提高速度,但DynamoRIO的覆盖率信息是如何同步给fuzzer主进程的?

  2. 具体是如何实现寄存器环境的记录与恢复,从而实现目标函数的不断循环?

  3. 覆盖率信息是如何记录与分析的?

覆盖率信息记录与分析原理

第3个问题发现已经有人分析过afl,可以参见这里《AFL内部实现细节小记》(http://rk700.github.io/2017/12/28/afl-internals/),简单总结下:

  1. AFL在编译源码时,为每个代码生成一个随机数,代表位置地址;

  2. 在二元组中记录分支跳转的源地址与目标地址,将两者异或的结果为该分支的key,保存每个分支的执行次数,用1字节来储存;

  3. 保存分支的执行次数实际上是一张大小为64K的哈希表,位于共享内存中,方便target进程与fuzzer进程之间共享,对应的伪代码如下:

    1
    2
    3
    cur_location = <COMPILE_TIME_RANDOM>;
    shared_mem[cur_location ^ prev_location]++;
    prev_location = cur_location >> 1;
  4. fuzzer进程通过buckets哈希桶来归类这些分支执行次数,如下结构定义,左边为执行次数,右边为记录值trace_bits:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    static const u8 count_class_lookup8[256] = {
    [0] = 0,
    [1] = 1,
    [2] = 2,
    [3] = 4,
    [4 ... 7] = 8,
    [8 ... 15] = 16,
    [16 ... 31] = 32,
    [32 ... 127] = 64,
    [128 ... 255] = 128
    };
  5. 对于是否触发新路径,主要通过计算各分支的trace_bits的hash值(算法:u32 cksum **=** hash32(trace_bits, MAP_SIZE常量, HASH_CONST常量);)是否发生变化来实现的

覆盖信息的传递原理

  1. 先在fuzzer进程中先创建命名管道,其中fuzzer_id为随机值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //afl-fuzz.c
    pipe_name = (char *)alloc_printf("\\\\.\\pipe\\afl_pipe_%s", fuzzer_id);

    pipe_handle = CreateNamedPipe(
    pipe_name, // pipe name
    PIPE_ACCESS_DUPLEX | // read/write access
    FILE_FLAG_OVERLAPPED, // overlapped mode
    0,
    1, // max. instances
    512, // output buffer size
    512, // input buffer size
    20000, // client time-out
    NULL); // default security attribute
  2. 创建drrun进程去运行目标程序并Hook,在childpid_(%fuzzer_id%).txt的文件中记录子进程id,即目标进程ID,然后等待管道连接,并通过读取上述txt文件以获取目标进程id,主要用来后面超时中断进程的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    //afl-fuzz.c    
    pidfile = alloc_printf("childpid_%s.txt", fuzzer_id);
    if (persist_dr_cache) {
    cmd = alloc_printf(
    "%s\\drrun.exe -pidfile %s -no_follow_children -persist -persist_dir \"%s\\drcache\" -c winafl.dll %s -fuzzer_id %s -drpersist -- %s",
    dynamorio_dir, pidfile, out_dir, client_params, fuzzer_id, target_cmd);
    } else {
    cmd = alloc_printf(
    "%s\\drrun.exe -pidfile %s -no_follow_children -c winafl.dll %s -fuzzer_id %s -- %s",
    dynamorio_dir, pidfile, client_params, fuzzer_id, target_cmd);
    }
    ......
    if(!CreateProcess(NULL, cmd, NULL, NULL, inherit_handles, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
    FATAL("CreateProcess failed, GLE=%d.\n", GetLastError());
    }
    ......
    if(!OverlappedConnectNamedPipe(pipe_handle, &pipe_overlapped)) {
    FATAL("ConnectNamedPipe failed, GLE=%d.\n", GetLastError());
    }

    watchdog_enabled = 0;

    if(drioless == 0) {
    //by the time pipe has connected the pidfile must have been created
    fp = fopen(pidfile, "rb");
    if(!fp) {
    FATAL("Error opening pidfile.txt");
    }
    fseek(fp,0,SEEK_END);
    pidsize = ftell(fp);
    fseek(fp,0,SEEK_SET);
    buf = (char *)malloc(pidsize+1);
    fread(buf, pidsize, 1, fp);
    buf[pidsize] = 0;
    fclose(fp);
    remove(pidfile);
    child_pid = atoi(buf);
    free(buf);
    ck_free(pidfile);
    }
    else {
    child_pid = pi.dwProcessId;
    }
  3. 在插桩模块winafl.dll中打开前面创建的命名管道,然后通过管道与fuzzer主进程进行交互:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //winafl.c
    static void
    setup_pipe() {
    pipe = CreateFile(
    options.pipe_name, // pipe name
    GENERIC_READ | // read and write access
    GENERIC_WRITE,
    0, // no sharing
    NULL, // default security attributes
    OPEN_EXISTING, // opens existing pipe
    0, // default attributes
    NULL); // no template file

    if (pipe == INVALID_HANDLE_VALUE) DR_ASSERT_MSG(false, "error connecting to pipe");
    }
    ......
    char ReadCommandFromPipe()
    {
    DWORD num_read;
    char result;
    ReadFile(pipe, &result, 1, &num_read, NULL);
    return result;
    }

    void WriteCommandToPipe(char cmd)
    {
    DWORD num_written;
    WriteFile(pipe, &cmd, 1, &num_written, NULL);
    }
  4. 当插桩模块winafl.dll监测到程序首次运行至目标函数入口时,pre_fuzz_handler函数会被执行,然后通过管道写入’P’命令,代表开始进入目标函数,afl-fuzz.exe进程收到命令后,会向目标进程写入管道命令’F’,并监测超时时间和循环调用次数。afl-fuzz.exe与目标进程正是通过读写管道命令来交互的,主要有’F’(退出目标函数)、’P’(进入目标函数)、’K’(超时中断进程)、’C’(崩溃)、’Q’(退出进程)。覆盖信息通过文件映射方法(内存共享)写入winafl_data.afl_area

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    //winafl.c
    pre_fuzz_handler(void *wrapcxt, INOUT void **user_data)
    {
    ......
    if(!options.debug_mode) {
    WriteCommandToPipe('P');
    command = ReadCommandFromPipe();

    if(command != 'F') {
    if(command == 'Q') {
    dr_exit_process(0);
    } else {
    DR_ASSERT_MSG(false, "unrecognized command received over pipe");
    }
    }
    } else {
    debug_data.pre_hanlder_called++;
    dr_fprintf(winafl_data.log, "In pre_fuzz_handler\n");
    }
    ......
    memset(winafl_data.afl_area, 0, MAP_SIZE); // 用于存储覆盖率信息

    if(options.coverage_kind == COVERAGE_EDGE || options.thread_coverage) {
    void **thread_data = (void **)drmgr_get_tls_field(drcontext, winafl_tls_field);
    thread_data[0] = 0;
    thread_data[1] = winafl_data.afl_area; //如果开启-thread_coverage选项,则会将覆盖率信息写入线程TLS中
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //winafl.c
    static void
    setup_shmem() {
    HANDLE map_file;

    map_file = OpenFileMapping(
    FILE_MAP_ALL_ACCESS, // read/write access
    FALSE, // do not inherit the name
    options.shm_name); // name of mapping object

    if (map_file == NULL) DR_ASSERT_MSG(false, "error accesing shared memory");

    winafl_data.afl_area = (unsigned char *) MapViewOfFile(map_file, // handle to map object
    FILE_MAP_ALL_ACCESS, // read/write permission
    0,
    0,
    MAP_SIZE);

    if (winafl_data.afl_area == NULL) DR_ASSERT_MSG(false, "error accesing shared memory");
    }

篡改目标函数循环调用的原理

此步的关键就在于进入目标函数前调用的pre_fuzz_handler函数,以及函数退出后调用的post_fuzz_handler函数。

进入pre_fuzz_handler函数时,winafl.dll会先获取以下信息

1
2
3
4
5
6
app_pc target_to_fuzz = drwrap_get_func(wrapcxt);	//获取目标函数地址
dr_mcontext_t *mc = drwrap_get_mcontext_ex(wrapcxt, DR_MC_ALL); //获取目标函数当前内存上下文信息
drcontext = drwrap_get_drcontext(wrapcxt); //获取DynamoRIO上下文

fuzz_target.xsp = mc->xsp; // 保存栈指针,xsp是各平台下的通用标记变量
fuzz_target.func_pc = target_to_fuzz; // 目标函数地址

其中内存上下文信息支持各平台的寄存器记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
typedef struct _dr_mcontext_t {
/**
* The size of this structure. This field must be set prior to filling
* in the fields to support forward compatibility.
*/
size_t size;
/**
* The valid fields of this structure. This field must be set prior to
* filling in the fields. For input requests (dr_get_mcontext()), this
* indicates which fields should be written. Writing the multimedia fields
* frequently can incur a performance hit. For output requests
* (dr_set_mcontext() and dr_redirect_execution()), this indicates which
* fields will be copied to the actual context.
*/
dr_mcontext_flags_t flags;

#ifdef AARCHXX
reg_t r0; /**< The r0 register. */
reg_t r1; /**< The r1 register. */
reg_t r2; /**< The r2 register. */
reg_t r3; /**< The r3 register. */
reg_t r4; /**< The r4 register. */
reg_t r5; /**< The r5 register. */
reg_t r6; /**< The r6 register. */
reg_t r7; /**< The r7 register. */
reg_t r8; /**< The r8 register. */
reg_t r9; /**< The r9 register. */
reg_t r10; /**< The r10 register. */
reg_t r11; /**< The r11 register. */
reg_t r12; /**< The r12 register. */
# ifdef X64 /* 64-bit */
reg_t r13; /**< The r13 register. */
reg_t r14; /**< The r14 register. */
reg_t r15; /**< The r15 register. */
reg_t r16; /**< The r16 register. \note For 64-bit DR builds only. */
reg_t r17; /**< The r17 register. \note For 64-bit DR builds only. */
reg_t r18; /**< The r18 register. \note For 64-bit DR builds only. */
reg_t r19; /**< The r19 register. \note For 64-bit DR builds only. */
reg_t r20; /**< The r20 register. \note For 64-bit DR builds only. */
reg_t r21; /**< The r21 register. \note For 64-bit DR builds only. */
reg_t r22; /**< The r22 register. \note For 64-bit DR builds only. */
reg_t r23; /**< The r23 register. \note For 64-bit DR builds only. */
reg_t r24; /**< The r24 register. \note For 64-bit DR builds only. */
reg_t r25; /**< The r25 register. \note For 64-bit DR builds only. */
reg_t r26; /**< The r26 register. \note For 64-bit DR builds only. */
reg_t r27; /**< The r27 register. \note For 64-bit DR builds only. */
reg_t r28; /**< The r28 register. \note For 64-bit DR builds only. */
reg_t r29; /**< The r29 register. \note For 64-bit DR builds only. */
union {
reg_t r30; /**< The r30 register. \note For 64-bit DR builds only. */
reg_t lr; /**< The link register. */
}; /**< The anonymous union of alternative names for r30/lr register. */
union {
reg_t r31; /**< The r31 register. \note For 64-bit DR builds only. */
reg_t sp; /**< The stack pointer register. */
reg_t xsp; /**< The platform-independent name for the stack pointer register. */
}; /**< The anonymous union of alternative names for r31/sp register. */
/**
* The program counter.
* \note This field is not always set or read by all API routines.
*/
byte *pc;
union {
uint xflags; /**< The platform-independent name for condition flags. */
struct {
uint nzcv; /**< Condition flags (status register). */
uint fpcr; /**< Floating-Point Control Register. */
uint fpsr; /**< Floating-Point Status Register. */
}; /**< AArch64 flag registers. */
}; /**< The anonymous union of alternative names for flag registers. */
# else /* 32-bit */
union {
reg_t r13; /**< The r13 register. */
reg_t sp; /**< The stack pointer register.*/
reg_t xsp; /**< The platform-independent name for the stack pointer register. */
}; /**< The anonymous union of alternative names for r13/sp register. */
union {
reg_t r14; /**< The r14 register. */
reg_t lr; /**< The link register. */
}; /**< The anonymous union of alternative names for r14/lr register. */
/**
* The anonymous union of alternative names for r15/pc register.
* \note This field is not always set or read by all API routines.
*/
union {
reg_t r15; /**< The r15 register. */
byte *pc; /**< The program counter. */
};
union {
uint xflags; /**< The platform-independent name for full APSR register. */
uint apsr; /**< The application program status registers in AArch32. */
uint cpsr; /**< The current program status registers in AArch32. */
}; /**< The anonymous union of alternative names for apsr/cpsr register. */
# endif /* 64/32-bit */
/**
* The SIMD registers. We would probably be ok if we did not preserve the
* callee-saved registers (q4-q7 == d8-d15) but to be safe we preserve them
* all. We do not need anything more than word alignment for OP_vldm/OP_vstm,
* and dr_simd_t has no fields larger than 32 bits, so we have no padding.
*/
dr_simd_t simd[NUM_SIMD_SLOTS];
#else /* X86 */
union {
reg_t xdi; /**< The platform-independent name for full rdi/edi register. */
reg_t IF_X64_ELSE(rdi, edi); /**< The platform-dependent name for
rdi/edi register. */
}; /**< The anonymous union of alternative names for rdi/edi register. */
union {
reg_t xsi; /**< The platform-independent name for full rsi/esi register. */
reg_t IF_X64_ELSE(rsi, esi); /**< The platform-dependent name for
rsi/esi register. */
}; /**< The anonymous union of alternative names for rsi/esi register. */
union {
reg_t xbp; /**< The platform-independent name for full rbp/ebp register. */
reg_t IF_X64_ELSE(rbp, ebp); /**< The platform-dependent name for
rbp/ebp register. */
}; /**< The anonymous union of alternative names for rbp/ebp register. */
union {
reg_t xsp; /**< The platform-independent name for full rsp/esp register. */
reg_t IF_X64_ELSE(rsp, esp); /**< The platform-dependent name for
rsp/esp register. */
}; /**< The anonymous union of alternative names for rsp/esp register. */
union {
reg_t xbx; /**< The platform-independent name for full rbx/ebx register. */
reg_t IF_X64_ELSE(rbx, ebx); /**< The platform-dependent name for
rbx/ebx register. */
}; /**< The anonymous union of alternative names for rbx/ebx register. */
union {
reg_t xdx; /**< The platform-independent name for full rdx/edx register. */
reg_t IF_X64_ELSE(rdx, edx); /**< The platform-dependent name for
rdx/edx register. */
}; /**< The anonymous union of alternative names for rdx/edx register. */
union {
reg_t xcx; /**< The platform-independent name for full rcx/ecx register. */
reg_t IF_X64_ELSE(rcx, ecx); /**< The platform-dependent name for
rcx/ecx register. */
}; /**< The anonymous union of alternative names for rcx/ecx register. */
union {
reg_t xax; /**< The platform-independent name for full rax/eax register. */
reg_t IF_X64_ELSE(rax, eax); /**< The platform-dependent name for
rax/eax register. */
}; /**< The anonymous union of alternative names for rax/eax register. */
# ifdef X64
reg_t r8; /**< The r8 register. \note For 64-bit DR builds only. */
reg_t r9; /**< The r9 register. \note For 64-bit DR builds only. */
reg_t r10; /**< The r10 register. \note For 64-bit DR builds only. */
reg_t r11; /**< The r11 register. \note For 64-bit DR builds only. */
reg_t r12; /**< The r12 register. \note For 64-bit DR builds only. */
reg_t r13; /**< The r13 register. \note For 64-bit DR builds only. */
reg_t r14; /**< The r14 register. \note For 64-bit DR builds only. */
reg_t r15; /**< The r15 register. \note For 64-bit DR builds only. */
# endif
union {
reg_t xflags; /**< The platform-independent name for
full rflags/eflags register. */
reg_t IF_X64_ELSE(rflags, eflags); /**< The platform-dependent name for
rflags/eflags register. */
}; /**< The anonymous union of alternative names for rflags/eflags register. */
/**
* Anonymous union of alternative names for the program counter /
* instruction pointer (eip/rip). \note This field is not always set or
* read by all API routines.
*/
union {
byte *xip; /**< The platform-independent name for full rip/eip register. */
byte *pc; /**< The platform-independent alt name for full rip/eip register. */
byte *IF_X64_ELSE(rip, eip); /**< The platform-dependent name for
rip/eip register. */
};
byte padding[PRE_XMM_PADDING]; /**< The padding to get ymm field 32-byte aligned. */
/**
* The SSE registers xmm0-xmm5 (-xmm15 on Linux) are volatile
* (caller-saved) for 64-bit and WOW64, and are actually zeroed out on
* Windows system calls. These fields are ignored for 32-bit processes
* that are not WOW64, or if the underlying processor does not support
* SSE. Use dr_mcontext_xmm_fields_valid() to determine whether the
* fields are valid.
*
* When the fields are valid, on processors with AVX enabled (i.e.,
* proc_has_feature(FEATURE_AVX) returns true), these fields will
* contain the full ymm register values; otherwise, the top 128
* bits of each slot will be undefined.
*/
dr_ymm_t ymm[NUM_SIMD_SLOTS];
#endif /* ARM/X86 */
} dr_mcontext_t;

接下来就是获取和设置fuzzed的目标函数参数:

1
2
3
4
5
6
7
8
9
10
//save or restore arguments
if (!options.no_loop) {
if (fuzz_target.iteration == 0) {
for (i = 0; i < options.num_fuz_args; i++)
options.func_args[i] = drwrap_get_arg(wrapcxt, i); //首次运行先获取运行参数
} else {
for (i = 0; i < options.num_fuz_args; i++)
drwrap_set_arg(wrapcxt, i, options.func_args[i]); //设置运行参数
}
}

当目标函数退出后,执行post_fuzz_handler函数,会恢复栈顶指针和pc地址,以此实现目标函数的循环调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static void
post_fuzz_handler(void *wrapcxt, void *user_data)
{
dr_mcontext_t *mc;
mc = drwrap_get_mcontext(wrapcxt); //获取上下文信息

if(!options.debug_mode) {
WriteCommandToPipe('K');
} else {
debug_data.post_handler_called++;
dr_fprintf(winafl_data.log, "In post_fuzz_handler\n");
}

/* We don't need to reload context in case of network-based fuzzing. */
if (options.no_loop)
return; //网络型Fuzzing无需重载上下文信息

//超过循环次数就退出进程
fuzz_target.iteration++;
if(fuzz_target.iteration == options.fuzz_iterations) {
dr_exit_process(0);
}

mc->xsp = fuzz_target.xsp; //恢复栈顶指针
mc->pc = fuzz_target.func_pc; //篡改pc地址加原目标函数地址
drwrap_redirect_execution(wrapcxt); //篡改执行流
}

总结

总结下整个winafl执行流程:

  1. afl-fuzz.exe通过创建命名管道与内存映射来实现与目标进程交互,其中管道用来发送和接收命令相互操作对方进程,内存映射主要用来记录覆盖率信息;
  2. 覆盖率记录主要通过drmgr_register_bb_instrumentation_event去设置BB执行的回调函数,通过instrument_bb_coverage或者instrument_edge_coverage来记录覆盖率情况,如果发现新的执行路径,就将样本放入队列目录中,用于后续文件变异,以提高代码覆盖率;
  3. 目标进程执行到目标函数后,会调用pre_fuzz_handler来存储上下文信息,包括寄存器和运行参数;
  4. 目标函数退出后,会调用post_fuzz_handler函数,记录恢复上下文信息,以执行回原目标函数,又回到第2步;
  5. 目录函数运行次数达到指定循环调用次数时,会中断进程退出。

聊聊那些黑客小说

系统宕,
资料泄,
挂马黑站何时了?
安全知多少!
告警昨夜又响起,
往事不堪回首月明中。
漏洞应犹在,
只是域名改。
问君能有几多愁?
恰似行行代码错误留。

黑客小说:培养对信息安全的兴趣

之前在《漏洞战争》的前言里面有提到,鄙人初入象牙塔之时,曾看过多本黑客小说,包括《黑客传说》、《地狱黑客》(后改名为《禁区之门》,参考凯文·米特尼克事迹写的,后来又出了第2部,但好像没写完)、《指间的黑客》等等,后来也因此对安全产生兴趣,入了这行道。

但,道归道,兴趣归兴趣!

因为曾有多少人,来了兴趣多年,却未曾自学过。

“我对安全很感兴趣,求师傅教教我!”这种老套路的提问,大家见得还少吗?

正如许多人宁愿被动受苦,也不愿主动吃苦学习一样,这个问题在知乎上也曾被热烈讨论过。被动受苦大多不用多动脑,久而久之,身体也会慢慢地适应,也就逐渐被生活、被制度所驯化。

不过,此处想说的重点是,黑客小说可以培养人们对信息安全的兴趣,而学习的过程本身就是孤独单调的,如有兴趣相伴,则更易独行久远!

行业人写小说:有始无终,多半夭折

安全圈里面其实也有一些人写过黑客小说,不乏某些知名人士,但基本上都是开了个头,却没多久就夭折收场。反正,我是没见过有人写完出版的。

于是,我上Google搜索了下起点网,通过一些安全专业名词进行搜索,找到几部相关小说:

  “第一节操作系统原理、第二节系统及命令详解、第三节溢出漏洞原理、第四节web原理、第五节http协议、tcp/ip协议第六节sql注入原理、上传漏洞、XSS、CSRF……”

​ ——《别说我是黑客》

  过了十多分钟,jsky的界面左侧刷出来许多asp文件和几个目录。右栏则是标注着绿色和红色叹号的几列英文。
  看到扫描结果出来,杨风面带微笑进行下一步操作。他的运气不错,得到了xss与sqlinjection漏洞,这是网站暴露出来的跨站脚本漏洞和sql注入漏洞。
  杨风转到育民高中主页,随手选取了一个注入地址,提交上引号。
  返回错误。
  接着提交and1=1语句。
  返回正常。
  继续提交and1=2语句。
  返回错误。
  杨风做这两步,主要是测试网站程序设计者是否在其中过滤了关键字,如果过滤就不能被注入。

​ ——《黑客记事本》

​ “尊敬的孙诚先生,请允许我对你和你的团队–塞伯坦工作室致以敬意。我们的检测人员已经验证了你所寄过来的数据,并证实了ios漏洞的存在,在对你们表示感谢的同时,Zerodium会按照约定,向你的团队支付共计五万四千美元的报酬,并希望你们能尽快将后续数据补完。在邮件中,你们提到找到了ios的两处极为隐秘的高危漏洞,Zerodium上下都对此非常感兴趣。希望能够尽快收到你的回复,报酬方面请不用担心,Zerodium是一个非常有信誉的平台!”

​ ——《从变形金刚开始》

“我明白了,灵根这个电源,就相当于是有着固定编码的反编译器,每个灵根都有一个固定编码,就是所谓的灵根属性。灵根可以把跟自己编码一样的灵气团,进行逆向反编译,把编译好的灵气团分解成单纯由0和1构成的信息流,就是所谓的灵力!”

​ ——《程序员修真之路》

从教你用jsky黑站(PS:为啥不推荐wvs呢),到黑客穿越、程序员修仙等,各种奇思妙想,在网络小说领域也算是另类的存在。

但是从这些专业名词看,作者即使不是安全圈的,至少也是IT技术行业的,对一些常见的安全事件、技术名词都比较了解。

文学与IT技术本身就是两个不同的领域,要同时兼顾就有一定难度。如果算程序员里面写的小说,比较火的,应该是那本《疯狂的程序员》吧!

懵懂之美:似懂非懂才是最大的乐趣

我已经很久没看黑客小说,尤其是入了行之后,就更不看了。

如果要推荐的话,还是开头提到的那3本小说:《黑客传说》、《禁区之门》、《指间的黑客》,不过那是我大学时的口味了,现在重新看也不一定就如当初那般喜欢。

因为当你对安全行业熟悉后,再去看这些小说的时候,可能就容易较真,少了些许乐趣。

我当初看上面的小说的时候,其实也还没入门安全,所以看得特别起劲。

所以,如果要看这类小说,就把那些行业知识都抛诸脑后,享受那当初的懵懂之美,才是读小说的乐趣所在。

后话

也许是性格使然,在文字世界里,鄙人老喜欢委婉地批判人和事(俗称:骂人)。不过文学世界里,一千个读者就有一千个哈姆雷特,应该支持下这种多元文化。不然就像娱乐圈里,谁演孙悟空,都要被六小龄童骂一般,少了胸怀,甚至阻碍了行业发展。

安全研究者的自我修养(续)

接上篇继续聊安全研究者的自我修养,上篇重点讲技术修炼,本篇聊聊行业现象、谈谈沉淀、情怀等等。

11、工具与方法论沉淀

虽说代码审计是项必备技能,但终究是项体力活。

有些漏洞(比如逻辑漏洞)可能就需要人工审计,但也有不少漏洞是可以自动化Fuzzing,一些能自动化或半自动化实现的,尽量写程序自动化。

因为,纯人工审计终究熬不过年纪,熬不过团队人员的离散变迁,熬不过互联网的快速发展……

比如,2012年刚开始写《漏洞战争》时,单身一人,从早上8点多起床吃饭,然后开始调代码、看代码,一直奋战到晚上12点,身体无压力。近7年过去了,现在要是这么折腾,身体就要散架了……

比如,团队里的人分工做不同领域的代码审计,若无工具和方法论沉淀,那么有人走的话,此人对应的领域可能就无法持续产出;若有新人加入,代码审计的技能又不好传承,很多得自己重头来。所以,一直觉得,好的团队应该是,即使人员离散变迁,依然能够独立运作、持续产出的。

比如,Linux内核在2018年净增87万行代码,很多类似复杂庞大的项目,看代码有时看都看不过来,一般都是针对性地挑模块作代码审计。

比如,Fuzzer开发里面就有很多共用功能是可以直接做成框架沉淀下来,文件变异、崩溃监控、样本去重精简等等,很多时候有个新的攻击面需要测试,就可以直接在框架的基础上写fuzzer,将会高效很多。下文提到的一个IE漏洞挖掘案例就是基于这思路挖到的。

我曾经想开发两个漏洞挖掘系统,一个二进制,一个Web,名字都想好了,合称”冰弓玄箭“,但业余一直都没什么时间开发,仅写了个界面,希望2019年能够完成:

image-20190112143451515

”冰弓“的Logo直接用的是“破甲弓”,感觉很酷……

再说说方法论,这词虽有点虚,但其实本质上就是一种技术方法的总结而已。

比如,渗透测试的时候,总有些人每次都能搞到RCE,无论啥网站,完全摆脱“随机挖洞”的命运。多数情况下,他们都会有一套自己测试方法,或者将一些经验转换成工具,测试时就拿自己的工具和以往总结的方法论开搞。

比如,STRIDE威胁建模本身就是一套方法论,一套简单的风险助记符,当然我这里不是说安全研究要用它,只是举个方法论的例子,它也没有那么万能。

写这么多,总结起来就一句话:多总结,多沉淀!

12、漏洞研究风向标:安全公告

如果大家有关注四大厂商(Google、Microsoft、Apple、Adobe)的安全公告的话,会发现有段时间会出现很多类似漏洞的公告,出现一个新的攻击面之后,一帮研究人员就蜂捅而上狂刷一波。

这种情况一向是先下手为强,而上文提到的工具和方法论就更显得尤为重要了,否则最后都只能捡剩的。

比如本周 Microsoft 安全公告出来后,我仔细分析了下,然后下班回家写了个Fuzzer,挂着跑了一天,出来个Crash,再用几分钟成功构造出PoC,实现IE浏览器的远程代码执行,可见也是个品相极佳的神洞:

image-20190112153428430

但不幸的是,我打了1月的补丁后,发现修复了,成功“撞洞”,真的是欲哭无泪……

但至少证明,通过安全公告寻找新的攻击面,然后挖掘一些类似漏洞,一直是一种高效的漏洞研究方式。

13、老一辈研究者都去哪儿了?

img

最近腾讯AILab张潼离职的事传得很火,还有之前各大厂聘请的AI科学家陆续辞职,回归学术界,很多人因此唱起科学家之于科技公司的无用论,主要有以下几点原因:

  1. 研究成果无法落地为产品:做安全研究也是如此,很多事情是无法落地的,圈内很多研究团队都是拿漏洞来打比赛赚影响力,真正能实现为公司营利的(打比赛赚奖金的忽略不计,因为那些都不够给研究者们的工资),我只知道有1个研究团队/实验室今年营利了。
  2. 长期无产出,KPI压力大:研究了很长时间,最后仍一无所获,那KPI咋办、PPT怎么写、晋级怎么答辩。安全行业有句老话来形容安全研究工作,叫“三年不开锅,开锅吃三年”,但多数个人和企业都等不到三年。之前同事说王小云为何能破解出MD5,是因为她在学校里很长时间没搞出东西的时候,领导没找她麻烦,没有KPI压力,以致能够长期专注于此。具体原因我不确定,但学术界自然是没有企业有这般KPI压力。
  3. 业务数据不共享:业务部门的产品数据基本不太可能共享给实验室作研究的,一般都是实验室以SDK的形式提供给业务用,数据由业务自主控制。这种情况对于安全研究的影响相对较少一些。

头两点是多数安全研究者的困境,也跟圈内同行讨论过,下面聊聊这帮老一代“知青”最后都去哪儿了?这里我主要总结一些圈内人的应对方法(其实多数都是转型),具体不作点评,总结为主,也欢迎私信讨论(新注册的公众号已不允许留言)。

  1. 坚持研究:这帮人主要还是那些研究能力较强的,且有一定研究成果的人,围观下各大实验室就知道个大概,不多说;
  2. 转型安全产品开发与运营:有产品就能解决落地问题,帮助企业解决实际问题,有不少人走这条道,去做威胁情报系统、漏洞扫描器、WAF、云安全产品等等;
  3. 转型业务安全:跟研究工作差异较大,因为业务安全的主要问题很多时候并非漏洞,而是跟业务产品相关的黑灰产对抗等等;
  4. 自由研究者:国外很多此类研究者,靠拿漏洞赏金过活,俗称“赏金猎人”,国内相对少一些,也有一些国内自由研究者后来又进企业做研究的,这里讲的几种转型都可以来回转换,有些人就干过。
  5. 创业:这里包括安全行业内的创业,也包括那些开淘宝店、奶茶店、服装生意、卖水果的……

14、个人终究干不过团队

有时想搞的研究太多了,但发现一个人根本搞不过来,需要多人协作才可能完成。但需要多人在研究领域上有交集,否则拉在一块也是各搞各的。

前篇第7点讲到“进入研究者团队或社区,互相学习”,也是一大影响因素,互相学习也是一种提高效率和产出的方式。

算了,不多说了!

后话

这次真的结束了,没有续篇了。

思考了很多,总结了很多,有些也是写了删,删了写。

安全研究领域一直也没人写过这些,出来唠叨几句,也欢迎大家私信讨论。

最后奉一首酒桌上的《苦行僧》结束本话题,听过这首歌很多个版本,包括原唱,但终究还是觉得视频里这位老哥唱得更具江湖气、更具情感、更具感染力……旁边一老哥听着听着都偷偷抹泪了!

之所以点这首歌,是因为:每一个研究者都是独立自行的苦行僧!