首页
社区
课程
招聘
[原创]Win 8.1/10 x64平台 S3 Resume任意执行漏洞 (基于UEFI平台)
发表于: 2020-4-20 00:39 5313

[原创]Win 8.1/10 x64平台 S3 Resume任意执行漏洞 (基于UEFI平台)

2020-4-20 00:39
5313

  • 前言:
    不知道这个漏洞之前有没有人发现并上报,我上报国家信息安全漏洞共享平台,对方要求我提供POC。我掂量了一下自己能力,觉得在驱动里实现POC难度挺高的,所以我就放弃了(当然,我已经顺手报给微软MSRC)。于是我就移步至此,记录调试过程。实验结论基于我前两篇文章:探索Windows S3唤醒函数 (一) 探索Windows S3唤醒函数 (二) 从Win7开始,x64位平台需要在测试模式下才能加载未认证的驱动,一般也没几个用户会开测试模式,所以限制了这个漏洞的利用。另外Win7可能不支持UEFI Bios,因此,这个漏洞可能在WinXp/Win7系统上不能奏效。

  • 产生原理:
前面文章里反复提到一个内核变量: HalpLowStubPhysicalAddress,漏洞的产生全在于它:
Step 1).OS在 HalpSetupAcpiPhase0阶段,调用HalpAllocPhysicalMemory分配(可执行)物理地址,并将起始地址写入 HalpLowStubPhysicalAddress;(下文会给出调试证明。随文附件中,我提供了Win10 RS5 x64 Free Build Hal.dll和对应的Hal.i64 )
Step 1.5).在之后的某个阶段(暂时没有跟踪到),OS将初始化 HalpLowStubPhysicalAddress所指的物理内存,其起始4Byte为一个JMP跳转指令; (下文亦会给出调试证明)
Step 2).进入S3 Sleep时,OS以 HalpLowStubPhysicalAddress 作为参数,调用HalpSetupRealModeResume函数,将 HalpLowStubPhysicalAddress的值写入内核变量HalpWakeVector。前面文章提过, HalpWakeVector对应UEFI S3 WakeVector;
Step 3).外部事件触发S3 Resume,UEFI Bios执行 S3ResumeBootOs,通过SwitchStack函数跳转到OS指定的S3 WakeVector,也就是 HalpLowStubPhysicalAddress的值;(下文只能提供Intel Comet Lake RVP board bios log)

  • 利用方式:
原理都写到这份上了,大家应该心知肚明了:创建驱动,仿造 Step 1)和Step 1.5)分配物理内存并在物理内存里构建JMP。然后覆盖 内核变量HalpLowStubPhysicalAddress的值为我们分配的物理地址!!!,完成后让OS进入S3,推它一把,剩下的它自己完成去吧~

  • 证明:
首先证明Step 3):
2020-04-14-04:15:15.962 PROGRESS CODE: V03031006 I0
2020-04-14-04:15:15.962 
2020-04-14-04:15:16.009 Transfer to 16bit OS waking vector - 1000
2020-04-14-04:15:16.009 
2020-04-14-04:15:18.018 Froome: TaskSmmPanelPowerOnOffSequense set EC_CONF_DEV_En to 0x1
这段日志出自 Intel Comet Lake RVP board的串口输出日志,对应实现可以定位到下列UEFI源码(UEFI源码,大家可以从UDK2015项目中获得。我用的是IBV基于UDK的CodeBase,涉密不便于贴出):

这段日志表明了WakeVector的物理内存位于0x1000,这个物理地址由OS填写(确切的说是ACPI子系统填写)。

再证明Step 1):
a).首先查找OS为导出函数HalpSetupRealModeResume,根据IDA分析,我知道:HalpSetupRealModeResume距离OS导出函数hal!HalPerformEndOfInterrupt的偏移为0x2c35,所以我先定位运行时hal!HalPerformEndOfInterrupt函数的地址(注意,每次重启函数地址都会变,但函数间偏移不会变,不要随意下函数断点!)
;查找内核导出函数hal!HalPerformEndOfInterrupt
0: kd> x hal!*Halp* 
fffff800`26c0bad0 hal!HalPerformEndOfInterrupt (<no parameter info>)
fffff800`26c210b0 hal!HalProcessorIdle (<no parameter info>)

