栈溢出与二进制攻防实战指南
Poseidon 发表于 湖北 二进制安全 1368浏览 · 2025-05-27 08:17

获取地址

在漏洞利用的过程中,我们常常需要获取一些变量,函数的地址,以便于能够进行进一步的利用。这里我将获取地址的方法分为如下几类

直接寻找地址,即我们可以通过反编译等手段直接看到对应符号的地址。

泄漏地址,即需要我们通过控制程序的执行流来泄漏程序中的某些符号指针的内容,来获取对应的地址。

推测地址,这里我们一般常用的就是根据某个段内的符号之间的偏移是固定的,从而来推断一些新的符号的地址。

猜测地址,一般主要指的是,我们需要自己去猜测对应符号的地址,这里伴随的往往就是暴力枚举了。

上述几种方法,是一种递进地考虑方式,我们在获取相关符号的地址时,应保持这样的思考方式。

在上面的几种方式中,我认为主要有两点核心思想

充分利用代码本身的性质,比如程序某些代码的位置就是固定的,如不开启 PIE 时,代码段的位置;再比如,glibc 的后三位是固定的。

充分利用相对偏移的性质,这是由于目前程序加载时往往加载的内存都是一段一段的,所以相对偏移往往是固定的。

更加具体的,我们可以看如下的介绍。

直接寻找地址

程序中已经给出了相关变量或者函数的地址了。这时候,我们就可以直接进行利用了。

这种情形往往适用于程序没有开启 PIE 的情况。

泄漏地址

在泄漏地址的过程中,我们往往需要找到一些敏感的指针,这些指针里存储着要么就是我们想要的符号的地址,要么就是与我们想要的符号的地址相关。

下面给出几个例子。

泄漏变量指针

比如

1泄漏 main arena 中各种 bin 的头表指针,可能就可以获取堆中或者 glibc 中某个变量的地址。

泄漏 got 表

有时候我们并不一定非得直接知道某个函数的地址,可以利用 GOT 表跳转到对应函数的地址。当然,如果我们非得知道这个函数的地址,我们可以利用 write,puts 等输出函数将 GOT 表中地址处对应的内容输出出来(前提是这个函数已经被解析一次了)。

ret2dl-resolve

当 ELF 文件采用动态链接时,got 表会采用延迟绑定技术。当第一次调用某个 libc 函数时,程序会调用_dl_runtime_resolve 函数对其地址解析。因此,我们可以利用栈溢出构造 ROP 链,伪造对其他函数(如:system)的解析。这也是我们在高级 rop 中介绍的技巧。

/proc/self/maps

我们可以考虑通过读取程序的 /proc/self/maps来获取与程序相关的基地址。

推测地址

在大多数情况下,我们都不能直接获取想要的函数的地址,往往需要进行一些地址的推测,正如上面所说,这里就重点依赖于符号间的偏移是固定的这一思想。

Stack Related

关于栈上的地址,其实我们大多时候并不需要具体的栈地址,但是我们可以根据栈的寻址方式,推测出栈上某个变量相对于 EBP 的位置。

Glibc Related

这里主要考虑的是如何找到 Glibc 中相关的函数。

有 libc

这时候我们就需要考虑利用 libc 中函数的基地址一样这个特性来寻找了。比如我们可以通过 __libc_start_main 的地址来泄漏 libc 在内存中的基地址。

注意:不要选择有 wapper 的函数,这样会使得函数的基地址计算不正确。

常见的有 wapper 的函数有?(待补充)。

无 libc

其实,这种情况的解决策略分为两种

想办法获取 libc

想办法直接获取对应的地址。

而对于想要泄露的地址,我们只是单纯地需要其对应的内容,所以 puts , write,printf 均可以。

puts,printf 会有 \x00 截断的问题

write 可以指定长度输出的内容。

下面是一些相应的方法

PWNLIB.DYNELF

前提是我们可以泄露任意地址的内容。

