首页
社区
课程
招聘
[翻译]一篇安全学习笔记
发表于: 2017-8-19 11:48 9248

[翻译]一篇安全学习笔记

2017-8-19 11:48
9248

我就我看到的安全视频做了一些笔记(作为一种快速回忆的方法)。

这些笔记对于初学者来说可能更有用。

这些笔记并非以难易程度排序,而是以时间顺序(也就是说,最近的在前面)。

 

本文全部文字在“知识共享署名-非商业性使用-相同共享方式4.0国际”(CC-BY-NC-SA)条款执行提供。

 


写于 2017 / 7 / 2

有感于 @p4n74  @h3rcul35  InfoSecIITR #bin 的讨论。我们讨论的是让新手觉得很困惑的如何着手处理比较大的二进制文件,尤其是精简过的(stripped)。

 

不管是要逆向还是要pwn它,为能有效进行漏洞利用,你都要先分析拿到的二进制文件。由于该二进制文件可能被精简过(显示为file),所以要我们得知道从哪开始分析。

 

在寻找二进制文件里的漏洞的过程中,有几种不同的分析方式(就我收集到,不同的CTF战队有不同的风格):

 

1.静态分析

1.1 将代码全部转换(Transpiling)成C

这种分析方式比较少见,但是对于小型的二进制文件来说挺有用的。这种方法就是将整个文件都拖进逆向工具中。在IDA中打开每一个函数(用decomplier view,反编译视图),然后重命名(快捷键:n,重新编辑(快捷键:y)这些函数使反编译代码更具可读性。然后,将这些代码复制/导出成单独的.c文件,这些C文件可以经过编译也能得到和最初那个文件差不多的二进制文件。至此,对源文件的分析就算做好了,可以用来查找漏洞或者其他有用信息了。只要找到漏洞点,利用IDA处理好的汇编和伪码(用Tab实现两者的快速切换,用空格切换函数的图形视图和文本视图)就可以进行exploit的编写了。

 

1.2最小化对反编译的分析

 

我们常常得做这件事,因为二进制文件里的大部分是相对没用的(从攻击者的角度)。你只需要分析可疑函数或者能帮你找到漏

洞的函数。为达此目的,可以从以下几个方面着手:

 

1.2.1 main函数开始

 

现在,很多精简过的二进制文件(stripped binary),对main函数并没有标记(虽然IDA 6.9 以后的版本已经帮你标记了),但是,时间久了你也得自己学会如何从进入点(也就是 IDA默认打开的地方)到达main,并从那里开始分析。

 

1.2.2 查找相关的字符串

 

有的时候,你知道程序会输出某些有用的字符串(比如说,逆向中的“恭喜, flag%s”).你可以跳转到到字符串视图(快捷键:Shift+F12),找到该字符串,用XRefs进行逆向操作(快捷键:x.XRefs能帮你找到该字符串的函数路径,对路径中的函数使用XRefs,直到你找到main(或者你知道的其他点)。

 

1.2.3 从一些随机函数开始

 

有的时候,没有特别有用的字符串,然后你又不想从main开始。这个时候,你可以快速浏览整个函数表,查找看起来比较可疑的函数(比如有很多常量,很多xor等等)或者调用了重要函数(对mallocfree等等进行XRefs),然后你就可以从那里开始了,向前(顺着调用它的函数)或者向后(XRef该函数)均可。

 

1.3 纯汇编分析

 

有时,你无法使用反编译视图(原因可能是:奇怪的架构,或者反汇编技术,或者手动汇编,或者反汇编看起来过于复杂)。这种情况下,只看反汇编视图是最有效的法子了。对于未知的架构,打开自动备注功能是一个非常有用的方法,因为它可以给每一条指令进行注释。另外,节点着色(node colorization)和群节点功能(group nodes functionalities)也超有用。即使你都没有用到这些,只是简单的给汇编语言加注释就能帮到很多。就我个人而言,我更喜欢写python形式的注释,这样我稍后就可以手动将它们转换成python了(对不得不用Z3库的逆向尤其有用)。

 

1.4 使用类似BAP的平台

 

这种分析是(半)自动的,而且对于大多数软件来说通常是有用的,但是很少能直接用在CTF中。

 

2.Fuzzing 模糊测试

 

Fuzzing是获取漏洞很有效的技术。你都不需要完全了解它,只需使用漏洞检查工具(Fuzzer)就可以得到很多触手可及的漏洞,之后再对它们进行分析分类得到真正的漏洞。更多信息参看我的笔记 basics of fuzzing  genetic fuzzing

 

3.动态分析

动态分析可以在使用静态分析找到漏洞之后进行,以便更快的写出exploit。当然,也可以用动态分析来发现漏洞。一般是这样,在调试器中运行可执行程序,尝试着走一遍代码以期找到触发bug的地方。在合适的地方设置断点,然后分析寄存器//栈的状态,我们就可以知道发生了什么。我们也可以用调试器快速确定重要函数。可以这么做:比如,在最开始的时候对所有函数都暂时插入断点,然后分两步走:一步走所有不重要的函数,一步只走重要的函数。第一步经过所有不重要的函数然后取消端点,最后只留下第二步中的重要函数还有断点。

 

我个人的分析方式是,先用静态分析,通常从main(或者,对于不是基于console的应用程序,从字符串)开始,快速找到可疑函数。然后从这里开始向前向后扩展,通常会写注释,重命名,重编辑变量以便反编译。和很多人一样,对于可能有用但现在还未知的函数/变量等我会用 AppleBanana Carrot等这些名称,为便于分析(对我来说,跟踪func_123456这种风格的名字有点难)。我也会用IDA中的结构体视图来定义结构体(和枚举)来使反编译的效果更好。一旦我找到漏洞,我会用pwntools(并用它来调用gdb.attach() )来写一个脚本。这样我对于发生了什么就有更多的控制权。在gdb中,我通常使用gdb的简单版本,虽然我有添加peda命令,如果需要就可以即时加载peda

 

我的风格也一直在变化,因为对于我的工具我用得越来越顺手了,再加上自己写的工具。非常乐意听到其他的分析方式, 以助我更快分析。如果您对本文有任何评论/批评/赞扬,请移步我的Twitter @jay_f0xtr0t.

 

 

写于2017 / 6 / 4

有感于Gynvael Coldwind这个超赞的视频。在这个视频中他谈及ROP的基础,并提了一些技巧。

 

面向返回编程(Return Oriented ProgrammingROP)是最典型的漏洞利用技术之一,用来避开NXnon executable memory, 不可执行内存)保护。微软的NXDEPdata execution prevention, 数据执行保护)。Linux也有NX,这就意味着,在这种保护机制下,你不再可以将shellcode放到堆栈中并使之得以执行。现在,要执行代码,你得进入之前已经存在的代码区(main binary 主库,或者它的库——Linuxlibcldd等;Windowskernel32ntdll等)。ROP是通过复用已经存在的代码片段产生的,并指出如何将你想要执行的代码融入到这些代码片段中。

 

