[Pwnable.kr] fsb

http://pwnable.kr/play.php

Isn’t FSB almost obsolete in computer security?
Anyway, have fun with it 🙂

ssh fsb@pwnable.kr -p2222 (pw:guest)

这道题叫fsb,主要是考察格式化字符串的。重点是要搞清楚格式化字符串如何泄露内存以及如何修改内存。

当然还要记得这道题一个最重要的trick:利用函数调用之间ebp的关系。


格式化字符串的利用,参考 https://www.anquanke.com/post/id/85785

格式化字符串漏洞在通用漏洞类型库CWE中的编号是134,其解释为“软件使用了格式化字符串作为参数,且该格式化字符串来自外部输入”。会触发该漏洞的函数很有限,主要就是printf、sprintf、fprintf等print家族函数。介绍格式化字符串原理的文章有很多,我这里就以printf函数为例,简单回顾其中的要点。

printf()函数的一般形式为printf(“format”, 输出表列),其第一个参数就是格式化字符串,用来告诉程序以什么格式进行输出。正常情况下,我们是这样使用的:

但也会有人这么用:

也许代码编写者的本意只是单纯打印一段字符(如”hello world”),但如果这段字符串来源于外部用户可控的输入,则该用户完全可以在字符串中嵌入格式化字符(如%s)。那么,由于printf允许参数个数不固定,故printf会自动将这段字符当作format参数,而用其后内存中的数据匹配format参数。

以上图为例,假设调用printf(str)时的栈是这样的。

1)如str就是“hello world”,则直接输出“hello world”;

2)如str是format,比如是%2$x,则输出偏移2处的16进制数据0xdeadbeef。

通过组合变换格式化字符串参数,我们可以读取任意偏移处的数据或向任意偏移处写数据,从而达到利用格式化字符串漏洞的作用。

基本的格式化字符串参数

%c:输出字符,配上%n可用于向指定地址写数据。

%d:输出十进制整数,配上%n可用于向指定地址写数据。

%x:输出16进制数据,如%ix表示要泄漏偏移i处4字节长的16进制数据,%ilx表示要泄漏偏移i处8字节长的16进制数据,32bit和64bit环境下一样。

%p:输出16进制数据,与%x基本一样,只是附加了前缀0x,在32bit下输出4字节,在64bit下输出8字节,可通过输出字节的长度来判断目标环境是32bit还是64bit。

%s:输出的内容是字符串,即将偏移处指针指向的字符串输出,如%i$s表示输出偏移i处地址所指向的字符串,在32bit和64bit环境下一样,可用于读取GOT表等信息。

%n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置,如%100×10n表示将0x64写入偏移10处保存的指针所指向的地址(4字节),而%hn表示写入的地址空间为2字节,%hhn表示写入的地址空间为1字节,%lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。有时,直接写4字节会导致程序崩溃或等候时间过长,可以通过%hn或%hhn来适时调整。

%n是通过格式化字符串漏洞改变程序流程的关键方式,而其他格式化字符串参数可用于读取信息或配合%n写数据。


Trick: ebp

函数在被调用的时候先将原函数的ebp保存到栈中,在跳转至被调用函数的内部;返回时先将当前ebp恢复为原函数的ebp,再跳转回到原函数中。也就是说,栈中保存的ebp永远指向原函数ebp,它是一个链式的结构。


看看源程序

我们需要修改某个将要调用的函数GOT表内容为执行execve(args[0], args, 0);的代码地址。观察一下,我们可以修改的有:printf(), sleep(), read()三个函数的GOT表内容。任君选择。需要注意的是:如果修改的printf()函数,那么覆盖的代码地址千万不能包含printf(“Congratz!\n”);的那一段。因为这样会造成死循环,拿不到shell。

利用脚本如下:

 

 

Leave a Comment