pwn入门-canary&PIE bypass
pwn入门-canary&PIE bypass
- 主函数没有get shel怎么办?(ret2text)
- 整个程序没有system函数怎么办?(ret2libc)64位程序就这么简单?(ret2csu)
- 静态编译怎么办?(ret2syscall)
- 保护不全有没有好办法?(ret2shellcode)
- 栈与非栈的格式化字符串
canary
canary:是一种用来防护栈溢出的保护机制。其原理是在一个函数的入口处,先从fs/gs寄存器中取出一个4字节(eax)或者8字节(rax)的值存到栈上,当函数结束时会检查这个栈上的值是否和存进去的值一致
canary保存在tls结构体中。
all和部分是有区别的
all是在所有方法都会加Cancary,部分只会在char那边加
1 | #include <stdio.h> |
绕过方法:
格式化字符串绕过:canary通过格式化字符串读取canary的值
Canary爆破(针对有fork函数的程序)
fok作用相当于自我复制,每一次复制出来的程序,内存布局都是一样的,当然canary值也
一样。那我们就可以逐位爆破,如果程序崩溃了就说明这一位不对,如果程序正常就可以
接着跑下一位,直到跑出正确的canary劫持stack_chk_fail
修改got表中stack chk fail函数的地址,在栈谥出后执行该函数,但由于该函数的地址
被修改,所以程序会跳转到我们想要执行的地址
TLS
stack smashing
格式化字符串
%s就是把一串字符串打印出来,而判断字符串结束的依据是结尾的x00,所以如果我们把输入的内容和canary连在一起,那么打印的时候就会将canary一起打印出来。需要注意的是canaryl的最后一位一定是%00,用于防止连带输出,所以我们改的时候要把canary最后一位也改了
在输入1后连带着把canary的值打印出来(c语言总是将00作为结束而上一步把00改为b)
简单来说就是,把\x00覆盖掉,让printf把canary也输出出来,我们接受之后的7字节或者3字节
例题:
1:print,2:read0x64,3:break
下断点read处,运行,stack40
输入长值
exp:
z
:用于发送/接收数据或与程序进行交互。
backdoor = 0x4007d6
:这是程序中后门函数的地址。
canary
:栈保护值(Stack Canary),通常用于检测栈溢出攻击。
1 | ru = lambda x: p.recvuntil(x) # 接收直到指定字符串 |
1 | 1.执行单条汇编指令 |
0x18是buf到canary的长度,之后是8字节canaey,8字节rbp,8字节返回地址
canary的值在栈上,位于rbp寄存器之上,也就是说buff之后的8字节就是cancry,在之后是rbp最后是rsp(函数返回地址)
在 pwndbg
中,输入 stack 40
的目的是查看当前栈顶(rsp
指向的位置)附近的 40 个栈单元(每个单元通常是 8 字节)。每个单元显示一个内存地址及其内容。
1 | 高地址 |
ebp/rbp之上是返回地址
返回地址和canary间还有8个字节
PIE
PIE技术是一个针对代码段(.text)、数据段(.data)、未初始化全局变量段(.bss)等固定地址的一个防护技术,如果程序开启了PE保护的话,在每次加载程序时都变换加载地址,从而不能通过ROPgadget等一些工具来帮助解题
ctrl+s,shift+f7
所有代码段的地址都只有最后三个数是已知的,这里有一点要知道的是,程序的加载地址一般都是以内存页为单位的,所以程序的基地址最后三个数字一定是0,这也就是说那些地址已知的最后三个数就是实际地址的最后三个
数。知道这一点之后我们就有了绕过pi的思路,、虽然我并不知完整的地址,但我知道最后三个数,那么我们是不是可以利用栈上己有的地址,只修改他们最后两个字节(最后四个数)即可。
所以对于绕过PIE保护的核心思想就是partial writing(部分写地址)
例题
断点下到return
在启用了 PIE (Position Independent Executable) 的程序中,程序的基地址在每次运行时可能不同。
$rebase(0x933)
的作用是:将相对地址 0x933
映射到实际的内存基址。
$rebase(0x933)
会将 0x933
加到当前程序的基地址上。
在 x86_64 中,栈地址一般是8字节对齐。在很多情况下,我们只需要覆盖低字节或低两字节,以改变特定的地址偏移。
地址是 0x7fffffffdcf0
,如果只想修改 f0
部分,那么实际上需要写入 \xf0\x88
,因为地址是小端序存储的。