# 0x00 安全素养
# 网络安全法
不要违法,不要违法,不要违法。
参考资料:
https://baike.baidu.com/item/中华人民共和国网络安全法
# 0x01 基础
编程和逆向工程的能力是相当重要的。
# 编程语言功底
-
精通C语言(指针、函数)、C++
-
汇编语言(熟悉常见指令及寄存器等,能够熟练阅读汇编代码)
-
熟练使用python
# 逆向功底&底层原理
-
IDA PRO的使用(或者Ghidra等其它工具)
-
gdb的基本使用(断点、单步、查看内存等)
- 《程序员的自我修养》
- 程序的编译链接原理
- 内存管理
- 进程地址空间
- 函数调用惯例
- …
- …
# 0x02 二进制安全
# CTF
先从CTF入手,学习各种漏洞类型及利用手法,不断刷题、阅读,大一大二专注在CTF比赛中,要求能够解决大部分常规难度的题目。
学习新的题目时,不要先看wp,要先自己去找漏洞,找漏洞是最重要的,漏洞的利用上可以跟着wp一步一步调试。
这里有一些刷题平台:
- https://buuoj.cn/
- https://ctf.bugku.com/
- https://www.ctfhub.com/#/index
- https://adworld.xctf.org.cn/
- https://pwnable.tw/
# PWN
# 配置环境
CTF中接触到的PWN题大多属于LINUX PWN,给电脑装LINUX系统比较麻烦,一般情况下是装虚拟机。
如果想跳过配置环境这一步,可以直接使用KALI LINUX,但还是建议自己装一次环境(日后可能会需要下载安装一些工具,需要会LINUX系统的操作)。
可以参考我的另一篇文章:
pwn环境搭建
# 基本工具
# 逆向辅助类
-
IDA PRO
IDA PRO是一款很好用的反汇编工具,其反编译插件在很多时候能够将代码还原到接近源码的水平,但是IDA的使用比较复杂,需要自己学习。
链接:https://pan.baidu.com/s/1ZN8Kd2QbpnP5qm1QBfxaIA
提取码:j2my -
GDB
GDB是一款功能强大的程序调试工具,是动态调试必不可少的工具,可以很方便的查看堆中链表的状态,各个地址的内容。GDB拥有多个插件(peda、pwndbg、gef等)这些插件提供了一些额外的命令,在可视化和功能上都进行了扩展。
推荐安装一个插件即可,多个插件的切换与系统环境变量相关,自行百度。
# 漏洞利用类
这一类工具几乎都可以在GITHUB上下载。
-
pwntools
pwntools是一个CTF框架和漏洞利用开发库,涵盖了pwn题利用脚本所需要的各种工具。包括方便的IO交互函数,ROP、格式化字符串等利用的自动化工具,shellcode生成器等等,是目前最好用也是仅有的大型pwn利用框架。能节省大量编写脚本的时间。
-
ZIO
ZIO 是一个专门为 CTF PWN 开发的 Python 库,基于 ZIO 可以方便实现对远程服务器上的服务程序进行数据读写操作,也支持对本地程序的数据读写操作。
-
ROPgadget
ROP是一种高级的内存攻击技术,常用来绕过现代操作系统的各种通用防御。Ropgadget用于找寻程序中用来组装ROP链的gadget。
-
checksec
查询程序架构和保护机制的开启状况。
-
one_gadget
分析定位libc中获取shell的地址,在满足特定条件的情况下,仅拥有该地址就可以get shell。而非手动输入/bin/sh和system的地址。
-
seccomp-tools
分析程序中的seccomp安全机制开启的具体情况。
-
...
# 保护机制
# 系统选项
ASLR:地址空间随机化(系统选项)
ASLR有三个等级,对应系统文件中的三个值。
0:关闭ASLR。
1:mmap base、stack、vdso page将随机化,“.so”文件会被加载到随机地址,程序的代码段加载地址将被随机化。
2:在1的基础上增加了heap随机化。
ASLR保证每次程序加载的时候自身和所加载的库文件都会被映射到虚拟地址空间的不同地址处。
# 编译选项
-
NX:堆栈不可执行
主要防止程序直接在非可执行的内存区(堆和栈)上运行shellcode代码。
-
PIE:程序基地址随机化
程序每个段加载出来的基地址都是随机化的。
-
RELRO:重定位
一般分为partial relro和full relro两种情况,前者重定位信息可写(如GOT表),后者不可写。
-
STACK CANARY:栈溢出保护
在栈的缓冲区和控制信息(如 EBP 等)间插入一个 canary word。这样,当缓冲区被溢出时,在返回地址被覆盖之前 canary word 会首先被覆盖。通过检查 canary word 的值是否被修改,就可以判断是否发生了溢出攻击,从而保护程序因为栈溢出而被攻击。
# 常见漏洞
# 栈相关漏洞
学习栈相关漏洞首先要对程序栈的结构,调用机制,以及函数参数传递规则(函数参数入栈出栈顺序)有一定了解。
-
栈溢出
指栈上的缓冲区被填入了过多的数据,超出了边界,从而导致栈上原有的数据被覆盖,如果能让指定内容覆盖到某些位置,就能get shell,通常覆盖栈的局部变量、bp(函数栈栈底指针)、ip(程序返回地址)。
关于栈溢出有几个需要学习的点:
-
当开启NX和CANARY后如何执行栈溢出。
-
覆盖ip:ip存放的是返回到父函数调用处的下一个位置,如果通过栈缓冲区用某一地址a覆盖了ip,在该函数结束后,会跳转到a地址上,从而劫持控制流。
-
覆盖栈中存储的临时变量:在知道两个变量地址偏移时,可以通过一个变量覆盖另一个变良,从而改变另一个变量。
-
关注一些敏感函数:这些函数能够产生缓冲区溢出,从而实现栈溢出。
常见的漏洞函数:
- gets(buff)
- scanf("%s",buff)
- ...
潜在的漏洞函数:
- read
- strcpy
- memcpy
- ...
-
# 栈相关漏洞的利用
-
栈的特殊利用
-
libc信息泄露:main函数栈底存放了返回地址,如果能泄露改返回地址,就能计算出libc的基址,从而得到其他函数地址。
-
通过libc泄露栈地址:在已知libc地址的情况下,可以根据libc中的Environ偏移来计算栈的偏移,从而计算出栈的地址。
-
往栈上写ROP
-
环境变量修改
-
多级指针
-
-
栈喷射
在ROP时无法找到确定的栈地址,便预先在栈中布置大量重复的ROP数据块,只要在返回时位于其中一个数据块就能实现ROP。
并非必须是ROP,shellcode、ROP地址等数据也可以利用栈喷射。
-
栈迁移
溢出的长度太短,尝试改变sp和bp寄存器的值,在新一段地址上建立栈并写入rop链
-
线程栈
现阶段了解一下即可。
# 堆相关漏洞(堆漏洞是CTF比赛重点)
堆主要指用户动态申请的内存(如调用malloc、alloc、alloca等函数)。
学习堆相关漏洞需要了解:
堆基本数据结构chunk(alloced chunk、free chunk、top chunk)。
堆空闲块管理结构bin(fast bin、unsorted bin、small bin、large bin)。
malloc、free函数的基本规则,建议结合源码对照分析。
tcache机制(很重要)。
-
常规堆溢出
与栈溢出类似,在堆上的缓冲区被填入了过多的数据,超出了边界,导致堆中原有的数据被覆盖。通常有以下两种情况:
- 覆盖本堆块内部数据:通常发生在结构体内部,如结构体中数组溢出,就会覆盖后续变量。
- 覆盖后续堆块数据:不仅影响后续堆块的数据,还破坏了堆块的结构。
第一种情况,参照栈溢出即可。
第二种情况,有其他利用方法。
-
Off By One
相比于常规的堆溢出,Off By One只能溢出一字节,在CTF比赛中,Off By One通常位于堆块末尾,溢出的一字节恰好能够覆盖下一堆块的size域的最低位,Off By One本身比较难以利用,通常用来触发Unlink。
-
Use After Free(UAF)
UAF即释放后使用漏洞,当堆指针在释放后为被置NULL,就会形成悬挂指针,在下一次访问该指针时,仍然能够访问到原指针所指向的堆内容。UAF的利用需要具体问题具体分析,来确定其是否能泄露信息或修改信息。
-
Double Free
Double Free主要指对指针存在多次释放的情况,算是UAF中比较特殊的一种,针对用于释放堆块的函数。对此释放能够使堆块发生重叠,前后申请的堆块可能会指向同一块内存。还可以构造特殊的堆结构,从而运用针对堆结构的利用方法。
-
IO_FILE
IO_FILE本身并不是漏洞,而是程序执行 fopen 等函数时创建的结构体。在出现House Of Orange后,陆续出现了利用IO_FILE进行攻击的手法
- FSOP
- Vtable劫持
- 利用缓冲区指针达成任意读写
- …
# 堆相关漏洞的利用
-
Unlink
Unlink是把free掉的堆块从所属的bins链中,卸下来的操作。它是在free掉一块chunk(除fastbin的chunk外)之后,glibc检查这块chunk相邻的上下两块chunk的free状态之后,做出的堆块合并引起的。
Unlink攻击指的是先伪造堆块,在free操作时触发堆块合并,在绕过一系列检测机制后,就可以通过伪造的堆块修改其他堆块的指针,或者实现任意地址写。
-
Fastbin Attack
Fastbin Attack主要针对fastbin的使用和释放机制,对fastbin的间接利用比较多。
直接利用fastbin的方法主要是针对fastbin的单链表结构,更改其后续指针,控制fastbin在下次所要分配的堆块。
-
House Of Orange
使用Unsorted Bin Attack修改IO_list_all来获取shell。
学习House Of Orange需要理解两个概念——Unsorted Bin Attack和FSOP。
-
Large bin attack
覆盖largebin中bk_nextsize指针,在largebin插入unsorted bin时,可以做到往任意地址写一个堆地址的效果
-
堆喷射
堆喷射主要指在堆块中布置好大量重复性的数据,便于目的地址索引到堆上的数据,与栈喷射相似但更为麻烦,在真实漏洞中利用较多,而在CTF比赛中不常出现。
# 格式化字符串漏洞
格式化字符串漏洞主要是针对一些格式化函数(printf、sprintf、vsprintf等)。这些格式化函数利用格式化字符串来指定串的格式。下面给出四个比较关键的格式符:
%x(%lx):替换为参数的值(十六进制)。
%p:替换为参数的值(指针形式)。
%s:替换为参数所指向内存的字符串。
%n:将格式化串中该特殊字符之前的字符数量写入参数中(获取地址的参数)。
同时还要了解参数定位。
例如:printf("%p,%x",&a,&b); 其中的%p和%x对应第1、2个参数。
也可以使用"%d$m"的形式来定位参数列表中第d个参数(从1开始);其中m是前面所说的格式符(x、p、s、n等)。
# 格式化字符串的利用
格式化函数遇到格式符的关键字符后,会按照传参规则去寻找参数来进行替换或修改。如果实际参数数量小于所需参数数量,依然会将对应位置的数值当成参数进行转换,从而触发格式化字符串漏洞。
-
任意地址读
通过前面所说的参数定位“%d$m”,在确定所需信息在栈中的位置(位于第几个参数),通过参数定位可以将该信息泄露出来。
-
任意地址写
主要是利用格式化字符串中的%n对参数进行写入,写入的值是格式化字符串中%n之前的字符数量。
%n修改4字节、%hn修改2字节、%hhn修改1字节。
可以结合%c来修改成特定的值,例如"%100c"会替换为宽度为100的字符。
# 整型漏洞
整型漏洞主要指发生在整型数据上的漏洞,整型溢出指试图保存的数据超过整型数据的宽度时发生的溢出。
-
宽度溢出
整型数据在计算机中的存储一般按字节进行存储,不同的整型数据所需要的字节数也不同,“所需要的字节数”就是该整型数据的宽度,如果数据所要表达的值大于这个宽度就会发生宽度溢出。
宽度溢出主要出现在:
-
整型数据运算
-
整型数据赋值
-
...
-
-
符号转换
符号转化通常用于:
- 将无符号数和有符号数进行强制转换后数值相差过大,从而绕过条件判断。
- 有些函数对参数有特定要求,但使用时并没有严格按照参数的类型进行参数传递。
-
数组越界
由于检查不严格,导致在对数组内存的索引时超出了数组的预设范围,从而访问到其他数据。
如果可以显示数组内容,就可以实现信息泄露;如果可以修改数组内容,就可以实现信息修改。
# 逻辑漏洞
逻辑漏洞主要指程序逻辑上出现的问题,当程序逻辑不严密或者逻辑太复杂,就会导致一些逻辑分支不能正常处理或处理错误。在PWN题中较少出现,主要是竞态条件漏洞。
-
竞态条件漏洞
竞态条件漏洞是指多任务(多进程、多线程等)对同一资源进行访问时,因访问资源的先后顺序不同产生冲突的情况,通过竞态条件漏洞可以实现越权访问、资源篡改等操作。
# 其他平台漏洞
- IOT漏洞
- 虚拟机漏洞
- 内核漏洞
- ...
# REVERSE
Reverse即软件逆向工程,是对编译成型的二进制程序进行代码、逻辑和功能分析的过程。在CTF中主要考察软件静态分析和动态调试能力,常见的逆向目标为Windows、Linux平台下x86、x64二进制可执行程序。
# 能力要求
- 熟悉如操作系统,汇编语言,加解密等相关知识。
- 具有丰富的多种高级语言的编程经验。
- 熟悉多种编译器的编译原理。
- 较强的程序理解和逆向分析能力。
# 逆向分析的主要方法
-
静态分析法
静态分析法是在不执行代码文件的情况下,对代码进行静态分析的一种方法,主要观察代码文件的外部特性,包括文件类型分析和静态反汇编、反编译。通常使用反汇编工具查看内部代码,分析代码结构。
-
动态分析法
动态分析法是在程序文件的执行过程中对代码进行动态分析的一种方法,其通过调试来分析代码、获取内存的状态等,目的在于定位关键代码后,在程序运行的过程中,借由输出信息(寄存器,内存变化,程序输出)等来验证自己的推断或是理解程序功能。通常使用调试器来分析程序的内部结构和实现原理。
# 汇编指令体系结构
逆向分析的程序所使用的处理器架构通常为Intel架构,所以需要对Intel x86和x64指令体系有所了解,包括:
- x86指令体系下:
- 寄存器组
- 汇编指令集
- 数据传送类指令
- 栈操作与函数调用
- 算数、逻辑运算指令
- 控制转移指令
- 特殊指令
- x86应用程序二进制接口
- x64指令体系下与x86指令体系相似,这里主要标注不同点:
- 寄存器组
- 系统调用指令
- x64应用程序二进制接口
# 基本工具
# 反汇编和反编译工具
反汇编工具有很多,但最推荐的是IDA PRO,在反编译方面,IDA自带的Hex-Ray反编译插件也是最好的。
IDA的使用比较复杂,需要自己学习。
链接:https://pan.baidu.com/s/1ZN8Kd2QbpnP5qm1QBfxaIA
提取码:j2my
# 调试工具
调试工具有以下两个重要功能:
-
断点设置
允许用户选在程序中任意位置的某行代码,一旦程序运行到这一行,将知识调试工具停止运行程序,并显示程序的当前状态。
-
代码跟踪(单步调试)
允许用户在程序运行时跟踪他的执行,程序每执行一条汇编代码后都会暂停,允许用户观察甚至改变程序的状态。
调试工具主要根据操作系统来选择
-
Windows——Ollydbg
Ollydbg是Windows下一款具有可视化界面的用户态调试工具,推荐从吾爱破解论坛上下载吾爱破解专用版Ollydbg,这个版本有对抗反调试的功能。
-
Linux——GDB
GDB是一款功能强大的程序调试工具,是动态调试必不可少的工具,可以很方便的查看堆中链表的状态,各个地址的内容。GDB拥有多个插件(peda、pwndbg、gef等)这些插件提供了一些额外的命令,在可视化和功能上都进行了扩展。
推荐安装一个插件即可,多个插件的切换与系统环境变量相关,自行百度。
# Trace类工具
Trace类工具通过一定的方式监控并记录程序的运行,然后使分析者在记录的信息中得到程序的一些动态信息。
-
Strace
Strace使Linux下一个用来跟踪系统调用的工具,可以监控用户空间进程和内核的交互,比如系统调用、信号传递、进程状态变更等。
-
Qira
Qira是一个强大的Trace类工具,可以将程序整个执行流全部记录下来,然后给予用户回溯、查看命中断点的所有指令(即交叉引用)等。
Qira的安装比较难,多多百度/谷歌。
# 常规逆向分析流程
# 1、收集信息
使用strings/file/binwalk/IDA等静态分析工具收集信息,并根据这些静态信息进行google/github搜索
# 2、干扰分析技术
程序为了保护代码,通常会使用一系列技术来干扰逆向分析,这里会介绍一些常见的干扰分析技术。
-
花指令
花指令是代码保护中一种简单的技巧,原理是在原始代码中插入一段无用的或者能够干扰反汇编引擎的代码。花指令主要影响静态分析,在IDA中表现为一些指令无法识别,导致某些函数未能识别,从而无法对这些函数进行反编译。
要破解花指令,可以在IDA中手动将花指令patch成nop空指令,从而去除花指令。如果程序中花指令较多,可以通过分析花指令的特定模式,编写IDAPython脚本对花指令进行自动化搜索和patch。
-
反调试
反调试技术是指程序在运行时会检测其是否处于被调试状态,如果发现其正在被调试,就无法正常运行。
Windows下通过Ollydbg的StringOD插件可以过滤掉大多数的反调试方法。
Liunx下主要利用ptrace系统调用、proc文件系统检测和父进程检测来检测自身是否正在被调试,针对这些反调试方法,常用的方法就是定位到反调试的代码,然后对程序进行patch,在不影响程序正常功能的情况下,跳过对调试器的检测代码。
-
加壳
加壳是指在二进制的程序中植入一段代码。在运行的时候优先取得程序的控制权,这段代码会在执行的过程中对原始指令进行解密还原,之后再将控制权交还给原始代码,执行原来的代码。
被加过壳的程序,其真正的代码是加密存放在二进制文件中的,只有在执行时才从内存中解密还原出来,因此没法对加了壳的程序直接进行静态分析。
在CTF比赛中出现的带壳程序通常为已知的壳,大部分都可以通过使用专用工具或者脚本的脱壳。
可以使用PEiD工具查询该带壳程序用的什么壳,再用对应工具或脚本脱壳。
-
控制流混淆
对于控制流混淆的程序,没有办法直接进行静态分析,也无法进行反编译,而调试器调试也会陷入控制流的跳转混乱中。
对于控制流混淆的程序,通常通过Trace工具记录下程序运行的所有指令,然后再运行这些指令的基础上进行数据流分析。
-
双进程保护(Debug Blocker)
双进程保护是一种在调试模式下运行自身程序的方法。这种保护通常存在两个进程,由父进程调试子进程。实际功能的代码运行在子进程中,但是因为子进程已经处于调试状态,无法再使用其他调试器进行附加操作。同时父进程能够控制子进程,通过处理子进程的异常,可以控制子进程正常运行。
双进程保护技术的难点在于,真正的功能运行在子进程中,若要调试子进程,就需要断开其与父进程之间的链接,但是一旦断开,没有父进程处理子进程的异常,会导致子进程无法正常运行。
对于双进程保护的程序,其父进程通常功能单一,因此先针对父进程,了解其处理子进程异常的逻辑,然后调试子程序,使其脱离父进程之后仍能正常运行,最后在对子进程进行调试分析。
# 3、关键代码定位
在一个可执行程序中,汇编代码数量庞大,所以要能够定位出真正需要分析的关键代码,只有找到关键代码之后,才能对关键代码采用的算法进行分析,理清程序功能,最后针对程序功能,写出对应脚本,解出flag。
这里给出3个常用的关键代码定位法
-
API断点法
例如:在获取文本输入时,窗口类程序获取文本的方式主要是通过GetWindowsText和GetDlgItemText两个API来获取。在输出结果时,会弹出对话框,这时调用的API通常为MessageBox。在这些API函数中下断点,在调试工具中断下来后,通过栈回溯即可定位到关键代码。
-
字符串检索法
-
在IDA中
打开Strings子窗口,通过Ctrl+F输入想要查找的字符串。
-
在Ollydbg中
通过Alt+E,可以查看可执行模块,找到主模块,点击右键,选择中文搜索引擎,根据需要搜索ASCII或者UNICODE。
-
-
辅助工具定位法
针对特定语言或编译器生成的程序,可以用一些辅助工具帮助快速定位案件处理程序的地址。
如:针对MFC程序的xspy,针对Delphi程序的Dede。
# 4、常见加密算法识别
在对数据进行变换时,通常会使用一些常用的加密算法,如果能够快速识别出对应的加密算法,就能更快的分析出整个完整的算法,下面给出CTF比赛中常用的几种加密算法。
-
base64
base64将输入中的每3字节(24比特)按每6比特分成一组,编程4个小于64的索引值,然后通过一个索引表得到4个可见字符。
索引表为一个64字节的字符串:
“ABCDEFGHIJKLMNOPQRSTUVMXYZabcdefghijklmnopqrstuvwxyz0123456789+/”如果在代码中发现引用了这个索引表,基本可以确定是base64。此外,还有一些变种的base64,主要是改变了索引表。
-
TEA
TEA算法是一种常见的分组加密算法,密钥为128比特位,明文为64比特位,主要做了32轮变换,每轮变换都涉及移位和变换。
在TEA算法中有一个固定的常数0x9e3779b9或者0x61x88647。
如果在加密函数中发现了这个常数,基本可以确定是TEA。
-
AES
AES是一种常见的分组加密算法,加密过程涉及4种操作:字节替代、行移位、列混淆、轮密钥加。其中字节替代过程是通过S盒完成一个字节到另一个字节的映射。(S盒的结构自行百度)
如果发现程序中有S盒或者动态生成了S盒,基本可以确定是AES。
-
RC4
RC4算法属于流加密算法,包括初始化函数和加解密函数。其中初始化代码对某一字符数组进行了初始化赋值,且赋值分别递增,之后又对数组进行了256次交换操作。
通过识别初始化代码,可以判断是否为RC4。
-
MD5
MD5信息摘要算法,是一种广泛使用的密码散列函数,会产生一个128位(16字节)的散列值。
MD5加密算法中有个初始化函数MD5Init(),会初始化四个MD5链接变量的整型参数。分别为0x67452301、0xefcdab89、0x98badcfe、0x10325476,当看到这四个常数,就可以怀疑是MD5。
# 5、求解flag
-
直接从内存获取
一些比较简单的题目可以直接查看内存来获取flag,这种题目只需要在特定的地方下断点,然后查看内存即可获得flag。
-
对算法进行逆变换操作
这类题目要根据程序的输出结果,再分析程序的加密算法,然后编写出对应的逆算法得到flag。
-
线性变换的求解
如果加密算法是一个线性变换,output的第i位只能由input的第i位决定,通过获取input[i]的所有可能输入对应的输出output[i],即可求出input[i],对于这种变换,可以进行单字符爆破。
-
约束求解
如果在output=convert(input)之后,需要output满足多个约束条件,这种情况下就属于约束求解。
通常会用到的约束求解器为z3。
# 小结
- 使用strings/file/binwalk/IDA等静态分析工具收集信息,并根据这些静态信息进行google/github搜索。
- 研究程序的保护方法,如代码混淆,保护壳及反调试等技术,并设法破除或绕过保护。
- 反汇编目标软件,快速定位到关键代码进行分析。
- 结合动态调试,验证自己的初期猜想,在分析的过程中理清程序功能。
- 针对程序功能,写出对应脚本,求解出 flag。
# 实战&进阶
一般情况下,大一、大二通过CTF比赛学习相应技术,大三开始接触实战。
根据自己的积累和兴趣,可选取一个方向去深入,例如文件媒体解析类、内核、浏览器内核、IOT、移动端等等。
下面是一些实战&进阶&提升的方式
-
复现漏洞
-
学习漏洞挖掘技术(fuzz)
-
关注顶会议题,尝试寻找攻击面去实践漏洞挖掘
- blcak hat
- poc
-
安全研究漏洞分析
-
甲方
- 对内漏洞挖掘
- 安全攻防
-
病毒分析
-
纯逆向
-
移动安全、安全加固(移动端)
-
游戏安全