我和expression的日与被日 经典分析

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

当你第一次用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(0x00000002, 0);
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;
}

(0)

相关推荐

  • 计算2000年01月01日起到指定日的天数

    这是一个计算2000年01月01日起到指定日的天数的函数 <br> (算头也算尾)日期格式为:YYYY-MM-DD <br> <?php //    计算从2000年01月01日开始到某日的天数 function cal_start2end($end_day,$start_day) { $start_day=ereg_replace("-","",$start_day); $end_day=ereg_replace("-&qu

  • 2013年国家法定节假日安排 春节假期2月9日至15日

    国务院办公厅关于2013年部分节假日安排的通知 国办发明电[2012]33号 各省.自治区.直辖市人民政府,国务院各部委.各直属机构: 根据国务院<关于修改<全国年节及纪念日放假办法>的决定>,为便于各地区.各部门及早合理安排节假日旅游.交通运输.生产经营等有关工作,经国务院批准,现将2013年元旦.春节.清明节.劳动节.端午节.中秋节和国庆节放假调休日期的具体安排通知如下. 一.元旦: 1月1日至3日放假调休,共3天.1月5日(星期六).1月6日(星期日)上班. 二.春节: 2月

  • Javascript实现通过选择周数显示开始日和结束日的实现代码

    最近在一个项目中遇到一个查询页面,其中一个查询条件是根据选择的年份.月以及周数显示选择的该周从几号到几号,这样一个需求.在网上搜索了一下,有类似的但是没有完全一样的,只好自己动手.思路是首先取得所选择的该年月1号这一天是星期几,然后算出这一周中周一的日期. 以此为基点,第1周的结束日即为这一天加6,第2周的开始日为这一天加7,结束日为这一天加13,以此类推,直到推算出第5周的开始日和结束日. 具体代码如下: //该方法有两个参数,date是给定的日期型参数,days是需要在该日期进行加减的天数.

  • 我和expression的日与被日 经典分析

    by jno 2007-11-29 http://www.ph4nt0m.org 当你第一次用expression方式来xss时,你肯定傻眼了,不停弹框,没法关闭浏览器,最终你只能祭出任务管理器将进程结束.也许你其他TAB页正有填到一半尚未提交的表单,你就这样被expression给日了,心里非常郁闷,于是就要想办法干它. 很多人第一反应就是cookie,没错这是个好办法: <div style="width: expression(if(document.cookie.indexOf('

  • 清明节是几月几日 清明节的由来

    2012年是4月5日 清明节是根据二下四节气来推算的.所以不一定每年的清明节都是4月4日.清明是二十四节气之一,清明节就在清明那一天,每年的清明大致都在阳历的4月4日至4月6日那天.节气的划分是根据太阳跟赤道的角度来定的,因此农历也没有一定的日子. 2010的清明节是4月5日 2010年清明节放假时间:4月4日至5日放假,共3天.其中,4月3日(星期六),4月4日(星期日)照常公休.4月5日(星期一,农历清明当日)为法定节假日. 清明,农历二十四节气之一.中国传统的清明节大约始于周代,距今已有二

  • 植树节是几月几日,植树节的的由来和意义整理

    中国: 每年3月12日为植树节 ; 朝鲜: 每年3月2日为植树节; 印度: 每年7月第一周开展全国植树节活动; 斯里兰卡:9月17日为植树节; 泰国:国庆节定为植树节; 菲律宾: 每年9月第二个星期六为植树节; 约旦:1月15日是植树节; 巴勒斯坦:1月6日是植树节; 埃及: 每年9月至11月为植树节; 巴西: 每年9月21日为植树节; 墨西哥: 每年6至9月的雨季里开展植树节活动; 哥伦比亚: 每年10月12日为植树节; 萨尔瓦多: 每年的6月21日举行; 法国: 每年3月为法定的绿化月,3月

  • Sql Server基本函数

    1.字符串函数  长度与分析用  datalength(Char_expr) 返回字符串包含字符数,但不包含后面的空格 substring(expression,start,length) 不多说了,取子串 right(char_expr,int_expr) 返回字符串右边int_expr个字符 字符操作类 upper(char_expr) 转为大写 lower(char_expr) 转为小写 space(int_expr) 生成int_expr个空格 replicate(char_expr,i

  • sql基本函数大全

    SQLServer基本函数  1.字符串函数长度与分析用 datalength(Char_expr) 返回字符串包含字符数,但不包含后面的空格  substring(expression,start,length) 不多说了,取子串  right(char_expr,int_expr) 返回字符串右边int_expr个字符  字符操作类  upper(char_expr) 转为大写  lower(char_expr) 转为小写  space(int_expr) 生成int_expr个空格  re

  • 深入SQL截取字符串(substring与patindex)的详解

    首先学习两个函数1.substring  返回字符.binary.text 或 image 表达式的一部分.基本语法:SUBSTRING ( expression , start , length ) expression:字符串.二进制字符串.text.image.列或包含列的表达式start:整数,指定子串的开始位置      注:SQL中"1"表示字符串中的第一个字符,而.NET中"0"表示第一个字符length:整数,指定子串的长度(要返回的字符数或字节数)

  • Go定时器cron的使用详解

    cron是什么 cron的意思就是:计划任务,说白了就是定时任务.我和系统约个时间,你在几点几分几秒或者每隔几分钟跑一个任务(job),就那么简单. cron表达式 cron表达式是一个好东西,这个东西不仅Java的quartZ能用到,Go语言中也可以用到.我没有用过Linux的cron,但网上说Linux也是可以用crontab -e 命令来配置定时任务.Go语言和Java中都是可以精确到秒的,但是Linux中不行. cron表达式代表一个时间的集合,使用6个空格分隔的字段表示: 字段名 是否

随机推荐