安全矩阵

 找回密码
 立即注册
搜索
查看: 4627|回复: 0

浅谈pwn中的栈溢出攻击

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2020-3-25 13:07:06 | 显示全部楼层 |阅读模式
本帖最后由 gclome 于 2020-3-25 13:08 编辑

本文转自:中南极光网安实验室


0.前言

pwn这个东西真是深奥,目前自己对于pwn的理解也刚刚处于微妙的入门阶段。想要系统全面地了解这方面,还是离不开CTF-wiki(https://ctf-wiki.github.io/ctf-wiki/pwn/readme-zh/)与针对细节问题的百度。

   说到pwn题,初次接触的萌新应该都会从栈溢出这个方向开始。如果还不了解什么是pwn,那可以查看一下我们的往期推送。然而这个入门真的只是推荐了条路线吗……作者作为一个懒人型萌新,还是希望有更有操作感的入门。

1.你需要做的前期准备

   首先,你需要有一个Linux系统的电脑或者一个linux系统的虚拟机。至于Linux镜像的选择的话,kali可能有更多的安全方向的预装软件,Ubuntu则更加主流,如果你遇到了操作系统层面的问题,Ubuntu系统可能更容易搜出解决方案。不过系统级别的问题还是重装系统更快,反正是虚拟机。
    之后你需要安装pwntools pwndbg。这里给出指令:
  1. sudo apt install python
  2. sudo apt install python-pip
  3. sudo apt install git
  4. pip install ROPgadget
  5. pip install pwntools
  6. git clone https://github.com/pwndbg/pwndbg
  7. cd pwndbg/
  8. ./setup.sh
复制代码
  
安装完成后建议重启一下虚拟机。

2.栈溢出攻击是什么?

如果学过数据结构或者跟汇编打过交道,想必对栈都并不陌生。程序有程序段,数据有数据段,缓冲区也有缓冲区的栈段。众所周知,栈的大小是有限的,但是有时候数据的输出可能是无限的。当读入数据时没有限制,或者限制大小比栈段大小要大时,都会导致缓冲区的溢出。

我们能利用缓冲区溢出做什么呢?先来解释一下栈帧的结构。







这张图建议从下往上看,你写入的数据会从rsp指向的地方往上依次排列,学过汇编的小伙伴也知道在执行函数返回的ret 指令时会从栈帧中弹出父函数的地址,并且程序跳转到该地址。而我们就可以利用这一步弹出,通过缓冲区溢出来实现将程序转移到一个我们希望它去的地址。

而做pwn题攻击的终极目标是什么呢?是取得系统的权限,即让攻击者能够执行一些指令。那我们需要控制程序跳转到哪里才能达成目的呢?针对初级的题目而言,主要是以下四种形式:



ret2text:控制程序执行程序本身的代码,比如程序中已经有了system函数,我们就可以通过执行system("/bin/sh") 或system(“sh”)实现。

ret2shellcode:先写入一段能够获取shell的汇编代码,然后让程序跳到rsp指向shellcode的开头,来执行该段汇编代码。但实现该攻击需要shellcode所在的区域有课执行权限。

ret2syscall:调用Linux的系统中断int 0x80实现。如果不知道linux有哪些系统中断可以查阅这个网址。

ret2libc:控制函数执行libc中的函数,返回一个函数的具体位置,诸如靠这样获得system函数的地址。
这几个方法需要的前置条件各有不同,不过使用起来的复杂程度大致还是递增的,有兴趣深入了解的可以参考CTF-Wiki,并且多百度一下。


3.原理知道了,提供的工具如何使用?

这里以ctf-wiki上给出的ret2text例子来深入讲解一下虽然别人CTF-wiki已经介绍了攻击步骤了,这里还是稍微说一下思路。

拿到程序的第一步,一定是确定一个做题的方向。首先使用checksec看一下最基本的文件信息。



我们对checksec显示的内容做一个大致的解释:

Arch 程序使用的架构
canary   栈保护,原理是用一个标记位放在栈中,用来防止缓冲区溢出攻击
NX(DEP)   设置栈段不可执行,enable时开启保护,不能执行代码
PIE(ASLR)  内存地址随机化。三种情况:
0 - 表示关闭进程地址空间随机化。

1 - 表示将mmap的基址,stack和vdso页面随机化。

2 - 表示在1的基础上增加栈(heap)的随机化。
RELRO    partial RELRO说明对got表有读写权限,FULL RELRO 则无法修改got表

所以该程序只启用了栈段不可执行的保护,我们不能在栈段放入sehllcode进行运行,而其他的攻击手段都可以使用。知道了大概的方向之后就可以用IDA查看源码了。

另外如果想使用IDA远程linux调试32位程序时会需要libstdc++.so.6的库文件。ubuntu环境下安装该库:apt-get install lib32stdc++6。如何使用远程调试可参考博客(https://blog.csdn.net/lacoucou/article/details/71079552)。



可以看出来这个程序不仅有system函数,并且输入system的参数都替我们准备好了,否则通常情况下是需要自己写入或寻找其他地方的字符的。在动态调试时可以看到这个的地址:



然后我们观察一下利用栈溢出漏洞的输入函数:



左侧eax寄存器的值是给gets作为输入数据的起点,而ebp代表的是当前的栈底。最后程序进行的步骤是leave,ret所以我们构造的填充用的输入应该有(eax与ebp间的插值)+leave时赋给ebp的字节数,即0x80+4个字节。

在平常构造跳转地址的时候还要注意,若是32位程序,是将参数放在栈中的,可以直接利用栈溢出进行修改;但是64位程序的函数前6个参数,则是分别存储在RDI, RSI, RDX, RCX, R8, R9这六个寄存器当中的。那么如何改变寄存器的值呢?我们可以使用ROPgadget工具来实现。



当然至于ROPgadget更进一步的使用就需要自己去百度了。

4. 接下来就是最后一步了!

当你成功找到了栈溢出攻击的方法,最后一步就是构造脚本了。我们使用pwntools这个现成的包来构造攻击脚本。pwntools包的使用分为以下几步:
1. 构造context。设定程序执行的环境与程序体系结构。常见的体系结构即i386 与amd64
2.建立连接。与本地文件进行交互时使用process("filename"),与远程交互时使用remote(ip="",port="")
3. 构造一个输入。这个就是具体题目具体分析了。基本套路就是前面的缓冲区与寄存器部分可以随意填充,而只需要保证跳转地址是你需要的跳转地址即可。注意跳转地址应使用p64()或p32()包裹。
4. 输入进去并进行交互。注意交互时如果没有正确实现提权是不会有结果的,可以用ls指令或echo "something"指令试一下。




运行结果:



这样就是做一个Pwn题的大概流程了,各位有没有觉得有趣呢?有趣的话可以看看入门路线进行更系统全面的学习。



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-29 15:41 , Processed in 0.016046 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表