读《风格感觉:21世纪写作指南》

1、节俭使用元话语(语句中标示话语结构的标记语言,即用来提醒读者应该注意什么),可以用提问代替元话语,比如:

【改前】:这一章讨论引起名字流行程度上升和下降的因素。

【改后】:一个名字流行或不流行的原因是什么?

2、放弃专家腔、更自然地对话

【改前】:近年来,越来越多的心理学家和语言学家将注意力转向儿童语言习得的问题。本文将评述这一过程近年来的研究。

【改后】:小孩子不用专门上课,就能获得说一门语言的能力。他们怎么做到的?

3、写作清晰有力,少用模糊词汇

【改前】:杰克是个诚实的人。

【改后】:杰克是个特别诚实的人。

加了“特别”反而让人产生困惑,甚至以为说的是反话,所以少用“非常”、“十分”、“特别”这种过犹不及的强调词。

4、少用抽象词,比如“xx性”、“xx观”、“xx力”,这种特别容易出现在工作中,俗称“官僚体”,腾讯内部就有一堆此类名词(闭环、打法、自控力、大局观、专注度……)

5、去掉僵尸名词:比如“做出确认”变成“确认”、“做出辞职的决定”变成“决定辞职”。

6、采用主动和互动风格,即站在读者角度或者自己主动的角度。

【改前】:我们很高兴地宣布,本实验室的新设施将对外开放,随时准备承接各类脑科学实验。

【改后】:你将有机会使用本实验室,来做你的脑科学实验。

7、【有序地称呼反复出现的事物】避免读者过多思考,免得他们需要思考是否为新事物

一个英国人、一个法国人和一个犹太人坐在一起,这个英国人说……,这个法国人说……,这个犹太人说……

少用他/她/它去代替前面说过的人物事等,减少读者疑惑和思考,避免产生歧义,比如:

愧疚、复仇和苦痛会从感情上毁掉你和你的孩子。必须摆脱他们。

上面的是摆脱你的孩子,还是摆脱愧疚、复仇和苦痛?

8、避免使用过多的连接词,否则文章就会变得臃肿不堪,比如下句中的因为就是多余的,因为前面的“原因”已经暗示我们正在做出解释:

太多人生活在黑暗之中的原因是因为他们想那样生活。

9、【谨慎地运用否定】正如克林顿辩解道“我没有和那个女人发生过性关系”的时候,并没有使各种传言平息下来,有时甚至起到“此地无银三百两“的反作用。

10、正确的写作用法只是促成良好写作最小的因素,其重要性远远比不过保持连贯性、使用古典风格、克服知识的诅咒,更不用说在智力上维护勤恳了。假如真的希望提高写作质量,或者怒斥他人的文章,最需要关注的不是那些语法规则,而是那些支配批判性思考和发现事实的规则:

​ a. 查资料

​ b.确保论证有理有据

​ c. 不要把轶事或个人经历当作世界的常态,避免把特殊个例当作显著现象

​ d. 谨防虚假的二分法:把复杂问题化为两种思想之间的战争,几乎无法帮助我们增进理解

​ e.论证应当基于理性,而非个人。即“不要证明自己对,而要弄清什么是对的”

最后说一句最重要的话:

热文或畅销书之所以流行,关键在于其内容,而非华丽的辞藻文法。

我的挑书手段

双十一,一年一度的”屯书日“又到了。

鄙人生平无其它购物嗜好,唯有购书。

在电子书横行的时代,各类读书应用都在崛起,比如”微信读书“、”QQ阅读“、”掌阅“等,个人习惯用微信读书,因为有社交好友的阅读推荐,以及公司送的读书券,所以基本也都是免费的,这个才是重点。

对于懒人,也有听书应用,比如”懒人听书“、”喜马拉雅“,特别适合听小说、历史人文,之前我就在上面听过《明朝那些事儿》、《盗墓笔记》等,特别是一些小说类的配音,如电影一般,甚是精彩声动。

开篇聊完,该说说”挑书”这件事儿了……

首先,对于非技术书,如果在上述读书app中有免费的,一般我不会去买实体书,直接在app上翻来看看,有兴趣就继续,没兴趣就结束了。

所以,这里所说的挑书,挑的是技术书以及无电子版的非技术书,或者其它想收藏的经典书籍(如书帖、史书、经管心理名著等等)。

挑书第一式:查作者翻目录

翻开当当网信息安全书籍总榜,在前3名,永远有一本书占其中,书名永远是《黑客xx从入门到精通》,而且还是个系列,好多本,也不得不承认,越初级越入门,越受欢迎,毕竟菜鸟永远比专业人士多……

image-20181110093819437

挑书第一眼,自然是作者与目录,但对它们的判断,又常常要求读者具备一定的专业知识,因为:

