Skip to content
KitploitKITPLOIT
工具博客
Log in
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
how-to-bypass-aslr-on-linux-x86_64 — ASLR 绕过,无需信息泄露 | Kitploit
工具/GitHubGitHub/nick0ve/how-to-bypass-aslr-on-linux-x86_64
漏洞利用CTF学习与教育二进制利用
GitHubnick0ve/how-to-bypass-aslr-on-linux-x86_64

how-to-bypass-aslr-on-linux-x86_64

ASLR 绕过,无需信息泄露

查看仓库

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
16717164年前Kitploit 审核通过

在 Linux x86-64 上绕过 64 位 ASLR

在这篇文章中,我将讨论 Samuel Groß 在他的 Remote iPhone Exploitation Part 2: Bringing Light into the Darkness -- a Remote ASLR Bypass 中描述的技术在 Linux x86_64 上绕过 ASLR 的应用。

为了展示这一点,我将解决来自 Buckeye CTF 的一个 pwnable 挑战,即 guess_god。

我会尽量让内容对初学者友好,如果你觉得自己已经足够自信,或者只想直接看漏洞利用,可以随意跳过任何部分。

0. 引言


我没有参加这个 CTF,但在 CTF 结束前大约 2 小时,由于 Guray00 在 fibonhack 的 discord 上请求帮助解决一些加密相关的把戏,我因此对这个挑战产生了兴趣。

我帮不上他的忙,但我看了一下 pwnable 挑战,觉得弄懂 P0 博客文章会是一件好事,并希望能拿到那份赏金。

1. ASLR 及其绕过方法

1.1 什么是 ASLR?

地址空间布局随机化(ASLR)是一种计算机安全技术,它涉及在进程的地址空间中随机定位可执行文件的基础地址以及库、堆和栈的位置。

1.2 Linux 上的 ASLR

在 Linux 上,你可以通过读取 /proc/<pid>/maps 文件,借助 procfs 来检查给定 pid 进程的内存映射。

如果你是一个进程,并且想了解自己的内存映射,可以读取 /proc/self/maps。

