1.7.2 美妙定位溢出点

“第一个条件是最主要的:‘有问题程序’返回点的精确位置。从漏洞公告和漏洞分析中我们可以知道,邮件的‘From:’字段太长就会覆盖到返回地址,那我们就写一个初步的溢出程序框架FoxMail1.c,来逐步定位返回点的位置。这个程序很简单,就是往邮箱发一封信,而且只有‘From:’字段。不要小看这个框架哦,虽然简陋,但我们会在此基础上打造出最终的梦幻版本。”

[FoxMail1.c请参见光盘 ]

“在程序的FoxMail1.c中,我们对‘From:’字段进行填充。因为不能超过0x200的长度,所以我们先填充0x150个A试试。”

memset (buffer, 0x41, 0x150);
sprintf (temp, "From: %s\r\n", buffer);
send (sock, temp, strlen (temp), 0);

“然后执行程序,发送成功!”如图1-20。

“然后我们用FoxMail接收邮件,大家看图1-21。”

“噢!接收的时候出错了!‘41414141’就是我们添加的A啊!”

“找到喽!”教室里一片欢腾。

“等一下!”老师把手一挥,“我把它们列出来,让大家仔细看看,和上节课的报错信息对比一下。”。

上节课的报错信息如图1-22。

FoxMail的报错信息如图1-23。

“发现有什么不同了吗?”老师问道。

“一个有两个按钮,一个只有一个按钮……”

“倒~~~”老师当即晕倒。

费了好大劲站起来后,老师说:“对,这的确是个不同的地方,这是由于程序错误的处理机制不同而造成的。但这不是重要的地方,大家再仔细看看里面提示的信息有什么不同。”

小知识:不同出错处理的外在表现

1.弹出“只有一个确定按钮的红叉框”,意味着外层有“try/except”块决定处理异常,而内层有“try/finally”块。当按下确定后,是在“try/finally”中执行。

2.弹出“有调试、关闭按钮的非红叉框”,意味着设置了“Just-In-Time Debugging”,并获得机会执行,这已经是最后机会了。

3.弹出“只有一个关闭按钮的非红叉框”,意味着没有设置“Just-In-Time Debugging”,内层也没有决定处理异常的“try/except”块。

4.触发异常,但什么框也未弹出,意味着内层有“try/except”块决定处理异常。或者在异常处理过程中再次触发异常。

宇强仔细看后,说道:“那…是不是前一个提示的是内存不能read(读)而出错;而后一个提示的是内存不能write(写)而出错?”

“对!就是这里啦!”老师高兴的说。“是这样的,我们覆盖了0x150个A,可能不仅覆盖过了EIP的地方,而且还覆盖了其他一些程序要用的参量,如果在程序返回前,要对那些参量改写,但参量的地址被改成‘41414141’,是根本不能写的,所以就造成了写(write)类型错误!”

“哦,原来是这样,那怎么办呢?” 宇强发现那位PLMM在自己发言时朝这边看了一眼,心中紧了一下,多么美丽的眼眸啊!

老师可不会注意这些,回答道:“我们把‘From:’字段覆盖短一点,要覆盖到返回地址,但不要覆盖到那些参量地址。这里我们采用二分法:即先前0x150太长,就改成0x75,如果0x75太短,不能覆盖返回地址没有报错,那又改长一点,改成0x115的长度,以此类推。”

老师接着说:“当我们覆盖到0x104时,我们想要的结果出现了!如图1-24。”

memset (buffer, 0x41, 0x104);
sprintf (temp, "From: %s\r\n", buffer);
send (sock, temp, strlen (temp), 0);

“哦!和原来那个是一样的错误,都是Read错误!”同学们叫了起来。

老师笑道:“哈哈,对!说明我们填充‘From:’字段时不能超过0x104的长度。解决了这个问题,我们继续,想办法定位返回点的位置。”

“大家想想,上节课的返回点我们是怎么确定的呢?”老师提示大家。

“嗯……是根据报错信息直接数出来的。”古风说道。

“对,这里我们也仿照那样,但这里的缓冲区太长了,我们把数的方法改进一下。”老师大笔一挥。

“我们改变FoxMail1.c,把‘From:’字段的填充方法改一下。改变的程序为FoxMail2.c。”

“我们把FoxMail1.c填充‘From:’字段的那段替换为如下。”

for(i=0; i<=0x104; i++)
    buffer[i] = 'A' + i % 10;
sprintf (temp, "From: %s\r\n", buffer);
send (sock, temp, strlen (temp), 0);

“我们进入邮箱,把原来的信删除;再执行FoxMail2.c,给邮箱发封新信。这次用FoxMail接收时出现的报错框成了‘Access violation at address 4A494847,Read of address 4A494847’, 如图1-25所示。”

“OK,我们记录下这个数字,看来这次是0x4A494847覆盖了返回点。再在FoxMail13.c中把 buffer[i] = 'A' + i % 10 的取余数改为整除。”

for(i=0; i<=0x104; i++)
    buffer[i] = 'A' + i / 10;
sprintf (temp, "From: %s\r\n", buffer);

“再次删除信件,执行FoxMail3.c。用FoxMail接收,这次出现的错误框成了‘Access violation at address 5A5A5A5A,Read of address 5A5A5A5A’,如图1-26。”

“从上面的两个提示中,我们就可得到精确的返回地址位置了!”老师得意的说。

大家都丈二和尚摸不着头脑:“怎么得到呢?”

“不要急,我们一起来推算一下。分析一下上面两次我们做的事情。”

“第一次用FoxMail2.c,是在‘From:’字段不停的加上A~J的循环(就是十六进制0x41~0x4A这十个数的循环)。”

“第二次用FoxMail3.c,是以10为一段长度,每段分别为0x41、0x42……来填充‘From:’。”

“注意了,第一次溢出时报错的最小值是0x47,此时只有0x41~0x4A在不断循环,所以我们可大胆推出尾数是0x47-0x41=6。”

“在第二次溢出时报错的全部是0x5A,而此时是从0x41开始,每10个数为一段。所以0x5A-0x41=0x19,就是十进制的25,即在字符串的第25个段。”

“所以我们可大胆计算出程序的返回点位置是: (0x5A-0x41)×10+(0x47-0x41)=25×10+6=256 ”

“哇!这样啊!”大家一片欢呼!

“哈哈,我们验证一下猜测结果吧!再改一下程序,指定‘From:’字段第256开始的四个字节是‘BBBB’,而其他全部为‘A’。”

memset(buffer, 'A', 0x104);
buffer[256] = 'B';
buffer[257] = 'B';
buffer[258] = 'B';
buffer[259] = 'B';

“代码如上修改后,如果猜测正确,大家想想,会是什么样呢?”老师问道。

“嗯,应该是‘BBBB’覆盖到了返回地址吧!”

“我们一起试试吧!执行这个程序(FoxMail4.c),果然弹出的对话框成了‘Access violation at address 42424242,Read of address 42424242’,如图1-27。”

“42就是‘B’的ASCII码表示!”

“哇!So Cool!”堂下响起了一片掌声!

“谢谢,谢谢大家的鼓励,我能取得现在这个成绩,是离不开大家的支持,谢谢你们,我爱你们!”

台下无语※※……¥

“呵呵,上面溢出点定位的方法非常巧妙和准确,以后大家在标准的堆栈溢出中,可经常使用这种方法来进行定位。”老师强调到。

“太好了,真是个好方法啊!这样别人给出漏洞证实程序,我们可以很快定位了!”教室里顿时议论纷纷。

“嗯,OK,回到我们这个程序的利用上来吧!”