这题如题面所说分为三个部分,难度不高,第一部分考栈溢出,第二部分有点简单的逆向,第三部分考了一点fastbin attack,作为复习基础知识刚刚好
拿到程序,首先做些基本检查:Partial RELRO,有Canary和NX,没有PIE。提供了libc(2.23)。got表里函数挺多,特别是看到了malloc和free,可能要用到堆。全局变量和main基本都保留了符号表,其他函数大多没有。
第一部分直接从main函数开始,是个冒险游戏,大选项有4个分支,结合文本/逆向代码不难发现,文本提到的“三把钥匙”其实是num1,num2,num3三个全局变量,而分支2对应的0x40165b函数要求这三个变量全部为1。分支1进第一次可以设置num1+=2,第二次就会exit(-1)。分支3在num2!=0时会设置num3+=2。分支4在num11==0x204且num1!=0会设置num2+=2,而num11是main函数一开始的时候设置的。这样依次输入516,1,4,2,3,2就可以成功进入0x4013a3。
0x4013a3稍微观察一下就会发现有越界写漏洞,第二个scanf可以直接把一个qword写到任意地址,刚好用来劫持返回地址
memset(local_428,0,0x400);
printf(">> ");
__isoc99_scanf(&DAT_00401ac9_%lld,&local_430);
printf(&DAT_00401ace,local_430);
puts(&DAT_00401ae8);
printf(&DAT_00401b38);
__isoc99_scanf(&DAT_00401ac9_%lld,local_428 + local_430 * 8);
puts(&DAT_00401bc0);
那么下一个问题是,要跳转到哪里?因为没有PIE可以把代码段里所有函数都试一试,看文本会发现0x4014b4会弹一个提示“闯过了第一关”,所以这应该是官方流程,我也是这么做的。
第二部分主要部分从0x4011ed开始,首先要循环输入字符串,进一个0x400a66的函数直到返回值不为0。0x400a66里面有大量的判断条件和分支。跳出循环后的0x401182函数要求num7==7,这是最终的目标。需要的条件比较零碎,但因为输入总长度限制很宽,很容易凑出合适的结果。我们要首先进APPLES6分支让num7=7,然后进CTF_IN分支让num5不为0以跳出循环。
第三部分从num7==7后的0x4010a7开始。堆题经典的资料管理系统,可以增删查,最多可以malloc16次,每次最大可以申请0x208字节。容易发现删和查只检查对应的条目序号是否被增过(是否为空),但删除时只是free了而没有把指针置空,因此可以double free和UAF。查的部分是个手动实现的puts,用strlen检查长度,逐字节write出来再补一个\n,因此可以用来leak
我的利用思路是:
- 首先用unsorted bin leak:申请一个不和top chunk相邻又不在fastbin范围的chunk,释放掉后会进入unsorted bin,再申请回来,把fd字段填充掉后可以读到后面的bk,其指向main_arena.top,从而获取glibc基址。
- 然后用fastbin attack:先两个构造0x70大小的trunk进行double free,然后利用__malloc_hook前面的一个\x7f用错位构造法伪造合法trunk,以通过fastbin检查,把fastbin末端指向__malloc_hook,写入one_gadget,最后再malloc一次完成getshell。one_gadget工具在libc中能找到4个onegadget,只有0xf1247的那个是能用的(对应[rsp+0x70]==0),都试一遍就行了