起初,ROP是从ret2libc开始的,然后通过可以使用更多的小代码块而逐步发展。有人说,ROP已“死”,因为新增的保护技术。但是在很多场合中依然有用(而且在很多CTF中是必须的)。

 

ROP最重要的部分是gadgets(指令片段)。Gadget是“可用于ROP的代码块”。通常是以ret结尾(其他类型的gadget可能也会有用,比如以pop  eax; jmp eax等结尾的)。我们将这些gadget串在一起形成exploit,成为ROP链。

 

ROP最重要的前提是你对栈有控制权(也就是说,栈指针指向你能控制的缓冲区)。如果这个条件不成立,你需要使用一些技巧(比如,stack pivoting栈转)来获取控制。

 

你要如何提取gadget?使用可以下载的工具(比如, ropgadget)或者在线工具(比如, ropshell)或者你自己写(对一些比较难的题目,这个可能更有用,因为你可以有针对性的设计)。最基本的,我们需要可以跳转到这些gadget的地址。这里就有一个ASLR(地址空间配置随机加载)的问题,在这种情况下,在真正可以执行ROP之前你会遭遇地址泄露。

 

现在是,我们要如何利用这些gadget形成rop链?我们首先寻找“基础gadget”。这类gadget可以帮我执行一些简单的任务(例如,pop ecx; ret,通过放置这个gadget可以将某个值加载到ecx中,在加载完该值后,就返回到链尾)最有用的基础gadget通常是“指向寄存器”,“将寄存器的值放置在指向某个寄存器的地址中”。

 

我们可以从这些最基础的函数开始逐步获取高级功能(类似下面的文章,exploitation abctionstra)。例如,利用“指向寄存器”,和“在某个地址存值”这个gadget,我们可以构造函数实现将特定的值传入特定的地址。用这个函数,我们可以将任意的字符串传到内存的任意位置。有了这么一个函数我们就差不多大功告成了。因为我们可以在内存中构造任意数据结构,也可以用任意的参数调用任意的函数(因我没有可以指向寄存器,将值保存到栈中)。

 

从基础函数开始而不是从可以实现复杂功能的函数开始的一个重要原因是,减少出错的几率(这在ROP中很常见)。

 

还有很多关于ROP的想法,工具,技巧,但这可能是另一篇笔记的内容了,下次吧: )

 

PS: Gyn的博文Return-Oriented Exploitation值得一读。

 

 

 

写于2017 / 3 / 27,增加于2017 / 3 / 29

有感于Gynvael Coldwind这个视频。在视频中他谈论了遗传模糊背后的理论,并开始组建一个简易的遗传模糊测试工具。之后,他在这个视频中完成了其实现。

 

“高级的”模糊测试(相较于我在 "Basics of Fuzzing" 提到的盲测试器,blind fuzzer)也修改/变异字节,但是比起盲测,它做得更智能一些。

 

我们为什么需要一个遗传模糊测试器呢?

 