“只有具备犯罪能力的人才能洞察他人的犯罪行为。”

先看作者,经常署名“xx工作室”,也有个人,此时都可以查看他们出过的书,对比各书目录,经常可以发现“换汤不换药”的行为,把旧书内容重新包装进新书再出版。

有时出版社编辑也会找我帮忙评价下某些新书目录,我就经常这么搞,偶而就会发现存在这种情况。

image-20181110095640534

现在很多书籍都可以在线试读了,也一种不错的挑书方式。

挑书第二式:利用工具查价

每到购物节,各网站都会推出“满多少减多少”的优惠,但有时查下历史价格趋势会发现,都是先抬高价格再打折的。

对于这种情况都一些工具可以查询,如果是在电脑上可以用“油猴“脚本:购物党自动比价工具:

image-20181110100630299

如果是手机上,可以使用”慢慢买“、”历史价值查询“,比如京东《态度》这本书,昨天就从32.5抬高到59:

image-20181110100956404

挑书第三式:看评价

购物网站上面都有评价可以看,还有豆瓣读书上,都可以看到一些书籍的评价,作为自己的一些参考。虽然豆瓣读书的评分不如电影那般准确,但评语还是可以参考的。

还有网上也有一些推荐书籍的文章,经典的比如”C语言之四书五经“、“Linux内核学习四库全书”,还有最近左耳朵耗子在微博上推荐的“程序员必读经典书籍”,都是可以借鉴参考的。

这些文章网上都有,大家自行搜索,对于信息安全从业人员,之前我也列了个书单“信息安全从业者书单推荐”:https://github.com/riusksk/secbook,好坏自行判断,毕竟不同人对同一本书的看法也是不一样的。

挑书第四式:预估对个人的实用价值

“买书如山倒,看书如抽丝”是多数人的真实写照,所以有时我也特别能理解女生为何喜欢整天买化妆品衣服包包之类的。对于这种情况,我一般这样选择:

  • 在未来一年自己用不上的技术知识,不买相关书籍。

  • 未来一年可能过时或淘汰的技术知识(比如Flash、塞班等),不买相关书籍

  • 挑选与当前自身能力要求相近或高一点的书籍,至少能看懂半本书的,当年初学二进制逆向,看《网络渗透技术》一书就跟天书一般,没几年的技术功底积累,也根本看不懂此书

  • 可能绝版的好书,也可考虑提前购买,即使当前看不懂,还说《网络渗透技术》这书,后来我在淘宝双倍价购买打印版了,现在china-pub上也可以双倍价购买此书,不过封面已换

挑书第五式:打铁仍需自身硬

遥想当年,我看的第一本安全书籍叫《黑客入门》,当时挑书的标准是:必须带有“黑客”两个字,否则不看。

过一段时间,发现这些书籍对提高技术并没有什么作用,然后开始找大学计算机课程开始从基础学起。

这个转折点,要从《深入理解计算机系统》开始,然后又开始学汇编、C/C++、数据结构与算法等等基本课。

当自己积累得越来越多的时候,能看懂的书就越多,对技术书籍的好坏判断,自然就有自己的评判标准。

WX20180707-203543

honggfuzz漏洞挖掘技术深究系列(5)—— Intel Processor Trace

对于闭源程序的反馈驱动Fuzzing,通常有3种方式:

  • 二进制插桩:使用Pin或DynamoRIO动态插桩监控代码覆盖率,比如winafl

  • 虚拟化技术:使用Qemu或Boch等虚拟化技术实现应用层和内核层的代码覆盖率监控,比如afl、bochpwn

  • 硬件级技术:使用Intel Processor Trace(PT)技术,比如honggfuzz

Intel PT

Intel® Processor Trace (Intel® PT) 是在i5/i7 5000以上型号上加入的功能,由于它是硬件级的特性,相比Qemu或Boch,在性能上和代码工作量会占有一定优势。在Linux上可以通过perf来使用PT,可以先简单看是否支持PT:

1
2
3
4
5
查看是否支持PT:
ls /sys/devices/intel_pt/format

追踪程序执行:
perf record -e intel_pt// program

也可以使用开源工具simple-pt中的ptfeature命令行工具来检测:

1
2
./ptfeature pt
Supports PT

最新版GDB也支持pt功能了:

1
2
3
4
5
6
7
gdb program
start
record btrace pt
cont

record instruction-history /m # show instructions
record function-history # show functions executed

honggfuzz perf_event_open

在程序内通过perf_event_openhttp://man7.org/linux/man-pages/man2/perf_event_open.2.html)函数可以使用PT实现BB基本块的覆盖率追踪,传递给指定进程pid来实现监控:

将返回的文件描述符传递给mmap映射为可读写的用户内存空间,以便从中读取PT记录的追踪数据:

