一个非常经典的文本管理,可以分配和管理空间,并向空间中写入文本。另外,附带一个猜随机数的小游戏,猜对了可以获取随机种子(的地址)。// 此程序中这两个是相等的
在删除已有的box时free掉了box指向的堆地址,但未将指针本身清零,故存在Double Free和Use After Free漏洞。
程序开启了全局PIE,地址完全随机,故必须先取得程序基地址。分析程序,发现之前的猜随机数小游戏猜对了可以输出随机种子的地址,而本题中的随机种子为一个data段的全局变量,相对程序基址的偏移已知,故可以通过获取这个地址来获取程序基址。关于Glibc的rand函数,查阅以下文章:272K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3#2K6j5%4y4Q4x3X3g2V1j5h3I4Q4x3X3g2U0j5g2)9J5c8W2)9%4c8i4y4W2L8r3W2F1k6$3g2J5i4K6u0r3M7X3q4F1k6r3!0E0i4K6u0r3rand函数的一个简单复现:
由此可知,在一个确定的随机种子下的序列中,下一个随机数的生成公式为:
而在此程序中,若猜错随机数,则会输出正确的随机数,故可以通过故意猜错来获取足够长的随机数序列,从而预测下一个随机数的值。猜到了正确的随机数后,获取到了seed变量的真实地址,减去偏移量0x202148,即得到了程序的基地址。
关于unlink的原理可以参见以下文章52fK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3u0D9L8$3N6Q4x3X3g2U0M7$3c8F1i4K6u0W2L8X3g2@1i4K6u0r3M7i4q4Q4y4h3j5J5z5e0x3@1x3K6t1H3x3g2)9J5c8X3q4J5N6r3W2U0L8r3g2Q4x3V1k6V1k6i4c8S2K9h3I4K6i4K6u0r3y4e0x3#2y4e0R3J5x3e0j5`.这个程序对堆块的大小有一个限制,从上到下的几个box,后一个比前一个必须大16以上,且大小控制在8 - 0x1000范围内。另一个限制是,能够free的指针只有第二个和第三个box。为了使堆块连续分配,需要控制堆块的大小在同一个bin范围内,这里选择small bin范围。先分配第二个和第三个box两个堆块,这里选择的大小为128和144,然后再free第二块和第三块(此时指针仍然存在)。此时堆中的布局如下:
再分配第四个box,大小为(128 + 144 + 16),为了将box2和box3的空间恰好完全覆盖为了触发unlink宏,需要通过检验:
故选取data段box2指针向前偏移0x18作为伪fd,向前0x10作为伪bk(对应的为指针list - 0x8, 指针list - 0x0)通过box4的指针去修改堆里的内容,现在堆里的布局如下:
free box3指针,成功,触发unlink,现在box2指针的值已经被修改为了指向指针列表 - 0x8的位置。
现在指针列表已经可控了,可以将没有用到的box1的指针修改为指向GOT表上地址的指针。由于程序限制了必须先分配才可查看的设定,在修改之前先分配一个小一点的box1,这里大小为112.通过box2指针来编辑指针列表,这里将box1的指针修改为GOT表上“free”函数的地址。(也可以是其他函数,但一定是要之前执行过的函数,由于延迟绑定,执行过的GOT表中才会存放真实地址)使用程序自带的show message功能查看box1中存放的内容,由于box1已被修改,故实际查看的是GOT表中free函数的地址。获取地址之后根据题目所给的libc文件,可以算出libc的基址。
[培训]科锐逆向工程师培训第53期2025年7月8日开班!