manpage4

Posted by rk700 on July 21, 2014

这道题从第一次看到,到现在已经好久了……

首先,这个游戏是叫wumpus,运行时加上参数-s <NUM>可以以我们提供的数做seed生成随机数,这样就保证每次都是一样的。取seed为100,输入SHOOT,1,2即可获胜。

问题还是出在sprintf,可以修改返回地址。关键是如何使字符串足够长。我曾经考虑过ctime的问题,因为其返回值是静态分配的一段空间,可能可以修改,但最后发现这条路还是不行。所以只能考虑name的长度了

在sprintf之前,两个name是通过sscanf获得的,而且限制了最大长度是100,但没有检查两个name是否都读入成功了。如果两个name都成功读入,那么就不够长了,所以最后的思路是让lastname不成功。而lastname是直接在栈上的一段空间,所以如果我们可以在此前写一些内容到这一段栈,那么就可以赋我们想要的内容给lastname而不受长度限制了

看代码,发现getnum和getlet都用了fgets,而buf都是在栈上的,且足够长;其实我找到的原始的wumpus.c里,那buf是static的;所以修改版本就是要让我们可以利用getnum里的fgets,来保存一部分内容到栈上。

最后一个输入是2,所以紧接着log_winner之前调用的函数是shoot,在shoot里调用了getnum。比较可以知道,getnum里的buf与log_winner里的lastname相距0x808-0x308+4+4+40×24=1328,除去第一个是2,我们还要再填充1327个字符。

接下来是lastname,ctime返回字符串长度25,如果firstname的长度是100,那么还需要0x108+4-25-1-10-100-1-10=121个字符在返回地址之前

综上,我们用

$ (perl -e 'print "N\nS\n1\n2" . "A"x1327 . "C"x121 . "\xe5\xd8\xff\xff" . "\n" . " "x155 . "B"x100 . "\n"';cat) | /manpage/manpage4 -s 100

注意这里还是加上cat。最后得到密码是vahshaihug

最后,在gdb调试的时候,需要stdin输入很多字符;这时我们可以先将输入存到文件input里,然后在gdb里,利用重定向
r -s 100 < input