PT记录的追踪数据采用压缩的二进制格式输出,每秒每个CPU都会持续记录并输出,由于是硬件记录的,最早自然是出现在内核空间,为了使用它,就需要将其导出到用户空间,即通过前面mmap方法映射到用户可写的内存空间,然后再去定位数据解码。PT导出的追踪数据被存储在一个叫AUX space的内存区域,它相对perfMmapBuf的偏移记录在perf_event_mmap_page->aux_offset,大小为perf_event_mmap_page->aux_size,上面代码的第二步mmap就是去映射AUX space

接下来就是利用libpt来解码捕获到追踪数据,实现函数位于perf_ptAnalyzePkt中:

最后将执行到的BB基本块信息更新到feedback map,之后的实现步骤就跟本系列第1篇驱动反馈中所讲的一致。

到这里,关于《honggfuzz漏洞挖掘技术深究系列》的文章先暂告一段落了,它就相当于是自己的学习笔记,也可以留作日后查询。

本系列的其它文章如下:

honggfuzz漏洞挖掘技术深究系列(1)——反馈驱动(Feedback-Driven)

honggfuzz漏洞挖掘技术深究系列(2)—— Persistent Fuzzing

honggfuzz漏洞挖掘技术深究系列(3)——Fuzz策略

honggfuzz漏洞挖掘技术深究系列(4)—— 扩展Fuzzer

honggfuzz漏洞挖掘技术深究系列(4)—— 扩展Fuzzer

对于一些复合文件格式,如果只是单纯的暴力Fuzzing,会导致生成很多无法被解析的文件,因此需要对文件变异作一些定制化的工作,比如docx、doc等office复合文件,docx是个压缩包,doc是个OLE格式,如果fuzz docx自然需要将其zip解压,再针对感兴趣的文件作变异,对于doc最好是作文件格式解析,只对感兴趣的stream作文件变异,这样的fuzzing的有效性才会更高。

庆幸地是,honggfuzz提供-c参数用于扩展变异规则以代替原有变异方式,同时提供有--pprocess_cmd在原有的文件变异后再作处理:

1
2
3
4
--mutate_cmd|-c VALUE
External command producing fuzz files (instead of internal mutators)
--pprocess_cmd VALUE
External command postprocessing files produced by internal mutators

-c功能比较有用,也是我用得比较多的,另一个--pprocess_cmd基本我没用过。

当你通过-f提供输入样本目录后,在fuzzing时,随机提取的文件会直接传递给-c参数指定的扩展命令作变异。

比如想针对某文件特定offset范围内的内容进行变异,下面是针对macOS/iOS字体文件中的虚拟指令作Fuzzing时写的脚本:

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
#!/usr/bin/env python

import mmap
import os
from random import randint
import sys

RANGE_START = 0x16D8
RANGE_END = 0x304D
MIN_BYTES_TO_FLIP = 1
MAX_BYTES_TO_FLIP = 5

if ".DS_Store" in sys.argv[1]:
exit(1)

with open(sys.argv[1], "r+b") as f:
mapped = mmap.mmap(f.fileno(), 0)
#print "file size: 0x%x" % len(mapped)
bytes_to_flip = randint(MIN_BYTES_TO_FLIP, MAX_BYTES_TO_FLIP)
bytes_flipped = 0

while bytes_flipped < bytes_to_flip:
byte_pos = randint(RANGE_START, RANGE_END)
#print "byte_pos: 0x%x" %byte_pos
byte_new = chr(randint(0, 255))
mapped[byte_pos] = byte_new
bytes_flipped += 1

mapped.close()

变异效果:

最后挖到一个TTF字体虚拟指令漏洞:

1
2
3
4
5
6
7
orig file:
2F90h: 00 3F C5 CD 2B 10 C1 10 DE 3F C5 【CD】 2B 10 C5 10

poc file:
2F90h: 00 3F C5 CD 2B 10 C1 10 DE 3F C5 【DD】 2B 10 C5 10

