如果觉得该文章有帮助的,麻烦师傅们可以搜索下微信公众号:良月安全。点个关注,感谢师傅们的支持。
这两天比较闲,没有什么项目。于是看看了之前获取到的一个系统的源码,结果一看是.net。我哪会什么.net代码审计啊,本着来都来了的原则,还是打算看看。
工具准备
ILSpy:[icsharpcode/ILSpy: .NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform! (github.com)](https://github.com/icsharpcode/ILSpy)
ILSpy主要用于反编译dll动态链接库。默认安装就好,安装完成后如果没有安装.net框架,工具会给出连接下载默认安装即可。安装完成后打开如下。
正式审计
首先看下目录。
是webform架构,那他的路由就是在任意目录下的asxm、aspx、ashx等文件中调用bin目录下的动态链接库。先看下这个系统的一个历史漏洞。
历史漏洞追踪
看的出来时调用了SalaryAccounting
类,我们通过ILSpy反编译bin目录下的KPMIIS.Web.dll
,然后进去找到对应的类。
从pyload中可以看出是调用Calculate
方法和staffId
参数。直接源码贴下
[WebMethod]
public string Calculate(string SalaryCategory, string StaffBirthDay, string staffId, string Department, string SubOrg, string taxMonthly)
}
string message = AccountProcess(SalaryCategory, StaffBirthDay, staffId.TrimEnd(new char[1] { ',' }), strOrganizationCode, SubOrg, taxMonthly);
sbMessage.Append(message);
}
catch (Exception ex)
protected string AccountProcess(string SalaryCategory, string StaffBirthDay, string staffId, string Department, string SubOrg, string taxMonthly)
{
try
{
StringBuilder sqlStr = new StringBuilder();
sqlStr.Append("SELECT HR_SalaryFilesHistory.SFH_STAFF_ID FROM dbo.HR_SalaryFilesHistory WHERE HR_SalaryFilesHistory.SFH_SA_ID != 0 ");
if (!string.IsNullOrEmpty(SalaryCategory))
{
sqlStr.Append($" AND SCO_CATEGORY_ID={SalaryCategory} ");
}
if (!string.IsNullOrEmpty(StaffBirthDay))
{
sqlStr.Append(string.Format(" AND YEAR(SFH_SALARY_YEARS)=YEAR('{0}') AND MONTH(SFH_SALARY_YEARS)=MONTH('{0}') ", StaffBirthDay + "-01"));
}
if (!string.IsNullOrEmpty(staffId))
{
sqlStr.Append($" AND SFH_STAFF_ID IN (SELECT tempString FROM F_SplitNew('{staffId}',','))");
}
if (!string.IsNullOrEmpty(Department))
{
sqlStr.Append($" AND SFH_STAFF_DEPARTMENT_ID IN (SELECT tempString FROM F_SplitNew('{Department}',',')) ");
}
if (!string.IsNullOrEmpty(SubOrg))
{
sqlStr.Append($" AND SUB_ORG_ID = {SubOrg} ");
}
DataSet ds = Gateway.Default.FromCustomSql(sqlStr.ToString()).ToDataSet();
StringBuilder staffIdFlow = new StringBuilder();
if (ds.Tables.Count > 0)
{
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
if (i == ds.Tables[0].Rows.Count - 1)
{
staffIdFlow.Append(ds.Tables[0].Rows[i][0]);
}
else
{
staffIdFlow.Append(string.Concat(ds.Tables[0].Rows[i][0], ","));
}
}
}
_bllSalaryFilesHistory.Delete(Convert.ToDateTime(StaffBirthDay), Convert.ToInt32(SalaryCategory), staffId, Department, SubOrg);
ReshuffledAccounting(SalaryCategory, StaffBirthDay, staffId, Department, SubOrg, staffIdFlow.ToString());
_bllSalaryFilesHistory.Insert(Convert.ToDateTime(StaffBirthDay), Convert.ToInt32(SalaryCategory), 1, staffId, Department, SubOrg, taxMonthly);
DataTable dtHistory = _bllHrVSalaryFilesHistoryList.GetDataTable(HR_V_SalaryFilesHistoryList._.SCO_CATEGORY_ID == SalaryCategory && HR_V_SalaryFilesHistoryList._.SFH_SALARY_YEAR == DateTime.Parse(StaffBirthDay).Year && HR_V_SalaryFilesHistoryList._.SFH_SALARY_MONTH == DateTime.Parse(StaffBirthDay).Month);
StringBuilder sbWhere = new StringBuilder();
if (!string.IsNullOrEmpty(SalaryCategory))
{
sbWhere.Append($" and T1.SCO_CATEGORY_ID = {SalaryCategory}");
}
if (!string.IsNullOrEmpty(staffId))
{
sbWhere.Append($" and SFH_STAFF_ID in ({staffId})");
}
if (!string.IsNullOrEmpty(StaffBirthDay))
{
sbWhere.Append(string.Format(" and T1.SFH_SALARY_YEARS = '{0}'", new DateTime(Convert.ToDateTime(StaffBirthDay).Year, Convert.ToDateTime(StaffBirthDay).Month, 1).ToString("yyyy-MM-dd")));
}
DataTable dtSalaryHistoryTotalTable = Gateway.Default.FromStoredProcedure("HR_P_SalaryHistoryTotalTable").AddInputParameter("@where", DbType.String, sbWhere.ToString()).ToDataSet()
.Tables[0];
WhereClip wcMonthly = new WhereClip() && new WhereClip(" 1 = 1 ", null, null, null);
string wcMonthDate = "";
if (!string.IsNullOrEmpty(StaffBirthDay))
{
wcMonthly = wcMonthly && new WhereClip($" ham.MONTHLY_DATE = '{StaffBirthDay}' ", null, null, null);
wcMonthDate = StaffBirthDay;
}
if (!string.IsNullOrEmpty(staffId))
{
wcMonthly = wcMonthly && new WhereClip($" A.STAFF_ID in ({staffId}) ", null, null, null);
}
if (!string.IsNullOrEmpty(Department))
{
string strOrganizationCode = "";
strOrganizationCode = new BLLCOMMON_Organization().GetIDsOrganizations(Department);
if (strOrganizationCode != "")
{
wcMonthly = wcMonthly && new WhereClip(string.Format(" A.ORGANIZATION_ID in ({0}) ", string.IsNullOrEmpty(strOrganizationCode) ? "-1" : strOrganizationCode), null, null, null);
}
}
if (!string.IsNullOrEmpty(SubOrg))
{
wcMonthly = wcMonthly && new WhereClip(string.Format(" E.SUB_ORG_ID in ({0}) ", string.IsNullOrEmpty(SubOrg) ? "-1" : SubOrg), null, null, null);
}
DataTable dtMonthly = _bllHrAttendanceMonthly.GetTable(wcMonthly, new WhereClip($" FLOW_STATE = {3} ", null, null, null), wcMonthDate);
可以看到是通过字符拼接的方式直接拼接参数,进入FromCustomSql
执行sql语句,从而导致sql注入。这样的话同理的其他一些参数也可以sql。
sql注入挖掘
那我们知道了这里的sql的执行流程,我们直接全局搜索FromCustomSql
,查看是否其他sql语句参数可控且无过滤。因为ILSpy的全局搜索不是很好用,所以这边保存代码导出用vscode打开查看搜索。可以看到有很多。
这里随便找一两个接口测试一下就结束了。
文件上传挖掘
依旧是先搜索关键字。
首先就可以看到存在fckeditor
编辑器,编辑器上传这边就不多说了,继续查看其他接口。
后面有陆续找到一些图片、附件上传的接口,发现都存在过滤。
最后发现有一处上传未过滤,具体信息就不透露了。因为本人能力不足实在太菜,只会照葫芦画瓢,大概只能到这种程度了。