对于盲测试器来说,有些程序可能会比较难搞,因为有的漏洞可能需要几个条件同时满足才会触发。在盲测试器中,发生这种事的可能性非常低,因为它不知道它是否有些许进化。举个具体的例子,如果有这么一段代码:if a: if b: if c: if d: crash!(让我们称之为CRASHER),这段代码表示需要满足四个条件才能crash该程序。然而,盲测试器可能就无法通过条件a, 因为四个变异a,b,c,d同时发生的可能性太低了。事实上,即使它做了a进化了,下一个变异可能有回到!a,因为它对程序一无所知。

稍等,那么什么时候这种情况才会出现?

 

举个例子,这在文件格式解析器中就很常见啊。为获取某些特定的代码路径,必须同时检查好几项“这个必须是这个值,那个必须是那个值,其他的也必须是规定好的”等等。还有,没有一款现实世界的软件是“简单的”,大多数软件有很多很多可能的路径,有些路径只有同时满足几个条件才能到达。因此,很多这类程序路径对于盲测试器来说是不可达的。另外,有些路径是真的不可达,因为没有完成足够的变异。如果这些路径由bug,盲测试器将无法将其找出。

 

那我们可以怎样可以比盲测试器做得更好?

 

考虑上面那段CRASHER代码的流程图。如果某个盲测试器机缘之下满足a,那它也不会意识到自己到达了新的节点,所以它会将其忽略。另一方面,AFL(和其他遗传或“smart”模糊测试器)会做的是,它们能意识到这是新的信息(“一条全新的路径”)然后将这个样本作文一个新的点保存到资料库中。这就意味着,现在这个测试器可以从a开始。当然,从a开始有时也会返回到!a,但更多时候,它不会,而是更可能到达b。这样又到达了一个新的节点,将其加入资料库中。继续这个操作,将会有越来越多的路径得到检查,最后到达crash!。

 

为什么要这样做?

 

将变异的样本保存到资料库中,就能到达之前未能到达的区域,也就能对这些区域进行模糊测试。那既然我们可以对这些区域进行测试,自然能找到这些区域的bug

 

为什么称之为遗传模糊?

 

这种“smart”模糊测试类似于遗传算法。样本的交叉、变异能产生新的样本。我们保留能适应我们测试条件的样本。在这个例子中,条件是“它能到达流程图中的多少个节点?”。保存能到达更多节点的。跟遗传算法不是很像,但是它的一个变体(因为我们保存了所有到达未探查领域的节点,并且没有做交叉)。基本上就是,从已经存在的样本中选择,进行变异,之后进行适应性测试(看它是否能到达新领域),再重复。

 

等一下,所以我们只是记录未到达节点?

 

不,并非如此。AFL记录的是图中的边,而不是点。还有,它不仅仅是说“边被遍历过或没有”,它记录该边遍历了多少次。如果一条边遍历了0124816,…次,它就可以当作是一条新的路径并添加到资料库中。这么做是因为,查看边比查看点更能区分应用程序的状态,用指数级增长来记录遍历的次数能够给到给多的信息(一条边被遍历一次很遍历两次有很大的不同,但是遍历10次和遍历11次的区别就没有那么大了)。

 

那么,在遗传模糊测试器中你要做什么?

 

我们需要两样东西。第一部分叫做追踪器tracer(或者,追踪设备)。它主要是告诉你执行了哪条指令。AFL用一种很简单的方法做到这一点。它跳到编译阶段,在形成汇编语言之后,编译之前,它会寻找基础代码块(通过找结束点,检查跳转/分支指令),并添加代码到每一个代码块以标志该代码已经执行(可能会进入shadow memory影子内存或其他)如果我们没有源代码,我们用其他技术来进行追踪(比如:pin, 调试器等)。事实证明,即使是ASAN也能给出覆盖率信息(具体看查看相关文档)。

 

第二部分是,我们利用追踪器提供的覆盖率信息去追踪刚出现的新路径,并将这些刚形成的样本增加到资料库中,以便将来进行随机选择。

 

追踪器有多种机制,有基于软件的,也有基于硬件的。基于硬件的,拿Intel CPU来说,其内存中有一个缓冲区,它记录了所有进入该缓冲区的基本代码块。这属于内核特征,所以内核不得不支持它并提供APILinux是这样的)。基于软件的,我们通过增加代码,或是用调试器(用临时断点,或者通过单步),或者用内存检测工具(Address SanitizerASAN)的追踪功能,或者用钩子,或者虚拟机,或者其他的方法来达到。

 


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 219
活跃值: (18)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
2
楼主哪来的这些资料
2017-12-7 13:58
0
雪    币: 34
活跃值: (864)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
3
肖进 楼主哪来的这些资料
看雪翻译小组提供。
2017-12-8 10:38
0
雪    币: 965
活跃值: (89)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
谢谢分享,  学习一下!
2017-12-9 11:02
0
游客
登录 | 注册 方可回帖
返回
//