2007年11月29日星期四

[Tips]我和expression的日与被日

by jno
2007-11-29
http://www.ph4nt0m.org

Text Mode

当你第一次用expression方式来xss时,你肯定傻眼了,不停弹框,没法关闭浏览器,最终你只能祭出任务管理器将进程结束。也许你其他TAB页正有填到一半尚未提交的表单,你就这样被expression给日了,心里非常郁闷,于是就要想办法干它。

很多人第一反应就是cookie,没错这是个好办法:

<div style="width: expression(if(document.cookie.indexOf('xxxx')<0){alert(1);document.cookie='xxxx=1;'+document.cookie;})"></div>

不过这样写有个问题,就是被攻击者浏览器只能执行一次你的alert,cookie的作用域大于一次页面执行,适合用来做跨页面的标识,而不是仅仅用来控制一个页面里的某段代码的执行次数,而且你测试起来也挺麻烦,弄得不好就要清cookie。

循着这个思路很自然就会想到在页面里设置标识,于是就有了第二种方法:

<div style="width: expression(if(!window.xxx){alert(1);window.xxx=1;})"></div>

使用全局变量来做标识,使我的代码在这个页面级别只执行一次,这样是一个比较完美的办法,也是目前被使用的最多的办法。

但是到这里总还觉得不爽,虽然我的alert只被执行了一次,但是判断代码还是在被不停的执行,我们还是在被它日,只不过感觉不出来而已了,我们的目标是日它,办法就是执行完我们的代码后删除这条expression,翻阅MSDN你很快能找到合适的方法:

object.style.removeExpression(sPropertyName)

看起来很美,可是你把这个语句放进expression内部用它来删除expression自身却怎么也不能成功,该死的alert还是会一遍遍的弹出来。使用setTimeout延迟执行?失败;使用execScript在全局执行?失败;结合setTimeout和execScript在延迟在全局执行?还是失败;在body尾部append一个外部script来执行?失败;在body尾部append一个外部script并且setTimeout延迟并且execScript全局执行?草,终于tmd成功了:

<!------1.htm------>
<html>
<style>
body 
{
    width
: expression(eval(String.fromCharCode(0x61,0x6C,0x65,0x72,0x74,0x28,0x31,0x29,0x3B,0x69,0x66,0x28,0x64,0x6F,0x63,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x62,0x6F,0x64,0x79,0x29,0x7B,0x76,0x61,0x72,0x20,0x73,0x3D,0x64,0x6F,0x63,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x63,0x72,0x65,0x61,0x74,0x65,0x45,0x6C,0x65,0x6D,0x65,0x6E,0x74,0x28,0x22,0x73,0x63,0x72,0x69,0x70,0x74,0x22,0x29,0x3B,0x64,0x6F,0x63,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x62,0x6F,0x64,0x79,0x2E,0x61,0x70,0x70,0x65,0x6E,0x64,0x43,0x68,0x69,0x6C,0x64,0x28,0x73,0x29,0x3B,0x73,0x2E,0x73,0x72,0x63,0x3D,0x22,0x31,0x2E,0x6A,0x73,0x22,0x3B,0x7D)));
    
/*alert(1);if(document.body){var s=document.createElement("script");document.body.appendChild(s);s.src="1.js";}*/
}
</style>
<body>
</body>
</html>

//--------1.js---------//
setTimeout(function(){execScript("document.body.style.removeExpression(\"width\")");}, 0);

可是还有那么一点不完美,就是无论怎么样,最少也要执行两次,不过我爽了,总算把这个expression给日了。当然如果你是个完美主义者,可以用这个方法结合if(!window.xxx)法。

各位看官看到这里,可能已经严重怀疑我是被虐狂,这么多方法测试下来,我还不弹框框弹到崩溃?其实我并非浪得虚名,测之前早有准备,先厚者脸皮去幻影邮件列表跪求alert框框原理,没想到大家非常热情地给予帮助,最终zzzevazzz大侠最先找到实现API是MessageBoxIndirectW,从win2k源代码中觅得。然后又花上半日工夫草成一个hook MessageBoxIndirectW的小工具,可惜又遇到个小问题至今没有解决,这个函数的参数是个MSGBOXPARAMS结构体:

typedef struct {
    UINT cbSize;
    HWND hwndOwner;
    HINSTANCE hInstance;
    LPCTSTR lpszText;
    LPCTSTR lpszCaption;
    DWORD dwStyle;
    LPCTSTR lpszIcon;
    DWORD_PTR dwContextHelpId;
    MSGBOXCALLBACK lpfnMsgBoxCallback;
    DWORD dwLanguageId;
} MSGBOXPARAMS, 
*PMSGBOXPARAMS;

我写了个小程序测试发现只要把hwndOwner和dwStyle都置为0,这个对跨框就不是模态的,父窗口点关闭也可以关闭程序,我hook的目的也在于此,可是在IE里具体测试的时候,发现即使对话框不是模态的,我点关闭IE按钮也没法关闭IE窗口,所以这个方法只针对有TAB页的IE7有意义,对话框非模态后,我可以切换到其他TAB页去并关闭弹框的TAB页,但是对于IE6来说不能点关闭就没有意义,于是我干脆也不修改什么参数了,直接把这个函数返回掉了,代码在最后附上。

至此,我和expression的恩怨总算可以告一段落,整个世界清静了。

/*
* FileName: IEAlertPatch.c
* Version: 1.0
* Contact: luoluonet@yahoo.cn
* P.S: Thanks zzzEVAzzz, he found out the API that alert uses.
*/
#include 
<Windows.h>
#include 
<Tlhelp32.h>
#include 
<Imagehlp.h>

#pragma comment(lib, 
"advapi32.lib")

//
// function prototype
//
DWORD WINAPI GetProcessIdByName(LPCTSTR lpProcessName);
__inline HookProc();
BOOL WINAPI HookAlert(DWORD pId);
LPVOID GetSC(LPVOID lpProc, DWORD
* dwLen, DWORD dwReserved);