;hal!HalPerformEndOfInterrupt+偏移得到HalpSetupRealModeResume的地址
;验证一下得到的地址对应的反汇编代码是否和IDA分析的结果一致,下图为IDA HalpSetupRealModeResume的输出
;当然一致,要不然我就不贴上来了!
0: kd> u fffff800`26c210b0+2c35
hal!HalProcessorIdle+0x2c35:
fffff800`26c23ce5 488b0d64b00400  mov     rcx,qword ptr [hal!HalHandleNMI+0x20c40 (fffff800`26c6ed50)]
fffff800`26c23cec 8b15f6ae0400    mov     edx,dword ptr [hal!HalHandleNMI+0x20ad8 (fffff800`26c6ebe8)]
fffff800`26c23cf2 e829d8ffff      call    hal!HalProcessorIdle+0x470 (fffff800`26c21520)
fffff800`26c23cf7 84c0            test    al,al
fffff800`26c23cf9 0f8546a1feff    jne     hal!HalFlushCommonBuffer+0x6f5 (fffff800`26c0de45)
fffff800`26c23cff e9dca0feff      jmp     hal!HalFlushCommonBuffer+0x690 (fffff800`26c0dde0)
fffff800`26c23d04 ffc3            inc     ebx
fffff800`26c23d06 851dd0b20400    test    dword ptr [hal!HalHandleNMI+0x20ecc (fffff800`26c6efdc)],ebx


b). 从 HalpSetupRealModeResume函数中得到内核变量HalpWakeVector的地址:
根据IDA的分析, HalpWakeVector是全局变量,并且被 HalpSetupRealModeResume函数访问(即上图红框处),因此我从 HalpSetupRealModeResume函数中得到内核变量HalpWakeVector的地址:fffff800`26c6ebe8
fffff800`26c23cec 8b15f6ae0400    mov     edx,dword ptr [hal!HalHandleNMI+0x20ad8 (fffff800`26c6ebe8)]

c).对比地址fffff800`26c6ebe8的值是否和 Intel Comet Lake RVP board输出的WakeVector相同:
0: kd> dd fffff800`26c6ebe8
fffff800`26c6ebe8  00001000 00000000 00000000 00000000
fffff800`26c6ebf8  ec50d00c ffff8600 00000000 00000000
两者值一致,但需要注意地址fffff800`26c6ebe8,也就是 HalpWakeVector存储的值是物理地址,因此要用windbg扩展命令!dd查看内容:
0: kd> !dd 00001000 
#    1000 00064de9 00000001 00000001 1018003f
#    1010 00000000 00000000 00000000 00000000
#    1020 00000000 00000000 00000000 00209b00
#    1030 00000000 00000000 0000ffff 00cf9300
敏感的你看了0x1000处前4B马上能意识到这是JMP指令,是的没错,用OD试试

由此可见,UEFI代码从Facs->FirmwareWakingVector跳转到 HalpWakeVector后,又会执行一次Jmp指令跳转到指定的payload。我们的实验驱动也可以这样实现,源码我就不提供了,太麻烦!

  • 产生原理:
前面文章里反复提到一个内核变量: HalpLowStubPhysicalAddress,漏洞的产生全在于它:
Step 1).OS在 HalpSetupAcpiPhase0阶段,调用HalpAllocPhysicalMemory分配(可执行)物理地址,并将起始地址写入 HalpLowStubPhysicalAddress;(下文会给出调试证明。随文附件中,我提供了Win10 RS5 x64 Free Build Hal.dll和对应的Hal.i64 )
Step 1.5).在之后的某个阶段(暂时没有跟踪到),OS将初始化 HalpLowStubPhysicalAddress所指的物理内存,其起始4Byte为一个JMP跳转指令; (下文亦会给出调试证明)
Step 2).进入S3 Sleep时,OS以 HalpLowStubPhysicalAddress 作为参数,调用HalpSetupRealModeResume函数,将 HalpLowStubPhysicalAddress的值写入内核变量HalpWakeVector。前面文章提过, HalpWakeVector对应UEFI S3 WakeVector;
Step 3).外部事件触发S3 Resume,UEFI Bios执行 S3ResumeBootOs,通过SwitchStack函数跳转到OS指定的S3 WakeVector,也就是 HalpLowStubPhysicalAddress的值;(下文只能提供Intel Comet Lake RVP board bios log)

  • 利用方式:
原理都写到这份上了,大家应该心知肚明了:创建驱动,仿造 Step 1)和Step 1.5)分配物理内存并在物理内存里构建JMP。然后覆盖 内核变量HalpLowStubPhysicalAddress的值为我们分配的物理地址!!!,完成后让OS进入S3,推它一把,剩下的它自己完成去吧~

  • 证明:
首先证明Step 3):
2020-04-14-04:15:15.962 PROGRESS CODE: V03031006 I0
2020-04-14-04:15:15.962 
2020-04-14-04:15:16.009 Transfer to 16bit OS waking vector - 1000
2020-04-14-04:15:16.009 
2020-04-14-04:15:18.018 Froome: TaskSmmPanelPowerOnOffSequense set EC_CONF_DEV_En to 0x1
这段日志出自 Intel Comet Lake RVP board的串口输出日志,对应实现可以定位到下列UEFI源码(UEFI源码,大家可以从UDK2015项目中获得。我用的是IBV基于UDK的CodeBase,涉密不便于贴出):

这段日志表明了WakeVector的物理内存位于0x1000,这个物理地址由OS填写(确切的说是ACPI子系统填写)。

再证明Step 1):
a).首先查找OS为导出函数HalpSetupRealModeResume,根据IDA分析,我知道:HalpSetupRealModeResume距离OS导出函数hal!HalPerformEndOfInterrupt的偏移为0x2c35,所以我先定位运行时hal!HalPerformEndOfInterrupt函数的地址(注意,每次重启函数地址都会变,但函数间偏移不会变,不要随意下函数断点!)
;查找内核导出函数hal!HalPerformEndOfInterrupt
0: kd> x hal!*Halp* 
fffff800`26c0bad0 hal!HalPerformEndOfInterrupt (<no parameter info>)
fffff800`26c210b0 hal!HalProcessorIdle (<no parameter info>)

;hal!HalPerformEndOfInterrupt+偏移得到HalpSetupRealModeResume的地址
;验证一下得到的地址对应的反汇编代码是否和IDA分析的结果一致,下图为IDA HalpSetupRealModeResume的输出
;当然一致,要不然我就不贴上来了!
0: kd> u fffff800`26c210b0+2c35
hal!HalProcessorIdle+0x2c35:
fffff800`26c23ce5 488b0d64b00400  mov     rcx,qword ptr [hal!HalHandleNMI+0x20c40 (fffff800`26c6ed50)]
fffff800`26c23cec 8b15f6ae0400    mov     edx,dword ptr [hal!HalHandleNMI+0x20ad8 (fffff800`26c6ebe8)]
fffff800`26c23cf2 e829d8ffff      call    hal!HalProcessorIdle+0x470 (fffff800`26c21520)
fffff800`26c23cf7 84c0            test    al,al
fffff800`26c23cf9 0f8546a1feff    jne     hal!HalFlushCommonBuffer+0x6f5 (fffff800`26c0de45)
fffff800`26c23cff e9dca0feff      jmp     hal!HalFlushCommonBuffer+0x690 (fffff800`26c0dde0)
fffff800`26c23d04 ffc3            inc     ebx
fffff800`26c23d06 851dd0b20400    test    dword ptr [hal!HalHandleNMI+0x20ecc (fffff800`26c6efdc)],ebx


