咸鱼,咸鱼~
vote
分析
发现没什么特别的保护:
分析程序,一个结构体:
程序支持添加候选人,为候选人投票,取消投票,票数为负就删除候选人,也可以显示他们的投票信息:
1 | void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) |
在添加候选人能控制malloc的大小,但是malloc出的前两个QWORD几乎不可控,特别第二个是时间不能控制,这也是唯一的对name域进行写入的机会:
1 | void create() |
另外person的count与一个全局的count应该是同步的,但是此处在创建候选人时未对其初始化,在投票处,会在子线程里面对其加一,会存在竞争条件:
1 | unsigned __int64 vote() |
然后show可以输出一些数据,用于信息泄露:
1 | unsigned __int64 show() |
最后就是cancel啦,一个明显的uaf:
1 | void cancel() |
利用
大致如下:
- create六个候选人,list[0x00]=chunk0x00….list[0x05]=chunk0x05,chunk大小为
0x60
,即name size大小为0x60-0x08-0x10=0x48
,并且chunk0x01 03 05
name处要伪造3个chunk,即伪造的chunk偏移为0x10+0x10:p64(0)+p64(0x60)+p64(0x602000+2-0x08)+p64(0)+p64(0x60)+p64(&list[0x08]+2-0x08)+p64(0)+p64(0x60)+p64(0x602038+2-0x08)
- cancel两个候选人,即
free(list[0x01])->free(list[0x00])
则fastbin中:fastbin->chunk0x00->chunk0x01->0
- 为list[0x00]投票0x20次,即fd+0x20,指向了fakechunk0x00,create三个候选人,list[0x06]=chunk0x00,list[0x07]=fakechunk0x00,list[0x08]=0x602000+2-0x08
- 再次cancel两个候选人,即
free(list[0x03])->free(list[0x02])
,为list[0x02]投票0x20+0x18次,此时fd指向fakechunk0x01 - create三个候选人,list[0x09]=chunk0x00,list[0x0a]=fakechunk0x01,list[0x0b]=&list[0x08]+2+0x08=&list[0x0a]+2,(这里似乎会覆盖list[0x0b]但是由于它是最后被赋值,所以list[0x0b]不会被覆盖),此时为name赋值为
'a'*6+p64(0x602028)
即可在show list[0x0b]的时候输出write的地址,泄露出libc - cancel两个候选人,即
free(list[0x05])->free(list[0x04])
,为list[0x00]投票0x20+0x18+0x18次,此时fd指向fakechunk0x02 - create三个候选人,list[0x0c]=chunk0x00,list[0x0d]=fakechunk0x02,list[0x0e]=0x602038+2-0x08,为list[0x0e]的name赋值
'a'*6+map(p64,(alarm,read,__libc_start_main,time,malloc,fflush,setvbuf,atoi,__isoc99_scanf)
,在调用atoi之前会先调用fflush->__isoc99_scanf->memset->read
,其中memset值低2为不可控,那么最好先尝试onegadget……….此处省略一万字
then写出代码,可控制PC,然鹅,4个onegadget都不能用,惨!
1 | #!/usr/bin/env python |
于是换第二种思路:
先控制list使指向free,再通过cancel将其改为system再调用free(实在不想写具体的过程了…两天时间,一次次脑洞大开,发现是坑,起起落落,悲痛欲绝。。。
1 | #!/usr/bin/env python |