glyf table -> SimpleGlyf[] -> Instructions('0xCD' => ‘0xDD') -> MDRP指令

同样的,你也可以写个doc、docx等office文件格式解析并变异的扩展fuzzer,比如利用olefile库(但只支持修改同等大小不变的doc,要插入或删除需要自行实现),或者通过COM接口来实现操作。

比如之前有段时间doc中的公式编辑器存在很多漏洞,你就可以专门针对Equation Native流作fuzzing。

最后放两张图(riufuzz是自己对honggfuzz二次开发的版本,后面有机会再讲):

honggfuzz漏洞挖掘技术深究系列(3)——Fuzz策略

honggfuzz在对输入文件进行变异前,会先创建个临时文件名(honggfuzz+pid+time),然后将输入数据变异后写入临时文件。

fuzz策略的实现主要集中在mangle.c中,在循环的fuzzloop函数中,会根据用户的选择的fuzz方式来调用动态fuzz或者静态fuzz的方法,但最后都是调用mangle_mangleContent来变异文件数据:

跟进mangle_mangleContent函数:

重点就在于后半部分,它会随机选择变异函数进行处理,更改的字节数也是随机的,根据用户指定的mutation变异率来定,即允许变异文件大小的百分比,变异函数列表如下:

这些函数都是在mangle_init中初始化,各函数之间也会相互调用:

把这些函数过一遍就是honggfuzz中所有的文件变异规则了,如果想实现自己的fuzzer,这些规则来扣出来用Python实现一遍,顺便把afl的规则也扣过来就更完美了,下面是我之前写office fuzzer时的半成品代码,最后偷懒直接用radamas去实现变异了:

再回到刚才的变异函数列表,我们一个个走读源码。

1、mangle_Resize函数:

用空格填充随机位置

2、mangle_Byte函数:

向随机位置写随机的uint8类型的数据

3、mangle_Bit函数:

取随机位置的数值做位翻转

4、mangle_Bytes函数:

在随机位置覆盖写2~4字节数据

5、mangle_Magic函数:

取各种边界值进行覆写,这些边界值部分跟AFL还不一样,我在自己的fuzzer里面把它们作了整合。由于边幅所限,我省略了不少边界值:

6、mangle_IncByte函数:

取随机位置的数据加1

7、mangle_DecByte函数:

取随机位置的数据减1

8、mangle_NegByte函数:

取随机位置的数据取反

9、mangle_AddSub函数:

取随机位置的1、2、4或8字节的数据长度作加减操作,操作数取 rand(0~8192)-4096

10、mangle_Dictionary函数:

变异目录名,也是随机取文件夹名称进行变异,如果有多个目录,那被变异的目录数也是随机的

11、mangle_DictionaryInsert函数:

在目录的随机位置中插入随机数据

12、mangle_MemMove函数:

取随机位置的数据拷贝随机长度的数据,里面就是调用memmove函数实现的

13、mangle_MemSet函数:

取随机位置、随机大小,用UINT8_MAX数值填充

14、mangle_Random函数:

取随机位置、随机大小的缓冲区,用随机数填充

15、mangle_CloneByte函数:

取两处随机位置的作数据交换

16、mangle_Expand函数:

文件末尾扩展随机长度的空间,用空格填充,然后在随机位置,取前面的随机长度作数据拷贝

17、mangle_Shrink函数:

删除随机长度的文件内容

18、mangle_InsertRnd函数:

在文件的随机位置插入随机长度的数据

19、mangle_ASCIIVal函数:

在随机位置覆盖32字节的随机数

总结

在Fuzzing过程中,很多变异规则是共用的,可以参考一些主源的开源软件,比如afl\peach\honggfuzz\libfuzzer,提取规则作整合,然后写个自己的fuzzing框架,在后面作针对的fuzzer时,可以直接套用。

从上面的fuzz策略可以总结出常规的变异规则:

  • 随机数据替换
  • 数据值增减
  • 已知边界值替换
  • 插入随机数据
  • 删减文件内容
  • 目录变异
  • 数据拷贝覆盖
  • ……

知识的诅咒

一、过于装逼的演讲 = 废话

无论是演讲还是写文章,你对一帮不懂安全技术的人讲溢出,讲UAF漏洞利用,那纯粹是浪费时间。

早年觉得分享就该讲一些多数人不懂的东西,尤其是那些自认为高深的技术,让大家听得云里雾里的,这样才显得自己技术牛逼。

但实际上,别人听不懂的东西,对其而言,均是废话。

当我们对一件事物过于了解之后,往往意识不到自己对它的思考有多么的抽象。

比如“辐射”一词,大家常常说手机有辐射,X线有辐射,但又有几人能解释清楚什么叫“辐射”呢?

正是我们高估了一般读者/听众对我们所处知识世界的熟悉程度,才造就了“知识的诅咒”。

二、高学历造就知识面的狭窄

鄙人深深地觉得,高中时代就是我们知识面最丰富的时代,那时候真的是上知天文,下知地理,中知英汉语,左手历史,右手政治,无所不会,无所不能,虽然也常常只是勉强及格,但回首这二三十年,知识面依然还是没广过高中啊!

之前也面试过一些博士生,有些博士几年只搞个TLS,或者只搞个android app的数据流找隐私泄露之类,这种搞学术研究并无问题,但一旦想进入企业就很难。

这里也不是鄙视博士生,只是知识的深度和广度的选择而已,但这得看你未来选择的方向而定了。

三、学医历程:从“一身病”到“百病不侵”

学医的前一两年,学到啥病就觉得自己可能就有这病,最后就是感觉自己一身病,我们一般叫这为:“学医综合征”。

到后来下临床了,见过的病多了,更多恶心的、血腥的、神经的病症都见了个遍,自己抗恶心能力提高的不是一两万点啊,而且慢慢地认识到,其实世上很多疾病都是没法治的,如果你们有认识肾内科的医生朋友,问问他们就知道啥叫“挫败感”!

慢慢地,就觉得只要不死不残的病,都不算事。

四、贩卖知识焦虑

最近关注的一批公众号,开始文末各种课程广告,学英语、学写作、学开发各类广告满天飞,利用的正是人们对知识的焦虑。

之前的文章也提到过,多数的付费知识很多系统化的学习到知识,之前也买过一些讲书的、或者一些通用技能课程,一开始觉得挺有道理的,最后细想下,其实几乎都没有任何可实践性的方法,很多讲的最后还是废话。当然,有些个别付费知识还是不错,不过我所依赖的付费知识只有书本。

五、知识学习中的套路与陷阱

以前还没接触过电脑的时候,就听说要先练五笔打字,要练五笔就得先背字根口诀:
“王旁青头戋(兼)五一, 土士二干十寸雨……”

不过最后我没背也练成了五笔,全靠实际打字练习练出来的。

熟练之后,所有文字均会变成脑海中的一个手指的动作,所有关于五笔的知识全变成一种抽象化动作,无什么字根,无什么键盘位……
所以,很多时候网上说:学习A就得先学习B不一定靠谱,只有实践出真知。

记得以前大学时打算学习数据结构与算法,网上查了下,说需要先学习离散数学,然后我就得真去图书馆翻离散数学,最后没看几页就放弃了。

这事跟别人说“学Java要先看《Java编程思想》”一样,谁会在无基础的情况下硬看得下去啊!

可见找到一种适合自己的学习途径才是最好的方法。自己学得哪本书好,就学哪本书,哪来那么多规矩。

能够为己所用,则为知识;但倘若被知识所用、所卖、所困,则为诅咒。

honggfuzz漏洞挖掘技术深究系列(2)—— Persistent Fuzzing

上篇《honggfuzz漏洞挖掘技术深究系列(1)——反馈驱动(Feedback-Driven)》讲到基于软件的代码覆盖率驱动fuzzing的方式,除了软件还有硬件方式,即基于Intel BTS (Branch Trace Store) 或Intel PT (Processor Tracing) 去计算代码覆盖率,同时要求Linux内核>=4.2,这种方式的最大好处是完全由硬件支配,无所谓软件是闭源还是开源。由于硬件环境受限,我也一直未使用过,有此条件的同学可以试下。

本篇主要讲下持久型fuzzing(Persistent Fuzzing),即fuzzing API,这种方式会更精准和高效的。

先看使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
$ cat test.c
#include <inttypes.h>
#include <testlib.h>
extern int LLVMFuzzerTestOneInput(uint8_t **buf, size_t *len);

int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) {
_FuncFromFuzzedLib_(buf, len); // 目标函数
return 0;
}
$ clang-4.0 -fsanitize-coverage=trace-pc,indirect-calls,trace-cmp fuzzedlib.c -o fuzzedlib.o
$ clang-4.0 test.c fuzzedlib.o honggfuzz/libhfuzz/libhfuzz.a -o test
$ honggfuzz -z -P -f INPUT.corpus -- ./test