//
// start of winmain
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
{
    DWORD pId;
    OSVERSIONINFOEX osvi;
    BOOL bRet;
    TCHAR procName[] 
= TEXT("iexplore.exe");

    ZeroMemory(
&osvi, sizeof(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize 
= sizeof(OSVERSIONINFOEX);

    
//
    
// Get system version
    
//
    bRet = GetVersionEx((OSVERSIONINFO *)&osvi);
    
if (! bRet)
    {
        osvi.dwOSVersionInfoSize 
= sizeof(OSVERSIONINFO);
        bRet 
= GetVersionEx((OSVERSIONINFO *)&osvi);
        
if (! bRet)
            
goto FreeAndExit;
    }

    
// Verify if it is NT system
    if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
    {
        pId 
= GetProcessIdByName(procName);
        
if (pId != 0)
            HookAlert(pId);
    }

FreeAndExit:
    
return 0;

}
//
// End of WinMain
//

//
// @Name:        GetProcessIdByName
// @Author:        luoluo
// @Time:        2005-04-17
// @Param:        lpProcessName spacifies the ProcessName
// @Ret:        if success, return the process id
//                if failed, return 0
//        
DWORD WINAPI GetProcessIdByName(LPCTSTR lpProcessName)
{
    HANDLE hSnapshot;
    DWORD dwRet 
= 0;
    LPPROCESSENTRY32 pPe32;
    BOOL bRet;

    
// Get all the processes in the snapshot    
    hSnapshot = CreateToolhelp32Snapshot(0x000000020);
    
if (hSnapshot == INVALID_HANDLE_VALUE)
    {
        
goto FreeAndExit;
    }

    pPe32 
= (LPPROCESSENTRY32)malloc(sizeof(PROCESSENTRY32));
    ZeroMemory(pPe32, 
sizeof(PROCESSENTRY32));
    pPe32
->dwSize = sizeof(PROCESSENTRY32);

    
// Get the first process
    bRet = Process32First(hSnapshot, pPe32);
    
if (! bRet)
    {
        
goto FreeAndExit;
    }

    
if (stricmp(lpProcessName, pPe32->szExeFile) == 0)
    {
        dwRet 
= pPe32->th32ProcessID;
        
goto FreeAndExit;
    }

    
// Travesal the left processes
    while (TRUE)
    {
        bRet 
= Process32Next(hSnapshot, pPe32);
        
if (! bRet)
        {
            
goto FreeAndExit;
        }

        
if (stricmp(lpProcessName, pPe32->szExeFile) == 0)
        {
            dwRet 
= pPe32->th32ProcessID;
            
goto FreeAndExit;
        }
    }

FreeAndExit:
    
if (pPe32 != NULL)    free(pPe32);
    
if (hSnapshot != NULL)    CloseHandle(hSnapshot);

    
return dwRet;
}

__inline __declspec(naked) HookProc()
{
    __asm
    {
        leave
        retn 
4
        
/*
        push esi
        mov esi, [ebp+8h]
        mov dword ptr [esi+4h], 0h    // modify the hwnd
        mov dword ptr [esi+14h], 0h // modify the type
        pop esi
        
*/
        _emit 90h
        _emit 90h
        _emit 90h
        _emit 90h
    }
}

LPVOID GetSC(LPVOID lpProc, DWORD
* dwLen, DWORD dwReserved)
{
    LPVOID lpProc1 
= NULL;
    LPVOID lpSC 
= NULL;

    __asm
    {
        push ebx
        mov ebx, lpProc
        dec ebx
_loop:
        inc ebx
        cmp dword ptr [ebx], 90909090h
        jne _loop
        mov lpProc1, ebx
        pop ebx
    }

    
*dwLen = (DWORD)lpProc1 - (DWORD)lpProc;
    lpSC 
= malloc(*dwLen + dwReserved);
    memset(lpSC, 
0*dwLen + dwReserved);
    memcpy(lpSC, lpProc, 
*dwLen);
    
*dwLen += dwReserved;

    
return lpSC;
}

BOOL WINAPI HookAlert(DWORD pId)
{
    HMODULE hModule 
= NULL;
    DWORD dwMessageBoxIndirectW 
= 0;
    HANDLE hProcess;
    HANDLE hToken;
    TOKEN_PRIVILEGES tkp;
    BOOL bRet 
= FALSE;
    BOOL bRetVal;
    LPVOID lpCodeMemory;
    MEMORY_BASIC_INFORMATION mbi;
    SIZE_T szRet;
    DWORD dwOldProtect;
    DWORD dwJmpOffset 
= 0;
    unsigned 
char szJmpCode[5= {0};
    unsigned 
char szOldCode[5= {0};
    LPVOID lpHookCode 
= NULL;
    DWORD dwHookCodeLen 
= 0;

    hModule 
= LoadLibrary("user32.dll");
    dwMessageBoxIndirectW 
= (DWORD)GetProcAddress(hModule, "MessageBoxIndirectW");

    lpHookCode 
= GetSC(&HookProc, &dwHookCodeLen, 10);
    
if (lpHookCode == NULL)
    {
        
goto FreeAndExit;
    }

    
// Open process token to ajust privileges
    bRetVal = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);

    
if (! bRetVal)
    {
        
goto FreeAndExit;
    }

    
// Get the LUID for debug privilege
    bRetVal = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid);

    
if (! bRetVal)
    {
        
goto FreeAndExit;
    }

    tkp.PrivilegeCount 
= 1;
    tkp.Privileges[
0].Attributes = SE_PRIVILEGE_ENABLED;

    
// Adjust token privileges
    bRetVal = AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(&tkp), (PTOKEN_PRIVILEGES)NULL, 0);
    
if (! bRetVal)
    {
        
goto FreeAndExit;
    }

    
// Open remote process
    hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION, FALSE, pId);
    
if (hProcess == NULL)
    {
        
goto FreeAndExit;
    }

    
// Read 5 byte from function to be hooked
    bRetVal = ReadProcessMemory(hProcess, (LPCVOID)dwMessageBoxIndirectW, szOldCode, sizeof(szOldCode), NULL);
    
if (! bRetVal)
    {
        
goto FreeAndExit;
    }

    
// Allocate memory from remote process
    lpCodeMemory = VirtualAllocEx(hProcess, NULL, dwHookCodeLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    
if (lpCodeMemory == NULL)
    {
        
goto FreeAndExit;
    }

    
// Query the page information
    ZeroMemory(&mbi, sizeof(MEMORY_BASIC_INFORMATION));
    szRet 
= VirtualQueryEx(hProcess, lpCodeMemory, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
    
if (szRet == 0)
    {
        
goto FreeAndExit;
    }

    
// Modify the page protection for write
    bRetVal = VirtualProtectEx(hProcess, mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
    
if (! bRetVal)
    {
        
goto FreeAndExit;
    }

    
// the function has been hooked
    if (szOldCode[0== ((unsigned char)'\xE9'))
    {
        dwJmpOffset 
= (*((int*)(szOldCode + 1))) + dwMessageBoxIndirectW + 5 - ((DWORD)lpCodeMemory) - dwHookCodeLen + 5;
        memcpy(szOldCode 
+ 1, (LPVOID)(&dwJmpOffset), 4);
    }

    
// debugger present and breakpoint here
    if (szOldCode[0== '\xCC')
    {
        
goto FreeAndExit;
    }

    
// copy the start code of funciton hooked to the end of hook code
    memcpy((LPVOID)(((DWORD)lpHookCode) + dwHookCodeLen - 10), szOldCode, sizeof(szOldCode));

    
// code jmp back to function hooked
    memset((LPVOID)(((DWORD)lpHookCode) + dwHookCodeLen - 5), '\xE9'1);
    dwJmpOffset 
= dwMessageBoxIndirectW - ((DWORD)lpCodeMemory) - dwHookCodeLen + 5;
    memcpy((LPVOID)(((DWORD)lpHookCode) 
+ dwHookCodeLen - 4), (LPVOID)(&dwJmpOffset), 4);

    
// Write my code to remote process memory
    bRetVal = WriteProcessMemory(hProcess, lpCodeMemory, lpHookCode, dwHookCodeLen, 0);
    
if (! bRetVal)
    {
        VirtualFreeEx(hProcess, lpCodeMemory, dwHookCodeLen, MEM_RELEASE);
        
goto FreeAndExit;
    }

    
// Modify the page protection to protect
    bRetVal = VirtualProtectEx(hProcess, mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect);
    
if (! bRetVal)
    {
        
goto FreeAndExit;
    }

    
// hook code
    szJmpCode[0= '\xE9';    // jmp
    dwJmpOffset = ((DWORD)lpCodeMemory) - dwMessageBoxIndirectW - 5;
    memcpy(szJmpCode 
+ 1, (LPVOID)(&dwJmpOffset), 4); 

    
// Query the page information
    ZeroMemory(&mbi, sizeof(MEMORY_BASIC_INFORMATION));
    szRet 
= VirtualQueryEx(hProcess, (LPVOID)dwMessageBoxIndirectW, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
    
if (szRet == 0)
    {
        
goto FreeAndExit;
    }

    
// Modify the page protection for write
    bRetVal = VirtualProtectEx(hProcess, mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
    
if (! bRetVal)
    {
        
goto FreeAndExit;
    }
    
    
// Write hook code to the functon to be hooked
    bRetVal = WriteProcessMemory(hProcess, (LPVOID)dwMessageBoxIndirectW, szJmpCode, sizeof(szJmpCode), 0);
    
if (! bRetVal)
    {
        
goto FreeAndExit;
    }

    
// Modify the page protection to protect
    bRetVal = VirtualProtectEx(hProcess, mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect);
    
if (! bRetVal)
    {
        
goto FreeAndExit;
    }

FreeAndExit:
    
if (hProcess != NULL)
    {
        CloseHandle(hProcess);
    }
    
if (hToken != NULL)
    {
        CloseHandle(hToken);
    }
    
if (lpHookCode != NULL)
    {
        free(lpHookCode);
        lpHookCode 
= NULL;
    }

    
return bRet;
}

1 条评论:

Joe 说...

日! 用得着这么麻烦么