如果要使用 write 函数泄露的话,一次最好多输出一些地址的内容,因为我们一般是只是不断地向高地址读内容,很有可能导致高地址的环境变量被覆盖,就会导致 shell 不能启动。

LIBC 数据库

去 libc 的数据库中找到对应的和已经出现的地址一样的 libc,这时候很有可能是一样的。

也可以使用如下的在线网站:

libcdb.com

libc.blukat.me

**当然,还有上面提到的 **https://github.com/lieanu/LibcSearcher。

Heap related

关于堆的一些地址的推测,这就需要我们比较详细地知道堆里分配了多少内存,目前泄漏出的内存地址是哪一块,进而获取堆的基地址,以及堆中相关的内存地址。

猜测地址

在一些比较奇怪的情况下,我们可能可以使用如下的方式

使用一些暴力的方法来获取地址,比如 32 位时,地址随机化的空间比较小。

当程序被特殊部署时,其不同的库被加载的位置可能会比较特殊。我们可以在本地尝试,然后猜测远程的情况。

常见的保护程序方法

本节介绍 Linux 下常见的几种二进制程序保护机制,它们是为了防止诸如缓冲区溢出、代码注入、返回地址篡改等常见漏洞被利用。这些机制包括栈保护(Canary)、编译器级别的函数安全检查(FORTIFY)、不可执行堆栈(NX/DEP)、以及地址空间布局随机化(ASLR)与位置无关可执行文件(PIE)。

CANNARY(栈保护)

FORTIFY

FORTIFY_SOURCE设为1,并且将编译器设置为优化1(gcc -O1),以及出现上述情形,那么程序编译时就会进行检查但又不会改变程序功能FORTIFY_SOURCE设为2,有些检查功能会加入,但是这可能导致程序崩溃。

gcc -D_FORTIFY_SOURCE=1 仅仅只会在编译时进行检查 (特别像某些头文件 #include <string.h>)gcc -D_FORTIFY_SOURCE=2` 程序执行时也会有检查 (如果检查到缓冲区溢出,就终止程序)

NX(DEP)

PIE(ASLR)

一般情况下NX(Windows平台上称其为DEP)和地址空间分布随机化(ASLR)会同时工作。内存地址随机化机制(address space layout randomization),有以下三种情况

可以防范基于Ret2libc方式的针对DEP的攻击。ASLR和DEP配合使用,能有效阻止攻击者在堆栈上运行恶意代码。

Built as PIE:位置独立的可执行区域(position-independent executables)。这样使得在利用缓冲溢出和移动操作系统中存在的其他内存崩溃缺陷时采用面向返回的编程(return-oriented programming)方法变得难得多。

pwntools

这一部分是关于 pwntools 工具的简明使用手册,涵盖了其在二进制漏洞利用中的几个核心功能模块,包括汇编与反汇编、ELF 文件操作、Shellcode 生成以及远程/本地进程的通信接口。内容清晰展示了如何利用 asmdisasm 快速转换指令,如何通过 ELF 类读取函数地址、GOT/PLT 信息,以及如何使用 shellcraft 快速生成不同架构的 Shellcode。

asm汇编与反汇编

elf文件操作

shellcode使用

pwnlib.shellcraft模块包含生成shell代码的函数。其中的子模块声明结构,比如

ARM架构: shellcraft.arm

AMD64架构: shellcraft.amd64

Intel 80386架构: shellcraft.i386

通用: shellcraft.common

remote/process读写接口

pwndbg调试

这一部分整理了在使用 GDB 调试器配合 pwndbg 插件进行二进制漏洞分析与调试时的常用指令与技巧。内容涵盖了调试基础操作、断点管理、内存与寄存器查看、栈与堆分析、源码追踪、程序保护机制识别(如 Canary、NX、PIE 等)等多个方面。

基本指令

执行指令

断点指令

删除、禁用断点

内存断点指令watch

捕获断点catch

打印指令

打印指令

打印源代码指令

修改数据指令

查找数据

堆操作指令

pwndbg插件独有指令


0 条评论
某人
表情
可输入 255