这里用到几个编译选项:

  • trace-pc:追踪执行过的基本块BB,在每个edge中插入__saitizer_cov_trace_pc()函数,可定义该函数作为相应的回调处理
  • indirect-calls:在每个间接调用中添加PC追踪,与前面的trace-pc或trace-pc-guard联合使用,回调函数:__sanitizer_cov_trace_pc_indir
  • trace-cmp:追踪每个比较指令和swith语句

以trace-pc为例,测试代码如下:


用trace_pc编译:

可以看到自定义的函数被执行,输出执行过的不同pc地址,其它编译选项的用法同上。
下面是honggfuzz对各个回调函数的定义情况:

然后就是记录代码覆盖率情况并进行统计,跟驱动反馈的方式一样了。

再回头看使用示例中的LLVMFuzzerTestOneInput函数,honggfuzz是如何处理它的呢?

通过for无限循环调用目标函数进行Fuzzing,其中参数buf,即样本文件内容,len是数据长度。

最后根据发现的新路径,将相应的样本作为新样本继续fuzzing。

从攻防角度谈黑客与程序员

常言道:“未知攻,焉知防”,然,“知攻未必知防”!

01 — 黑客的敌人是程序员

黑客所要攻破的任何一款产品,无论是网站、软件还是IoT硬件产品,这些都是由程序员开发的,所以他们要攻破的其实是程序员的安全思维缺陷。

