这题是一道线程相关的题目,漏洞类型是UAF,或者表述为TOCTOU(Time-of-Check Time-of-Use). 选单系统提供三个选项,分别使添加任务(add),删除任务(delete),和执行任务(go)。 问题出在执行任务的时候: 程序会新建一个线程执行run函数,run函数的操作如下: ![run](https://leanote.com/api/file/getImage?fi
> 谁说fastbin的UAF就只能用fastbin attack来做的 首先这又是一题典型的选单系统,提供增删查改功能,一眼就看出来是UAF了。 ![UAF](https://leanote.com/api/file/getImage?fileId=5b8106c9ab64417198000de6) 但是比较恶心的是下图的部分,程序写死了`malloc`的size为0x20,也就是说我们只能
漏洞的位置十分明显,删除操作后没有把列表中的指针清零,导致可以进行UAF或者Double free。 ![](https://leanote.com/api/file/getImage?fileId=5ab83a33ab644112f1000218) 程序中没有任何输出语句,所以才叫做silent,不过好在也不需要泄露任何地址,程序中已经给了`system`函数,直接调用它的plt地址就可以了
  1. struct user
  2. {
  3. __int64 count;
  4. __int64 time;
  5. __int64 name;
  6. };

漏洞出在cancel函数里面,如果create一个用户后没有投票操作的话,cancel函数free这个指针并不会把指针清零,可以造成一个double free或者是uaf。
malloc一个unsortedbin大小的chunk然后free掉,通过show函数就能泄露一个libc地址。
按照常规的思路做fast bin attack的话会行不通,因为返回来的地址prev位对应结构中的count参数,这个位置我们是没办法直接改写达到返回任意地址的目的的。
然后想到了uaf的做法,既然我们不能直接写,那么可以用vote函数把prev位的地址给加上去。在heap中伪造一个fake chunk,保证这个chunk是free的状态并且能够通过malloc的检查,然后还要把prev位设置成为最终想要返回的地址。

那么我们再malloc两次就能得到想要的地址了,这里当然是写got表,问题是用0x7f的大小试了很久都绕不过,gaintbranch提醒说got表上的0x7f会有干扰的,libc上的就能成功返回。后来又用0x60的大小去试,got表上仅存能够利用的0x60位置都不能用。

后来只好翻到got最前面0x601ffa那边去了,用onegadget一个一个尝试那个got位置可以成功调用,最后好像是在pthread_create那里成功了。

再vote一次即可触发。

vote.py

  1. from pwn import *
  2. import time
  3. env = {'LD_PRELOAD': './libc-2.23.so'}
  4. # p = process('./vote', env=env)
  5. p = remote('47.97.190.1', 6000)
  6. libc = ELF('./libc-2.23.so')
  7. context.log_level = 'debug'
  8. def create(size, name):
  9. p.sendlineafter('Action: ', '0')
  10. p.sendlineafter(': ', str(s