例如,你可以尝试用 cat 读取 /proc/self/maps:``` root@088ec31b2ce9:/home/ctf/challenge# cat /proc/self/maps 55faeb01c000-55faeb01e000 r--p 00000000 fe:01 2497233 /usr/bin/cat 55faeb01e000-55faeb023000 r-xp 00002000 fe:01 2497233 /usr/bin/cat 55faeb023000-55faeb026000 r--p 00007000 fe:01 2497233 /usr/bin/cat 55faeb026000-55faeb027000 r--p 00009000 fe:01 2497233 /usr/bin/cat 55faeb027000-55faeb028000 rw-p 0000a000 fe:01 2497233 /usr/bin/cat 55faeb115000-55faeb136000 rw-p 00000000 00:00 0 [heap] 7fe15dfb1000-7fe15dfd5000 rw-p 00000000 00:00 0 7fe15dfd5000-7fe15dffb000 r--p 00000000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7fe15dffb000-7fe15e166000 r-xp 00026000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7fe15e166000-7fe15e1b2000 r--p 00191000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7fe15e1b2000-7fe15e1b5000 r--p 001dc000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7fe15e1b5000-7fe15e1b8000 rw-p 001df000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7fe15e1b8000-7fe15e1c3000 rw-p 00000000 00:00 0 7fe15e1c7000-7fe15e1c8000 r--p 00000000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7fe15e1c8000-7fe15e1ef000 r-xp 00001000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7fe15e1ef000-7fe15e1f9000 r--p 00028000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7fe15e1f9000-7fe15e1fb000 r--p 00031000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7fe15e1fb000-7fe15e1fd000 rw-p 00033000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7fff4388f000-7fff438b0000 rw-p 00000000 00:00 0 [stack] 7fff43989000-7fff4398d000 r--p 00000000 00:00 0 [vvar] 7fff4398d000-7fff4398f000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]

root@088ec31b2ce9:/home/ctf/challenge# cat /proc/self/maps 55ffc0b1b000-55ffc0b1d000 r--p 00000000 fe:01 2497233 /usr/bin/cat 55ffc0b1d000-55ffc0b22000 r-xp 00002000 fe:01 2497233 /usr/bin/cat 55ffc0b22000-55ffc0b25000 r--p 00007000 fe:01 2497233 /usr/bin/cat 55ffc0b25000-55ffc0b26000 r--p 00009000 fe:01 2497233 /usr/bin/cat 55ffc0b26000-55ffc0b27000 rw-p 0000a000 fe:01 2497233 /usr/bin/cat 55ffc2108000-55ffc2129000 rw-p 00000000 00:00 0 [heap] 7f1ec6e0f000-7f1ec6e33000 rw-p 00000000 00:00 0 7f1ec6e33000-7f1ec6e59000 r--p 00000000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec6e59000-7f1ec6fc4000 r-xp 00026000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec6fc4000-7f1ec7010000 r--p 00191000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec7010000-7f1ec7013000 r--p 001dc000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec7013000-7f1ec7016000 rw-p 001df000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec7016000-7f1ec7021000 rw-p 00000000 00:00 0 7f1ec7025000-7f1ec7026000 r--p 00000000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec7026000-7f1ec704d000 r-xp 00001000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec704d000-7f1ec7057000 r--p 00028000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec7057000-7f1ec7059000 r--p 00031000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec7059000-7f1ec705b000 rw-p 00033000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7ffc72fa4000-7ffc72fc5000 rw-p 00000000 00:00 0 [stack] 7ffc72fe7000-7ffc72feb000 r--p 00000000 00:00 0 [vvar] 7ffc72feb000-7ffc72fed000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]

### 内存映射模式

如果你重复操作几次,就能推断出:
* 二进制文件的 PIE 基地址应该位于 0x00005500_00000000-0x00005700_00000000 范围内,这意味着有 2TB 个可能的地址。
* 堆位于二进制文件附近。
* 库位于 0x00007f00_00000000 - 0x00007fff_ffffffff 范围内,有 1TB 个可能的地址。
* 栈 \(大部分情况下\) 位于 0x00007ffc_00000000 - 0x00007fff_ffffffff 范围内,有 16gb 个可能的地址。
* 范围 0xffffffffff600000 - 0xffffffffff601000 总是被映射,如果你对它是什么感到好奇,可以阅读[这篇文章](http://terenceli.github.io/%E6%8A%80%E6%9C%AF/2019/02/13/vsyscall-and-vdso)。

## 1.3 如何在没有信息泄露的情况下绕过 ASLR

让我们讨论一下,当无法进行信息泄露时,你可以用什么方法来绕过 ASLR。

以下是我尝试对阅读 Saelo 博客文章后的收获所做的总结。

要绕过 ASLR,你需要:
* 一种内存喷射技术,它允许你在给定的地址范围内映射指定大小的连续内存。

  正如他所说,有两种方式可以做到:
  1. 滥用内存泄漏(不是信息泄露!)——一种内存块被“遗忘”而从未释放的 bug——并多次触发它,直到泄漏出所需数量的内存。
  2. 寻找并滥用“amplification gadget”(放大小工具):一段获取现有数据块并复制它(可能多次)的代码,从而使攻击者只需发送相对较少的字节就能喷射大量内存。
* 一个 `isAddressMapped` oracle,给定一个地址,它会告诉你该地址是否已被映射。

### Linux 上绕过 ASLR 的 PoC

让我们尝试复现 saelo 的 PoC,以在 Linux 上彻底打破 aslr。

<p align="center"> <img src="https://assets.kitploit.com/production/public/readmes/48660/93fcc6aff8ffd2e8395f34b35bc2f5d244e74a53b2fac39c3dae35c802227339.png" > <i> saelo 的 poc</i> <p/> <br/>

在 Linux 上这并不容易,只有当你能够分配 16TB 内存时,才有可能完全打破 ASLR。```C
#include <stdio.h>
#include <stdlib.h>

int main()
{
    // 64gb
    size_t size = 0x1000000000;

    // 16TB allocations
    for (int i = 0; i < 256; i++) {
        void *mem = malloc(size); // this ends up calling mmap
        if (!mem) {
            puts("Failed");
            return 1;
        }
        printf("%p\n", mem);
    }

    unsigned int *mem = (void*)0x7f0000000000ULL;
    *mem = 0x41414141;
    printf("R/W to %p: %x\n", mem, *mem);

    return 0;
}

关于 glibc 内存分配的说明

摘自 man malloc 的说明:

  • 通常,malloc() 会从堆中分配内存,并在需要时使用 sbrk(2) 调整堆的大小。当分配的内存块大于 MMAP_THRESHOLD 字节时,glibc 的 malloc() 实现会使用 mmap(2) 将该内存作为私有匿名映射进行分配。MMAP_THRESHOLD 默认为 128 kB,但可以使用 mallopt(3) 进行调整。通过 mmap(2) 执行的分配不受 RLIMIT_DATA 资源限制的影响(参见 getrlimit(2))。

因此 void *mem = malloc(size) 最终会调用 mmap(size + malloc_metadata_size, ...)

由于库是通过 ld 以 mmap 方式映射进进程的,这些分配最终会落在库附近。

边界跨越技巧

如果你查看 malloc 返回的地址,就能更好地理解发生了什么。小贴士:关注最高有效字节。

mem跨越 16TB 边界?
0x7fb03b55e010否
0x7fa03b55d010否
0x7f903b55c010否
0x7f803b55b010否
0x7f703b55a010否
0x7f603b559010否
0x7f503b558010否
0x7f403b557010否
0x7f303b556010否
0x7f203b555010否
0x7f103b554010否
0x7f003b553010否
0x7ef03b552010是
0x7ee03b551010是
0x7ed03b550010是
0x7ec03b54f010是
下载工具