所以说,黑客的敌人是程序员。但如果黑客本身就是程序员呢?

02 — 会开发的黑客:知己知彼,百战不殆

有时某些大厂的漏洞致谢公告放出后,可能会出现连续几个月,某类漏洞突然暴增。因为程序员也常常会出现习惯性错误,比如习惯用memcpy危险函数,且不校验size,那么有一必有二,有可能顺藤摸瓜找到一堆此类漏洞。

上面这种情况就是黑客从程序员的角度入手,去寻找突破口。

在之前的工作中,也遇到过一些不懂安全的程序员,在转入安全漏洞挖掘领域后,能够快速上手,挖到不少Android等大厂产品的漏洞。这也利益于此前的开发基础,具备开发能力的程序员比只懂安全的技术人员更容易在安全道路上走得更远更深。

黑客是否需要精通开发?这是个老生常谈的问题了。

如果你去看一些黑客大牛或漏洞挖掘高手写的代码,你会发现有时他们写得也跟我们一样烂。

但也不是说搞安全就不需要学编程开发了,而是必学课程。只是,很多情况下,黑客具备的是基础开发能力,而非开发产品的能力,两者还是有区别的。但其实也不是绝对,我说的只是一种普遍情况。

对于安全从业人员,掌握基础开发能力是必备的,而是否具备产品开发能力成了一道分水岭。即,产品开发能力是加分项,而非必备项。

03 — 懂安全的程序员:无存在感

先来看一幅漫画,估计这是许多安全开发同学的心声:防御住了,用户无感知;未防御住,用户就开始骂娘了,或者受公司处罚。

程序员开发的产品出安全事故,肯定会受到处罚。大家常调侃道:CSO就是用来背锅的,多少有些道理。

但如果懂安全的程序员开发出具有高安全性的产品,对用户,甚至对领导而言,都可能是无感知,跟不存在一般。

常说赏罚分明,但上述普遍情况明显是不对等、不完善的赏罚机制。所以现在也有一些公司慢慢地增设“开发之星”、“质量之星”之类的奖励机制,也算是对此的一种弥补措施。

04 — 攻防不对等:攻击的是短板,防御的是体系

黑客攻击时,只需要找到系统的一处短板,就有可能攻破系统。

业务防御时,则需要构建全面的防御体系,比黑客掌握更加全面的攻击点和防御策略,而非单一的技术点或者功能模块。

因此,防御比攻击更难,两者是不对等的。

有些大厂,比如微软就设有“纵深防御”奖励机制。纵深防御,通俗地讲,就是层层防御,一层防御失效后,仍有备用的其它防御策略生效。

比如电影《侠盗联盟》中,去城堡偷项链的场景:

翻过城墙后 =》还有保安 =》绕过保安后还有监控 =》绕过监控后还有防盗门 =》 进门后还有看门狗 =》最后还有指纹验证

05 — 知攻未必知防

常言道:“未知攻,焉知防”,对此我一直深信不疑。

但这是必要不充分条件,因为现在互联网越来越方便,工具化、教程化的资源网上遍地都是,很多脚本小子随手就可以拿来入侵网站,但让他们提供防御方案,基本都要歇菜。

之前面试过几个做渗透测试的同学,当问某个WEB漏洞的防御时,对方直接说,他只做攻击,不做防御,不懂这些。

对于企业而言,让你做攻击测试的目的,还是为了提前作防御,如果只知攻,不懂防,又有多大价值呢?

06 — 结语

对于不懂产品开发的黑客,以及不懂安全的程序员,我们均表示理解;对于即懂安全又懂开发的同学,我们则爱之、惜之。但无论你是黑客(白帽子:无任何违法乱纪行为),还是程序员,如对【微信支付安全】感兴趣,均可私信本账号,共讨职业发展大计,承诺每条有效私信必回,欢迎搭讪。

honggfuzz漏洞挖掘技术深究系列(1)——反馈驱动(Feedback-Driven)

反馈驱动:通过监控样本触发的代码覆盖率,进而改进输入样本以提高代码覆盖率,增加发现漏洞的概率。

目前业界中基于代码覆盖率的最著名的三大Fuzzer,当属Google开发的AFL、libfuzzer和honggfuzz,且他们都是开源的,在github上都可以搜索到。

相信很多搞fuzzing的同学都听过或用过这三款工具,本系列主要讲honggfuzz的原理与应用,之前把honggfuzz源码都阅读过一遍,并且二次开发过,该工具的挖洞效果还是不错的。

01 — 关于代码覆盖率的那些事

计算代码覆盖率情况就需要有个计量单位,通常有3种:

  • 函数(Fuction-Level)
  • 基本块(BasicBlock-Level)
  • 边界(Edge-Level)