b). 从 HalpSetupRealModeResume函数中得到内核变量HalpWakeVector的地址:
根据IDA的分析, HalpWakeVector是全局变量,并且被 HalpSetupRealModeResume函数访问(即上图红框处),因此我从 HalpSetupRealModeResume函数中得到内核变量HalpWakeVector的地址:fffff800`26c6ebe8
fffff800`26c23cec 8b15f6ae0400    mov     edx,dword ptr [hal!HalHandleNMI+0x20ad8 (fffff800`26c6ebe8)]

c).对比地址fffff800`26c6ebe8的值是否和 Intel Comet Lake RVP board输出的WakeVector相同:
0: kd> dd fffff800`26c6ebe8
fffff800`26c6ebe8  00001000 00000000 00000000 00000000
fffff800`26c6ebf8  ec50d00c ffff8600 00000000 00000000
两者值一致,但需要注意地址fffff800`26c6ebe8,也就是 HalpWakeVector存储的值是物理地址,因此要用windbg扩展命令!dd查看内容:
0: kd> !dd 00001000 
#    1000 00064de9 00000001 00000001 1018003f
#    1010 00000000 00000000 00000000 00000000
#    1020 00000000 00000000 00000000 00209b00
#    1030 00000000 00000000 0000ffff 00cf9300
敏感的你看了0x1000处前4B马上能意识到这是JMP指令,是的没错,用OD试试

由此可见,UEFI代码从Facs->FirmwareWakingVector跳转到 HalpWakeVector后,又会执行一次Jmp指令跳转到指定的payload。我们的实验驱动也可以这样实现,源码我就不提供了,太麻烦!

  • 产生原理:
前面文章里反复提到一个内核变量: HalpLowStubPhysicalAddress,漏洞的产生全在于它:
  • 产生原理:
Step 1).OS在 HalpSetupAcpiPhase0阶段,调用HalpAllocPhysicalMemory分配(可执行)物理地址,并将起始地址写入 HalpLowStubPhysicalAddress;(下文会给出调试证明。随文附件中,我提供了Win10 RS5 x64 Free Build Hal.dll和对应的Hal.i64 )
Step 1.5).在之后的某个阶段(暂时没有跟踪到),OS将初始化 HalpLowStubPhysicalAddress所指的物理内存,其起始4Byte为一个JMP跳转指令; (下文亦会给出调试证明)
Step 2).进入S3 Sleep时,OS以 HalpLowStubPhysicalAddress 作为参数,调用HalpSetupRealModeResume函数,将 HalpLowStubPhysicalAddress的值写入内核变量HalpWakeVector。前面文章提过, HalpWakeVector对应UEFI S3 WakeVector;
Step 3).外部事件触发S3 Resume,UEFI Bios执行 S3ResumeBootOs,通过SwitchStack函数跳转到OS指定的S3 WakeVector,也就是 HalpLowStubPhysicalAddress的值;(下文只能提供Intel Comet Lake RVP board bios log)

  • 利用方式:
原理都写到这份上了,大家应该心知肚明了:创建驱动,仿造 Step 1)和Step 1.5)分配物理内存并在物理内存里构建JMP。然后覆盖 内核变量HalpLowStubPhysicalAddress的值为我们分配的物理地址!!!,完成后让OS进入S3,推它一把,剩下的它自己完成去吧~
原理都写到这份上了,大家应该心知肚明了:创建驱动,仿造 Step 1)和Step 1.5)分配物理内存并在物理内存里构建JMP。然后覆盖 内核变量HalpLowStubPhysicalAddress的值为我们分配的物理地址!!!,完成后让OS进入S3,推它一把,剩下的它自己完成去吧~

  • 证明:
首先证明Step 3):
首先证明Step 3):
2020-04-14-04:15:15.962 PROGRESS CODE: V03031006 I0
2020-04-14-04:15:15.962 
2020-04-14-04:15:16.009 Transfer to 16bit OS waking vector - 1000
2020-04-14-04:15:16.009 
2020-04-14-04:15:18.018 Froome: TaskSmmPanelPowerOnOffSequense set EC_CONF_DEV_En to 0x1
这段日志出自 Intel Comet Lake RVP board的串口输出日志,对应实现可以定位到下列UEFI源码(UEFI源码,大家可以从UDK2015项目中获得。我用的是IBV基于UDK的CodeBase,涉密不便于贴出):
2020-04-14-04:15:15.962 PROGRESS CODE: V03031006 I0
2020-04-14-04:15:15.962 
2020-04-14-04:15:16.009 Transfer to 16bit OS waking vector - 1000
2020-04-14-04:15:16.009 
2020-04-14-04:15:18.018 Froome: TaskSmmPanelPowerOnOffSequense set EC_CONF_DEV_En to 0x1
这段日志出自 Intel Comet Lake RVP board的串口输出日志,对应实现可以定位到下列UEFI源码(UEFI源码,大家可以从UDK2015项目中获得。我用的是IBV基于UDK的CodeBase,涉密不便于贴出):

这段日志表明了WakeVector的物理内存位于0x1000,这个物理地址由OS填写(确切的说是ACPI子系统填写)。

再证明Step 1):
a).首先查找OS为导出函数HalpSetupRealModeResume,根据IDA分析,我知道:HalpSetupRealModeResume距离OS导出函数hal!HalPerformEndOfInterrupt的偏移为0x2c35,所以我先定位运行时hal!HalPerformEndOfInterrupt函数的地址(注意,每次重启函数地址都会变,但函数间偏移不会变,不要随意下函数断点!)
;查找内核导出函数hal!HalPerformEndOfInterrupt
0: kd> x hal!*Halp* 
fffff800`26c0bad0 hal!HalPerformEndOfInterrupt (<no parameter info>)
fffff800`26c210b0 hal!HalProcessorIdle (<no parameter info>)

