pwnable.kr fsb题解

漏洞分析YM老师布置的大作业,老师钦点我们组做unexploitable,难度吓人,看了一堆wp,毫无思路,遂自闭,于是先看一个稍微简单一点的格式化字符串漏洞题,收获不小,故写一篇博客记录下来,也希望能帮助到别人。

代码分析

#include <stdio.h>
#include <alloca.h>
#include <fcntl.h>

unsigned long long key;
char buf[100];
char buf2[100];

int fsb(char** argv, char** envp){
        char* args[]={"/bin/sh", 0};
        int i;

        char*** pargv = &argv;
        char*** penvp = &envp;
        char** arg;
        char* c;
        for(arg=argv;*arg;arg++) for(c=*arg; *c;c++) *c='\0';
        for(arg=envp;*arg;arg++) for(c=*arg; *c;c++) *c='\0';
        *pargv=0;
        *penvp=0;

        for(i=0; i<4; i++){
                printf("Give me some format strings(%d)\n", i+1);
                read(0, buf, 100);
                printf(buf);
        }

        printf("Wait a sec...\n");
        sleep(3);

        printf("key : \n");
        read(0, buf2, 100);
        unsigned long long pw = strtoull(buf2, 0, 10);
        if(pw == key){
                printf("Congratz!\n");
                execve(args[0], args, 0);
                return 0;
        }

        printf("Incorrect key \n");
        return 0;
}

int main(int argc, char* argv[], char** envp){

        int fd = open("/dev/urandom", O_RDONLY);
        if( fd==-1 || read(fd, &key, 8) != 8 ){
                printf("Error, tell admin\n");
                return 0;
        }
        close(fd);

        alloca(0x12345 & key);

        fsb(argv, envp); // exploit this format string bug!
        return 0;
}

先看main函数,首先打开了一个文件叫/dev/urandom,查资料得知这是Linux系统中提供的随机伪设备,提供永不为空的随机字节数据流,在本地Linux上使用cat /dev/urandom试了一下,屏幕上一直在滚动刷新随机字符。
在这里插入图片描述

继续分析,read()函数从这个随机流中读取了8个字节送给全局变量key,key是unsigned long long类型,也正好是8个字节,alloca根据key的值分配了一些空间,然后就调用了漏洞函数fsb,并把main函数的参数以及环境变量都传给了fsb。
在fsb函数中先进行了一通操作,功能像是清空了main函数的参数以及环境变量,可能是防止有其他非预期解?
紧接着使用for循环从键盘读入四个字符串并把他们以格式化字符串的形式打印出来,这就是格式化字符串漏洞点。其中read(0, buf, 100)中的0代表Linux系统下的标准输入,即从键盘获得输入。

标准输入0 从键盘获得输入 /proc/self/fd/0
标准输出1 输出到屏幕(即控制台) /proc/self/fd/1
错误输出2 输出到屏幕(即控制台) /proc/self/fd/2

然后sleep3秒,提示用户输入key的值,比较和之前随机生成的是否相等,如果相等,则给用户一个可以读取flag值的shell,否则退出。

思路

格式化字符串漏洞的效果就是可以读取/修改内存任意位置的值,这道题我们可以泄露key的值,也可以修改key为我们想要的值,当然也可以暴力一点直接修改GOT表,将某个函数的地址指向shellcode

难点分析

和我们之前上课接触的格式化字符串漏洞不同,这道题存放用户输入的格式化字符串是一个全局变量buf,它没有定义在漏洞函数fsb里,在内存中距离printf函数的位置非常远,从而导致我们很难去移动va_list指针到我们自己构造的字符串上去,就算我们真的找到了应该移动多少次va_list指针,下一次程序运行时距离也和前一次是不一样的(我猜是main函数中的alloca的原因?)

解法

不管三七二十一,先GDB调试一波,在printf(buf)这个地方下个断点,看看有没有可以利用到的地方。
在这里插入图片描述
随便输入一个字符串,然后断住,查看一下堆栈信息
在这里插入图片描述
可以看到第一个0x0804a100就是printf函数的第一个参数,也就是va_list指针一开始指向的位置,我们再继续往下看,可以发现第14和15个位置有两个很特殊的值0xffe8de10和0xffe8de14,而这个值就正好指向后面的两个内存位置,如图所示
在这里插入图片描述
所以思路就来了,我们可以连续用两次格式化字符串漏洞(而我们总共有四次机会)

  1. 第一次将va_list指针移到0xffe8de10这个值上,使用%n向其中写入key的低地址,然后将va_list指针移到0xffe8de14值上,使用%n向其中写入key的高地址,为什么要分高地址和低地址,因为key是unsigned类型,占用了8个内存单元。
  2. 第二次将va_list指针移到我们刚刚写入的两个值上,然后使用两次%n将0写入这两个地址中就可以了,这样一来我们就将key的值修改成了0。

下面开始攻击
首先就在刚刚下断点的地方把key的地址打印出来
在这里插入图片描述
然后计算一下0x804a060的十进制
在这里插入图片描述
现在就可以开始构造第一个格式化字符串

%134520928x%14$nAAAA%15$n

输出0x0804a060个字符,然后将这个数值写入0xffe8de10,在随便打印4个字符,这时总共输出了0x0804a064个字符,将这个数值写入0xffe8de14

然后构造第二个格式化字符串

%20$n%21$n

这就是将va_list指针移到我们刚刚写的0x0804a060和0x0804a064这两个值上去,然后写入0(因为我们这里打印了0个字符,注意只是第二次调用printf,前面打印的字符已经不算在内了)

构造好字符串我们就可以开始攻击了,启动程序,因为第一个printf会输出巨多巨多的字符,程序会卡死,所以我们需要将程序的输出重定向到/dev/null这个“黑洞”里去
在这里插入图片描述
在这里插入图片描述
此时我们已经获得了shell,但是由于这个程序的输出被重定向到/dev/null,我们没办法看到运行结果,所以我们需要将flag的值输出重定向到标准错误,也就是上面提到的"2"
在这里插入图片描述
可以看到flag的值被当成错误信息输出了,攻击成功!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值