1. 函数(Fuction-Level)

先说下函数,这个很容易理解,就是代码执行时调用到哪些函数,但是函数里面的具体代码行却不作统计,相对比较粗糙但高效的统计方式。

所以,通常的统计方式是用基本块,简称BB。

2.基本块(BasicBlock-Level)

什么是基本块,直接看下图就很容易理解了。

IDA中每一块代码就代表着一个基本块,就是以指令跳转为作划分界限的。

3.边界(Edge-Level)

edge本身就涵盖了基本块部分,唯一的差别是edge多记录了一些执行边界的信息。比如示例代码:

在IDA中可以看到A、B、C这3个基本块,但当a为假时,程序就会从A执行到C。

前面基本块的方式就无法确切地知道是否曾从A执行到C,尤其是该段代码被多次执行的情况,就更无法知道,这时edge覆盖方式就出现了。

edge会在A跟C之间建立虚拟块D,通过判断D是否执行过,来确认是否曾从A执行到C,这种方式也会比较消耗性能就是:

统计代码覆盖率又分两种情况:

1.【有源码】:直接使用SanitizerCoverage即可,在编译选项中添加相应的覆盖率统计方式,比如基本块统计方式可以添加:

CFLAG=“-fsanitize=address -fsanitize-coverage=bb”

2.【无源码】:使用Pin、DynamoRIO等二进制插桩工具去hook统计,或者pediy改指令的方式去监控也是可以的,本系列的后续文章可能会细说。

02 — Honggfuzz反馈驱动原理

本文我们默认以基本块作为代码覆盖率的统计方式,比如采用如下编译选项:

ASAN_OPTIONS=coverage=1:coverage_direct=1 -fsanitize=address -fsanitize-coverage=bb

执行后它会生成两个文件:.sancov.map和.sancov.raw,这是honggfuzz处理过的scancov文件。

其中.sancov.map文件记录是模块信息,.sancov.raw文件则记录着执行过的基本块地址信息:

1、首先,honggfuzz会先去分析.sancov.raw文件,保存命中的基本块地址,记录BB数,以及总共的BB数:

2、计算代码覆盖率 = 命中BB数 / (命中BB数 + 未命中的BB数)

以下是旧版honggfuzz关于coverage的显示内容格式:

新版的变成只记录BB/edge数:

3、发现新路径或者新插桩链接库加载,则添加此变异样本为新的输入样本,供后面作进一步变异,以触发更多新路径

03 — 总结

honggfuzz最早创建于2015年,跟AFL的发布时间差不多,AFL的出现可以说在安全界是项里程碑标志,给漏洞挖掘技术带来新的技术革命,网上对其源码分析的文章也到处可见,但对honggfuzz的详细分析还没有,因此才作此系列记录下。

个人也曾给honggfuzz贡献过代码,但由于某次pull request未被采用,于是就自己二次开发不再提交合并。因为我是在macOS下开发,而原作者主要是用Linux,那次提交的功能对我很有用,所以只好自己维护一份二次开发版本。

后来自己也在Windows和macOS平台上扩展了许多新功能,在本系列文章中,后面可能会单篇聊一聊,也感谢作者robertswiecki开发出这么好用的fuzzer,让我借此刷了不少CVE。

编译原理在安全领域的应用

什么是编译原理

上图是我从《编译系统透视:图解编译原理》里面扣出来的,基本包括编译原理的各个主要方面,从中可以对编译原理有个大体认识。

专业点来讲,编译原理就是介绍编译程序构造的一般原理和基本方法,内容包括语言和文法、词法分析、语法分析、语法制导翻译、中间代码生成、存储管理、代码优化和目标代码生成。

之前在知乎上看到过一份视频,讲的是程序员编写的代码如何被计算机识别,并在CPU上运行,如果你未曾学习编译原理,推荐观看一下(视频时长11:34)。

每个信安学生总有一份叫“污点分析”的毕业设计

污点分析是指将程序从外部获取的数据标记为污点,然后观察污点在程序执行过程中的传播,从而得到程序中的信息流等信息,里面涉及编译原理中的词法语法分析会多一些,主要被应用于恶意软件分析、攻击代码检测等软件安全性分析研究中。

记得刚参加工作那会,经常到网上搜索安全论文,发现很多信息安全专业的学生大多有做过一份关于“污点分析”的毕业设计,你现在上CNKI就可以搜索到很多,尤以硕士毕业论文居多。