;hal!HalPerformEndOfInterrupt+偏移得到HalpSetupRealModeResume的地址
;验证一下得到的地址对应的反汇编代码是否和IDA分析的结果一致,下图为IDA HalpSetupRealModeResume的输出
;当然一致,要不然我就不贴上来了!
0: kd> u fffff800`26c210b0+2c35
hal!HalProcessorIdle+0x2c35:
fffff800`26c23ce5 488b0d64b00400  mov     rcx,qword ptr [hal!HalHandleNMI+0x20c40 (fffff800`26c6ed50)]
fffff800`26c23cec 8b15f6ae0400    mov     edx,dword ptr [hal!HalHandleNMI+0x20ad8 (fffff800`26c6ebe8)]
fffff800`26c23cf2 e829d8ffff      call    hal!HalProcessorIdle+0x470 (fffff800`26c21520)
fffff800`26c23cf7 84c0            test    al,al
fffff800`26c23cf9 0f8546a1feff    jne     hal!HalFlushCommonBuffer+0x6f5 (fffff800`26c0de45)
fffff800`26c23cff e9dca0feff      jmp     hal!HalFlushCommonBuffer+0x690 (fffff800`26c0dde0)
fffff800`26c23d04 ffc3            inc     ebx
fffff800`26c23d06 851dd0b20400    test    dword ptr [hal!HalHandleNMI+0x20ecc (fffff800`26c6efdc)],ebx


b). 从 HalpSetupRealModeResume函数中得到内核变量HalpWakeVector的地址:
;查找内核导出函数hal!HalPerformEndOfInterrupt
0: kd> x hal!*Halp* 
fffff800`26c0bad0 hal!HalPerformEndOfInterrupt (<no parameter info>)
fffff800`26c210b0 hal!HalProcessorIdle (<no parameter info>)

;hal!HalPerformEndOfInterrupt+偏移得到HalpSetupRealModeResume的地址
;验证一下得到的地址对应的反汇编代码是否和IDA分析的结果一致,下图为IDA HalpSetupRealModeResume的输出
;当然一致,要不然我就不贴上来了!
0: kd> u fffff800`26c210b0+2c35
hal!HalProcessorIdle+0x2c35:
fffff800`26c23ce5 488b0d64b00400  mov     rcx,qword ptr [hal!HalHandleNMI+0x20c40 (fffff800`26c6ed50)]
fffff800`26c23cec 8b15f6ae0400    mov     edx,dword ptr [hal!HalHandleNMI+0x20ad8 (fffff800`26c6ebe8)]
fffff800`26c23cf2 e829d8ffff      call    hal!HalProcessorIdle+0x470 (fffff800`26c21520)
fffff800`26c23cf7 84c0            test    al,al
fffff800`26c23cf9 0f8546a1feff    jne     hal!HalFlushCommonBuffer+0x6f5 (fffff800`26c0de45)
fffff800`26c23cff e9dca0feff      jmp     hal!HalFlushCommonBuffer+0x690 (fffff800`26c0dde0)
fffff800`26c23d04 ffc3            inc     ebx
fffff800`26c23d06 851dd0b20400    test    dword ptr [hal!HalHandleNMI+0x20ecc (fffff800`26c6efdc)],ebx


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 2
支持
分享
最新回复 (7)
雪    币: 3738
活跃值: (3872)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
666,有源码当然该你得瑟啊!
2020-4-20 10:38
0
雪    币: 341
活跃值: (1005)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
劫持jmp
2020-4-20 11:04
0
雪    币: 802
活跃值: (4433)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
4
是有符号,不是有源码
2020-4-20 13:55
0
雪    币: 802
活跃值: (4433)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
5
是可以 不过那块内存是16位模式
2020-4-20 18:35
0
雪    币: 341
活跃值: (1005)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
hyjxiaobia 是可以 不过那块内存是16位模式
对,我看见过别人写得劫持jmp,从实模式到应用层贼溜。
2020-4-20 19:12
0
雪    币: 58
活跃值: (1135)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
hyjxiaobia 是有符号,不是有源码
符号不是公开的?
2020-4-22 17:36
0
雪    币: 802
活跃值: (4433)
能力值: ( LV12,RANK:260 )
在线值:
发帖
回帖
粉丝
8
yangya 符号不是公开的?
理论上是公开的
2020-4-22 20:27
0
游客
登录 | 注册 方可回帖
返回
//