written by ling
一、找溢出点
分析程序,程序对手机或者表都定义了一个如下的结构体,其中前4个字节为一个虚表指针。一共可以添加16次这样的结构体,都布局在堆中。
Struct item
{
+0dword vtable;
+4 charname[32];
+36char description[80];
+116dword price;
+120dword type;
}
同时,在生成menu时,程序会申请固定大小0xb18的空间来存放menu信息。经过测试,发现在最大可能填充item信息的情况下,menu会产生溢出,溢出刚好能覆盖item的前6个字节,也就是可以改写掉item的虚表。
二、失败的利用
程序的虚表中有2个函数指针,不过在调用这2个函数指针之前,都对虚表进行了如下判断。往虚表地址写数据失败,即虚表地址是不可写的。
既然虚表地址不可写,那就只能在只读代码段中找函数指针。
第一处函数指针是一个switch-case表,经分析发现这块进去之后能改写ebp-8处的值,而在调用虚表的函数中ebp-8处的值并没有什么意义。所以这儿应该是不行的。
剩下的函数指针就是原来的虚表了,不过因为虚表中是有2个函数指针的。可以通过虚表指针,让程序在调用本应该第一个函数指针时调用第二个函数,或者调用第二个函数的地方调用第一个函数。
分析下虚表地址使用的地方。
第一处函数指针:其中第二个参数是要买的个数,是用户可控的。对应的函数是计算打折的。
第二处函数指针,这是在生成menu时,生成单个item的信息时调用的,看了下参数基本不太可控。
对应的函数是往参数2所在地址生成一串字符串,其中字符串的后部分(description)是用户可控的。
所以当时想到了通过修改虚表在调用第一个虚表指针的地方调用sub_80492fe,可以往指定内存写入一段数据。
不过数据段中got表位于data段的最开始位置,而这样利用中能控制的数据在字符串的后面。所以没法直接往got表中写入固定的值。
同时,因为aslr的关系,所以根本不知道system的加载地址。而这样利用很难做到同时泄露库地址+改写got表。
所以到比赛最后也没想出利用方法。
三、赛后学习
比赛后,看了国外某队公布的利用脚本,发现它改写的是通过上面的sprintf改写了一个数据指针。
而这个数据指针中存放的是当前用户拥有多少钱。
在进行购买时,进行了如下操作:其中*(a1+116)是当前物品的价格,v1是购买的数量,这2个数据都是攻击者可控的。
因此如果将got表的地址(如sprintf)指向该数据指针。如果*(a1+116) * v1 与 offset(system-sprintf)的值相等,那么通过下列函数直接就可以将sprintf在got表中的地址改写成system的地址,之后就可以拿下shell了。
Exp下载地址:https://gist.github.com/Idolf/99ffa3c5f76165f77b19