当时我几乎翻遍网上所有能找到的“污点分析”论文,最后我得出几点结论:

  1. 不求效果,但求理论高深。之前搞flash xss检测,花了几天时间用一堆grep实现的检测工具,挖不了不到大厂的漏洞,包括淘宝、京东这些电商;但也有同学花几个月搞flash actionscript污点分析,虽然有点作用,但从工作效率和结果看,有时高深的理论不见得适合工业界,虽然我的几行grep没法拿来写论文。

  2. 千篇一律,天下文章一大抄。下载了一堆同主题方向的论文,发现有的内容改都没改,互相乱抄。

  3. 以漏洞挖掘的主题居多,但最后总要以挖洞效果结尾,有0day自然最好,没0day就找几个历史漏洞重现下(很多可能是特例化处理的),没历史漏洞就对比几个主流开源的挖洞工具,对比效率、性能等等,最挫的就是连效果都没有就收尾了。

有过此般经历后,我已很少再去翻国内的安全论文了,学术论文可能还是以翻阅国外的为主。

成熟的技术 ≠ 成熟的产品

污点分析技术最早是在1976被提出的,2005年左右开始,污点分析应用于二进制漏洞挖掘的研究火了好多年,其实技术已经相对成熟。

确实有一些人通过污点分析挖掘到不少有价值的主流软件漏洞,但这项技术要落地为一款安全产品还是有很多问题的。

首要问题就是误报率,之前有同学开发出基于污点分析的源码审计工具,每次外部报告漏洞的时候,复盘时总说能检测出来,只是没人工跟进。但是,检测出来的有成千上万条告警,需要消耗大量人力去排查这又有什么价值呢,本质上,还是没发现嘛!

这也算是安全运营的问题,深以为,凡是能检测到,但无法人工或自动跟进推动问题解决的,都是徒劳的。

相信有很多公司都有出过污点分析的安全审计工具,收费的或者开源的,国内的此类安全产品很多最后都不了了之,所以说成熟的技术不等于成熟的产品。

国外比较著名的商业源码审计工具就是Coverity和Fortify SCA,算做得最好的业界同款产品,但用过的人都知道,检测出来的问题,还是需要投入很多人工成本的。

反编译与加固

搞逆向的同学都知道IDA、JEB这些著名的逆向工具,平时在无源码的情况下,我们直接通过它们来分析程序逻辑,无需知晓其中涉及各类反编译技术。

反编译算是编译的逆过程,即将可执行程序转换成程序源码的过程,如果是转换成汇编语言,我们通常称为反汇编;如果是其它语言的(比如C、Java、C#等等),我们统称为反编译。

不同语言的编译过程还不一样,比如Java是通过JVM虚拟机将字节码转换成CPU认识的指令,而C是直接由编译器转换机器码供CPU执行的,因此它们的反编译过程也不一样。

所以如果不懂编译原理,又如何开发反编译工具呢?

有了反编译,自然就有了防反编译的工具,因此造就了各种加壳工具的出现。

未知攻,焉知防。要开发加固工具,就需要知道反编译原理,要知道反编译原理,又需要知道编译原理。

编译原理 <---> 反编译原理 <---> 加固原理

编译原理在漏洞攻防中的应用

搞文件Fuzzing,我们可以在样本(收集、筛选、精简)和Fuzzer(策略、方向等)上面下功夫,甚至简单地暴力fuzzing文件都可以挖到漏洞。

但对于JavaScript、CSS、Flash ActionScript这些脚本呢,直接简单地暴力变异文件根本无法进入正常的解析,做的大多是无用功。

因此我们需要一个能够准确生成代码的语法生成器,再用它生成fuzzing样本,这里主要涉及编译原理中的语法分析。这方面有著名的开源工具funfuzz(https://github.com/MozillaSecurity/funfuzz)、domato(https://github.com/google/domato),下图就domato生成js代码的语法模板片段:

除此之外,通过对clang/gcc等开源的编译工具对目标源码进行插桩,以帮助监控fuzz样本的代码覆盖率,反馈给fuzzer作改进,以进一步提高代码覆盖率,这块叫“驱动反馈(feedback-driven),比如著名工具就是afl、libfuzzer、honggfuzz,以后有机会可以专篇讲解此技术。

还有通过编译器增加防漏洞利用的机制,比如GS、CFG等安全机制,在对抗漏洞攻击上也起到了不小的作用。

所以通过研究llvm、gcc等编译项目,对漏洞攻防领域也是有一些可作为的地方。

总结

编译原理在逆向工程、漏洞攻防、软件开发等诸多领域有所应用,有时就看你怎么使用,也并不是每个人在安全工作领域中有机会运用到,但技多不压身,不妨多储备点知识,以免到了“书到用时方恨少”的地步。

若干年前,挖洞不用污点追踪都不好意思写论文; 现在搞安全不用人工智能都不好意思装逼,能用机器学习的,坚决不用正则;能用DNN的,坚决不用LR回归;能用无监督学习的,坚决不用有监督学习……但你不试下,怎么就知道grep不行呢?