PWN
序
PWN知识只有不断的重复,实践才能熟悉掌握。以下知识点以LINUX系统环境下为主要说明(Windows的会有点区别)。
Linux环境下的基础知识
从C源码到可执行文件的生成过程
程序的编译与链接
什么是可执行文件
可执行文件分类PE/ELF
ELF文件格式
附:
关于LINUX(Linux Foundation Referenced Specifications)
区分节和段的存储区域
加载ELF / 查看节和段区分布命令
注:
这里有两条命令和分别查看磁盘和内存中的ELF节和段的分布。
-
磁盘上查看节分布:
objdump -s [elf文件]
-
内存进程上查看段分布:
方法一:
可先 ,./elf文件 &
//&后台进程挂起,显示pid
然后,cat /proc/[pid]/maps
//查看段分布方法二:
gdb ./elf文件
//先gdb调试
vmmap
//查看段分布
段(segment)与节(section)
程序数据在内存中的组织分布
以上例子仅供参照。可以自己写一个demo然后调试一步一步查看相应信息。
大端序与小端序存储
关键寄存器
右图是CSAPP里面的,看不清没关系(网上一大堆),
关键了解最重要的三个寄存器作用:
- RIP控制CPU执行指令
- RSP,RBP控制函数栈帧区域(
栈帧
这里分享两篇硬核文章,就不重复造轮子了,好的资源就得充分利用。)
C语言函数调用栈(一)
C语言函数调用栈(二)
这两篇能帮你熟悉函数递归调用
详细过程,同时了解PWN中栈利用最重要的一环知识—栈帧。
注:
这两篇内容量对于初学者来说有点大,不过别灰心,只要肯多花时间一定能了解的大概(本人当初也是这么过来的,当你想放弃的时候就就参照笨笨的博主,当初我可是看了好多遍的。这样你总会信心倍增了吧,哈哈哈。)最后一点就是学习一起的最好动力就是兴趣(跟你智商高低没多大关系),如果你没兴趣,友情劝退还是别浪费时间了,这世界这么多选择,赶紧找个自己喜欢的方向发展才是对自己最好的。(哈哈哈,太哲学了,赶紧继续正题了)
静态链接与动态链接
- 静态链接
简单举例来说,就是你用的东西都是你自己买好的,专属于你。
- 动态链接
我用的东西都是借别人的,在需要的时候从有的地方借来用用就好了,跟别人共享着使用。
常用汇编指令影响范围(牢记)
-
leave等价
mov esp, ebp;
pop ebp -
RETN 等价 POP EIP; sub sp, n //还起到平衡堆栈的作用
改变ESP 和 EIP(near) -
RETF等价pop eip;pop cs
改变EIP和CS(far) -
CALL 等价 PUSH [EIP+硬编码数]
改变ESP 和 EIP -
JMP 本质只改变eip
intel 和 AT&T汇编格式
这在我别的文章也有专门的讲解,这里简单对照即可。
-
intel
opcode dst, src //源和目的操作位与AT&T正相反 -
AT&T
opcodel src, dst //同时寄存器前多个%,l是代表long长整型
溢出防护机制
全面了解保护机制
1. LINUX漏洞缓解机制介绍
2.推荐作者,分析的都很详细,文章例子在他文章里附的github链接里
PIE(Position-independent code)
- 每次加载时,
程序的基址
和libc的基址
都发生变化。
在计算中,与位置无关的代码(PIC )或与位置无关的可执行文件(PIE)是机器代码的主体,它们被放置在主存储器中的某个位置,无论其绝对地址如何,都可以正确执行 。
PIC通常用于共享库,因此可以将相同的库代码加载到每个程序地址空间中不与使用中的其他内存(例如,其他共享库)重叠的位置。 PIC还用于缺少MMU的较旧的计算机系统上,因此,即使在无MMU的系统的单个地址空间内,操作系统也可以使应用程序彼此远离。
位置无关的代码必须使用相对寻址。
ASLR(Address space layout randomization)
- 每次加载时,
libc的基址
发生变化。
地址空间布局随机化(ASLR)是一种计算机安全技术,可防止利用内存损坏漏洞。 为了防止攻击者可靠地跳转到内存中的某个特定漏洞利用功能,ASLR随机排列进程的关键数据区域的地址空间位置,包括可执行文件的基础以及堆栈,堆的位置 和共享库。
LINUX下关闭ASLR
echo 0 > /proc/sys/kernel/randomize_va_space
NX bit (no-execute)
NX位(不执行)是一项用于CPU的技术,用于分隔内存区域,供处理器指令(代码)存储或数据存储使用,该功能通常仅在哈佛体系结构处理器中才能使用。 但是,出于安全原因,NX位越来越多地用于常规的von Neumann体系结构处理器中。
DEP( Data Execution Prevention)
操作系统的系统级内存保护功能。标记内存页为不可执行。
RELRO(RELocation Read-Only,只读重定位)
这个机制启动等级有部分开启和完全开启。
Canary
对抗canary策略
1,泄露canary值
2,泄露fs:28H内的值
3,覆写fs:28H副本值
4,劫持stack_chk_fail
5,stack smashing
6,逐字节爆破(BROP gadget,相对限制多)
GOT(global offset table)| PLT(procedure linkage table)
GOT和PLT的理解对漏洞成因分析是很重要的,同时理解也是很绕的,分享一下各位大神的分析(望大家多多支持各位作者的分享精神)
1.GOT表 和 PLT表[这篇看完基本做PWN题就能搞懂了]
2.这里附个视频讲解的,给看文字比较难懂的同学
3.二进制漏洞挖掘之栈溢出-开启RELRO[这篇分析的很详细,加深对LINUX保护机制的理解,后半部分理解需要一定的知识积累]
4.Linux二进制ELF程序查找symbol过程分析[这篇跟3类似,从保护机制上分析PLT和GOT的内容]
建议:
为了加深理解印象,最好自己一步一步调试跟进去看看。
过程:
1,gdb调试看看got表,查看哪些函数还未被加载。
2,这里看个PUTS函数,下断
3,断下si跟进
4,来到plt表
5,重新运行,直接n步过
6,继续看看got表
已被填入puts真正地址
7,查看上面plt表中第一个jmp [got表里面的地址](0x804a014)
结论:
plt 表中,jmp [地址]。
第一次还未装载函数时,地址=got表地址(去找到函数真正地址);
第二次执行,地址=函数真正地址。
泄露libc地址
原理:动态连接程序中,本程序中含有的函数达不到需要实现的目的,需要借助动态链接库
中的函数实现其目的。
查找使用的动态链接库信息
原理:由于在分页机制
,在LINUX系统下是以4k
为单位,即12位(2^12=4k),所以利用此规则,即便开启了地址随机化保护机制,地址的后三位(在页中的偏移)
也是不变的。
下图举例说明:
然后在在线libc库查就好了
这里的offset都是偏移RVA
。还需要得出
基址=puts(VA) - puts(offset)
注:
当然方法还有别的,只要达到目的都可行。
工具
ldd
1.概述>
ldd命令 用于打印程序或者库文件所依赖的共享库列表。
2.语法>
ldd(选项)(参数)
选项
--version:打印指令版本号;
-v:详细信息模式,打印所有相关信息;
-u:打印未使用的直接依赖;
-d:执行重定位和报告任何丢失的对象;
-r:执行数据对象和函数的重定位,并且报告任何丢失的对象和函数;
--help:显示帮助信息。
参数
文件:指定可执行程序或者文库。
3.实例>
红色标记处是一个软链接(相当于win下的快捷键)
可到/lib/x86_64-linux-gnu/
目录下
命令:file libc.so.6
查看链接属性
4.原理补充说明
首先ldd不是一个可执行程序,而只是一个shell脚本。
ldd能够显示可执行模块的dependency,其原理是通过设置一系列的环境变量。
ldd显示可执行模块的dependency的工作原理,其实质
是通过ld-linux.so(elf动态库的装载器
)来实现的。我们知道,ld- linux.so模块会先于executable模块程序工作,并获得控制权,因此当上述的那些环境变量被设置时,ld-linux.so选择了显示可执行模块的dependency。
实际上可以直接执行ld-linux.so模块,如:/lib/ld-linux.so.2 --list program(这相当于ldd program)
objdump
objdump 有点像那个快速查看之类的工具,就是以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。对于一般只想让自己程序跑起来的程序员,这个命令没有更多意义,对于想进一步了解系统的程序员,应该掌握这种工具,至少你可以自己写写shellcode了,或者看看人家给的 exploit 中的 shellcode 是什么东西。
常用命令选项:
objdump -D -b binary -m i386 [file]
-D表示对全部文件进行反汇编;
-b表示二进制;
-m表示指令集架构;
file就是我们要反汇编的二进制文件;
同时我们也可以指定big-endian或little-endian(-EB或-EL);
objdump -f [file]
*显示file的文件头信息;
objdump -t file
*输出目标文件的符号表
;
objdump -h file
*显示file的Section Header信息;
objdump -x file
显示file的全部Header信息;
-S file
*输出目标文件的符号表, 当gcc -g时打印更明显;[S大写]
-s file
除了显示file的全部Header信息,还显示他们对应的十六进制文件代码;
-d file
*反汇编file中的需要执行指令的那些section;
-D file
与-d类似,但反汇编file中的所有section;
-R
*显示动态链接重定位信息;
-r
显示重定位信息;
objdump -s libc.so>test.txt
输出到txt文件
- 查看PLT表
objdump -d -j .plt file
- 查看GOT表
objdump -R file
注:
*号的一帮经常用。
readelf
1.概述>
readelf命令,一般用于查看ELF格式的文件信息,常见的文件如在Linux上的可执行文件,动态库(.so)或者静态库(.a) 等包含ELF格式的文件。
2.常用参数>
语法:
readelf (选项)(file)
-h
(elf header),显示elf文件开始的文件头信息。
-l
(program headers),segments 显示程序头(段头)信息(如果有数据的话)。
-S
(section headers),sections 显示节头信息(如果有数据的话)。
-g
(section groups),显示节组信息(如果有数据的话)。
-s
(symbols) 显示符号表段中的项(如果有数据的话)。
-e
headers 显示全部头信息,等价于: -h -l -S 。
-r
relocs 显示可重定位段的信息。
-d
dynamic 显示动态段的信息。
-V
version-info 显示版本段的信息。
附:
命令参数参考:GNU Binary Utilities(英文版)
ROPgadget
概述>
查找文件中一些需要的指令
和字符串
等。
看帮助手册吧
命令:ROPgadget -h
常用命令
ROPgadget --binary [file] --string [...] //后面根据需要自己补充,如下另一个使用例子
ROPgadget --binary 文件名 --only "pop|ret" | grep rdi
GCC
补充常用保护相关参数
-no-pie:关闭地址随机化
-fno-stack-protector:关闭堆栈保护
-z execstack:堆栈可执行
-m32: 生成32位文件
GDB
[附] vim查看文件二进制数据
命令行模式下:
%!xxd [-g 1]
转换成16进制模式看 //[ ]内可选
%!xxd -r
还原