漏洞简介
红帆iOffice的/ioffice/Identity/HbcaUserLogin.aspx接口存在SQL注入漏洞。攻击者可通过构造恶意SQL语句,未经身份验证地获取数据库敏感信息,影响范围包括红帆iOffice系统的数据访问权限。
影响版本
fofa语法
(title="iOffice.net" || body="/iOffice/js" || (body="iOffice.net" && header!="couchdb" && header!="drupal") || body="iOfficeOcxSetup.exe" || body="Hongfan. All Rights Reserved")
漏洞分析
先看下HbcaUserLogin.aspx 里引用的代码在哪里(Inherits)
<%@ Page Language="vb" AutoEventWireup="false" Inherits="iden.HbcaUserLogin" CodeBehind="HbcaUserLogin.aspx.vb" %>
去bin目录找到iden.dll后编译打开,看HbcaUserLogin它的实现逻辑
public class HbcaUserLogin : WebPageBase
{
[AccessedThroughProperty("Head1")]
private HtmlHead _Head1;
[AccessedThroughProperty("form1")]
private HtmlForm _form1;
[AccessedThroughProperty("ioScriptManager1")]
private ioScriptManager _ioScriptManager1;
[AccessedThroughProperty("updatePanel1")]
private ioUpdatePanel _updatePanel1;
[AccessedThroughProperty("btVerify")]
private Button _btVerify;
[AccessedThroughProperty("txthidIsLogin")]
private TextBox _txthidIsLogin;
[AccessedThroughProperty("btSetVisitBefore")]
private Button _btSetVisitBefore;
[AccessedThroughProperty("lblSerialNum")]
private TextBox _lblSerialNum;
[AccessedThroughProperty("ReConnect")]
......
private void Page_Load(object sender, EventArgs e)
{
this.Response.Expires = -1;
this.Response.Buffer = true;
this.Response.ExpiresAbsolute = DateTime.Now.AddSeconds(-1.0);
this.Response.Expires = 0;
this.Response.CacheControl = "no-cache";
}
protected void btVerify_Click(object sender, EventArgs e)
{
if (Operators.CompareString(this.lblSerialNum.Text.Trim(), "", false) == 0)
return;
iden.iden.HBCA hbca = new iden.iden.HBCA();
hbca.EmpID = checked ((int) Math.Round(Conversion.Val(this.Emp.EmpID)));
hbca.SubjectName = "HBCA";
hbca.Serial = this.lblSerialNum.Text;
switch (hbca.Verify())
{
case 0:
Page pgeParent1 = (Page) this;
pf.ShowMessage(ref pgeParent1, "这个证书没有分配给当前用户,认证无效!");
Page pgeParent2 = (Page) this;
pf.RunScript(ref pgeParent2, "retry()");
break;
case 1:
EmpCookie empCookie = new EmpCookie("ioLogin");
if (empCookie.GetCookie() != null)
{
empCookie.ItemAdd("Verified", "true");
empCookie.SaveCookie();
}
this.Session["VisitBefore"] = (object) "true";
this.Response.Redirect(ioSet.GetLoginCookieToUrl());
break;
}
}
最开始的一些变量定义,前端按钮btVerify
<form id="form1" runat="server">
<uc1:ioScriptManager ID="ioScriptManager1" runat="server" />
<ioctl:ioUpdatePanel ID="updatePanel1" UpdateMode="Conditional" runat="server">
<ContentTemplate>
<asp:Button ID="btVerify" runat="server" Style="display: none" />
<asp:TextBox ID="txthidIsLogin" runat ="server" style="display:none"></asp:TextBox>
<asp:Button ID="btSetVisitBefore" runat="server" Style="display: none" />
<table id="Table1" cellspacing="0" cellpadding="0" width="100%" align="center" border="0">
<tr>
<td height="100px">
</td>
</tr>
<tr>
<td class="td" valign="top" align="center">
<table id="Table5" cellspacing="0" cellpadding="0" border="0" style="width: 480px;
height: 220px">
<tr>
<td align="right"style="font-size:12px;">
请选择用户证书:</td>
<td>
<select name="CertID" style="width:150px">
<option value="">未取到用户证书</option>
</select>
</tr>
<tr>
<td align="right" style="font-size:12px;">
用户PIN码:
</td>
<td>
<input type="password" size="10" name="UserPIN" style="width:150px" onkeypress="if(event.keyCode==13) {doLogin();return false;}" />
</tr>
<tr>
<td align="center" colspan="2">
<%-- <img src="/ioffice/img/logo1.gif" border="0"/>--%></td>
<asp:TextBox ID="lblSerialNum" runat="server" Width="0px"></asp:TextBox>
</tr>
<tr>
<td valign="top" align="right" >
<img src="../img/ikeylogo.gif" border="0" /></td>
<td valign="middle" align="left" >
<img alt="" src="/ioffice/img/ProgressSmall.gif" /></td>
</tr>
<tr>
<td valign="top" align="center" colspan="2">
<a id="ReConnect" runat ="server" onclick="doLogin();" href="#">[登录]</a>
<a onclick="closewin()" href="#">[关闭窗口]</a></font> <a onclick="relogin()" href="#">[手动输入登录]</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
对应的后端的
protected void btVerify_Click(object sender, EventArgs e)
{
if (Operators.CompareString(this.lblSerialNum.Text.Trim(), "", false) == 0)
return;
iden.iden.HBCA hbca = new iden.iden.HBCA();
hbca.EmpID = checked ((int) Math.Round(Conversion.Val(this.Emp.EmpID)));
hbca.SubjectName = "HBCA";
hbca.Serial = this.lblSerialNum.Text;
switch (hbca.Verify())
{
跟进btVerify_Click看下
protected void btVerify_Click(object sender, EventArgs e)
{
if (Operators.CompareString(this.lblSerialNum.Text.Trim(), "", false) == 0)
return;
iden.iden.iKeyNetCA iKeyNetCa = new iden.iden.iKeyNetCA();
iKeyNetCa.EmpID = checked ((int) Math.Round(Conversion.Val(this.Emp.EmpID)));
iKeyNetCa.SubjectName = "NetCA";
iKeyNetCa.Serial = this.lblSerialNum.Text;
switch (iKeyNetCa.Verify())
{
case 0:
Page pgeParent = (Page) this;
pf.ShowMessage(ref pgeParent, "这个证书没有分配给当前用户,认证无效!");
break;
case 1:
EmpCookie empCookie = new EmpCookie("ioLogin");
if (empCookie.GetCookie() != null)
{
empCookie.ItemAdd("Verified", "true");
empCookie.SaveCookie();
}
this.Session["VisitBefore"] = (object) "true";
this.Response.Redirect(ioSet.GetLoginCookieToUrl());
break;
}
}
在判断lblSerialNum不为空后带入iden.iden.HBCA() 方法,跟进看下
public override int Verify()
{
if (Operators.CompareString(this.SubjectName, "", false) != 0)
this.LookupEmpAndLogin(this.Serial);
return Operators.ConditionalCompareObjectGreater(SqlData.ExecuteScalar(Globals.ConnectString, (CommandType) 1, $"{"select count(*) " + " from ssIdentity " + " where "} Serial='{this.Serial}'"), (object) 0, false) ? 1 : 0;
}
Serial即lblSerialNum又先被带入LookupEmpAndLogin 方法
protected void LookupEmpAndLogin(string SearchKey)
{
if (Operators.ConditionalCompareObjectEqual(HttpContext.Current.Session["VisitBefore"], (object) "", false) && Operators.CompareString(ioSet.GetClientSet("硬件认证直接登录"), "", false) != 0)
{
int iEmpID = this.LookupEmp(SearchKey);
if (iEmpID == 0)
return;
this.EmpID = this.LoginiOffice(iEmpID) != 0 ? 0 : iEmpID;
}
}
继续跟进LookupEmp 方法
protected virtual int LookupEmp(string SearchKey)
{
object objectValue = RuntimeHelpers.GetObjectValue(SqlData.ExecuteScalar(Globals.ConnectString, (CommandType) 1, $"select empid from ssIdentity where Serial='{SearchKey}'"));
return objectValue == DBNull.Value ? 0 : Conversions.ToInteger(objectValue);
}
ok,到这里,漏洞成因就非常明了了,从前端TextBox获取的lblSerialNum最终经过一系列赋值传递后被直接拼接进$"select empid from ssIdentity where Serial='{SearchKey}'" sql语句里,全程无过滤或者校验,从而造成了SQL注入漏洞。
漏洞复现
漏洞复现需要打开漏洞文件页面获取一些其他必要参数如__VIEWSTATE之类
POST /ioffice/Identity/HbcaUserLogin.aspx HTTP/1.1
Host: ioffice.mrxn.net
Content-Type: application/x-www-form-urlencoded
__EVENTTARGET=btVerify&__EVENTARGUMENT=&__VIEWSTATE=xxxxxx&__VIEWSTATEGENERATOR=xxxxx&btVerify=&lblSerialNum=SQLI_POC

成功利用报错注入在响应回显当前数据库用户信息

