ASP.NET 代码审计
2019-11-01 10:20:38 Author: xz.aliyun.com(查看原文) 阅读量:413 收藏

基础知识:

ASP.NET开发可以选用两种框架:ASP.NET CoreASP.NET Framework

ASP.NET开发也分为两种:

WebApplication:

WEB应用程序,改变代码后需要重启网页。具有namespace空间名称,项目中所有的程序代码文件,和独立的文件都被编译成为一个程序集,保存在bin文件夹中。

WebSite:

WEB网站,改变代码后不用重启网页。它没用到namespace空间名称,每个asp页面会转成一个dll。

ASP.NET比较关键的文件:

web.config:

1.web.config是基于XML的文件,可以保存到Web应用程序中的任何目录中,用来储存数据库连接字符、身份安全验证等。

2.加载方式:当前目录搜索 -> 上一级到根目录 -> %windir%/Microsoft.NET/Framework/v2.0.50727/CONFIG/web.config -> %windir%/Microsoft.NET/Framework/v2.0.50727/CONFIG/machine.config -> 都不存在返回null

Global.asax:

1. Global.asax提供全局可用的代码,从HttpApplication基类派生的类,响应的是应用程序级别会话级别事件,通常ASP.NET的全局过滤代码就是在这里面。

ASP.NET的常见拓展名:

%windir%\Microsoft.NET\Framework\v2.0.50727\CONFIG\web.config中有详细定义,这里提取部分简单介绍。

```
aspx:应用程序根目录或子目录,包含web控件与其他
cs:类文件
aspx.cs:web窗体后台程序代码文件
ascx:应用程序根目录或子目录,Web 用户控件文件。
asmx:应用程序根目录或子目录,该文件包含通过 SOAP 方式可用于其他 Web 应用程序的类和方法。
asax:应用程序根目录,通常是Global.asax
config:应用程序根目录或子目录,通常是web.config
ashx:应用程序根目录或子目录,该文件包含实现 IHttpHandler 接口以处理所有传入请求的代码。
soap:应用程序根目录或子目录。soap拓展文件

```

1.windows 2008R2

2.SSMS数据库管理

3.某系统

4.dnSpy反编译

熟悉框架

程序的文件目录

├─Admin
├─App_Data  //App_Data文件夹应该包含应用程序的本地数据存储
├─bin     // 包含应用程序所需的任何预生成的程序集
├─bootstrap
├─css
├─images
├─img
├─install
├─javascript
├─m
├─purchase
├─style
├─temp
├─Template
├─uploads
└─UserControl

WEB应用程序会把我们写的代码编译为DLL文件存放在Bin文件夹中,在ASPX文中基本就是一些控件名,所以需要反编译他的DLL来进行审计。

Logout.aspx

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

<html xmlns="http://www.w3.org/1999/xhtml" >
.
.
.
</html>

在文件头中有这几个参数:

1.Language="C#" //脚本语言

2.AutoEventWireup="true" //是否自动关联某些特殊事件

3.CodeBehind="Logout.aspx.cs" //指定包含与页关联的类的已编译文件的名称

4.Inherits="Book.Logout" //定义供页继承的代码隐藏类

我们所关注的也就是Inherits 的值,如上所示他指向了Bin目录下的purchase.dllBook类的Logout函数(注:purchase.dll是网站编译的项目名,一般与文件目录对应)

web.config

这个文件包含了目录权限控制、数据库密码等等

<location path="purchase/orderdetail.aspx">
    <system.web>
      <authorization>
        <allow users="*"/>
      </authorization>
    </system.web>
  </location>

  <authentication mode="Forms" />

比如我们使用的这套程序中authorization定义了purchase/orderdetail.aspx匿名可以访问,但是这套程序的本页面还写了一套验证

if (this.uid <= 0)
{
    if (!(base.Request.QueryString["g"] == "p"))
    {
        base.Response.Redirect("../login.aspx");
        return;
    }
    this.ph_pdf.Visible = false;
}

所以我们只需要访问purchase/orderdetail.aspx?g=p即可绕过跳转
,其中<authentication mode="Forms" />表示Form 表单认证。

在ASP.NET中全局过滤一般用到Global.asax至于他为什么可以起到全局过滤的作用可以看看ASP.NET三剑客。当然这套程序并没有全局过滤,在提交多个漏洞后,官网公告说这套程序建议内网部署,官网让公网部署的用户设置了身份验证:

<system.web> 
<authorization> 
<deny users= "?"/> 
</authorization> 
</system.web>

但是这套程序安装会默认插入多条用户数据。

审计注入

首先我们来看Login.aspx,前面已经贴过代码,我们需要反编译purchase.dll去找Book.Login,这里使用dyspy

login.aspx->Button1_Click->LoginForm()在login中控件名对应dll

public void LoginForm()
{
    int num = UsersHelper.Login(this.txt_username.Text, this.txt_pwd.Text);
    if (num > 0)
    {
        base.Response.Redirect(FormsAuthentication.GetRedirectUrl(num.ToString(), true));
    }
    else
    {
        Helper.Result(this, "用户名或者密码错误");
    }
}

跟进UsersHelper.Login

public static int Login(string username, string password)
{
    string sql = " select uid  from users_users where username=@username and password=@password;   ";
    SqlParameter[] prams = new SqlParameter[]
    {
        new SqlParameter("@username", username),
        new SqlParameter("@password", Helper.Encrypt(password))
    };
    object obj = Instance.ExeScalar(sql, prams);
    if (obj == null || obj == DBNull.Value)
    {
        return -1;
    }
    int num = int.Parse(obj.ToString());
    if (num > 0)
    {
        UsersHelper.Login(num);
    }
    return num;
}

这里使用的是参数化查询,所以这里不存在注入。登陆后的注入很多这里选一个。

search.aspx

这里剔除了部分无用代码,可以看到没有经过任何过滤,控件的值就拼接到text字符串,由Instance.ExeDataSet(text)执行,跟进ExeDataSet函数

没有过滤直接带入查询,如果你觉得从代码来看sql语句很麻烦,这里可以使用Sql Sever Profiler监控SQL语句。

Payload: 1%' and 1=user--

前面说到purchase/orderdetail.aspx?g=p可以绕过直接访问,具体原因可以移步《第二章:越权》,那么我们看看这个是否存在注入,如果存在那么将是一个前台注入。

看到69-88行,要执行命令需要this.isviewtrue ,在30-36行赋值只需要t=view即可
sid没有经过任何过滤,同时ExeDataSet函数也不存在过滤,即存在注入。

Payload: purchase/orderdetail.aspx?g=p&t=view&sid=1%20and%201=user--

ASP.NET安全认证

1.在web.config中有四种验证模式:

方式 描述
window IIS验证,在内联网环境中非常有用
Passport 微软集中式身份验证,一次登录便可访问所有成员站点,需要收费
Form 窗体验证,验证帐号/密码,Web编程最佳最流行的验证方式
None 表示ASP.NET自己根本不执行身份验证,完全依赖IIS身份验证

其中FORM窗体验证的流程图:

开启form窗体验证的同时还需要配置web.config,不然就会出现问题,一般来说还需要配置最基本的页面访问权限,比如禁止匿名用户访问。

<system.web>
    <authorization>
    <deny users="?"/>
    </authorization>
</system.web>

当然还可以设置一些管理页面允许某某用户访问等等,在这套程序中开启了form然后在程序里面验证的cookies,而且并没有设置所有页面的authorization

2.除去web.config的配置通常还有两种写法来验证是否登陆。

第一种:在每个页面判断Session["UserName"]是否等于null

第二种:类似php的include的继承,这也是本套程序使用的方法。

首先他定义了一个purchase.Master 母版页 在里面写上了权限验证的代码。

然后次母版页头文件会引入MasterPageFile="~/purchase/purchase.Master"调用之前都会先调用母版页的Page_Load函数来验证是否登陆。当然你也可能遇到没有使用母版页的程序,那么他可能是先定义一个onepage类继承page,然后其他页面继承onepage类,与此相同。

寻找越权

例1:

比如没有任何验证的,也没有继承验证类的,无需登陆访问

例2:

这套程序验证权限的地方比较少,只是简单的判断了是否登陆,登陆后基本可以访问大多数管理页面这里。

例3:

MyProfile.aspx文件中 直接获取表单数据进行update,并没有验证权限,导致低权限账号也可以update admin但是这里是参数化查询,所以不存在注入。修改admin的账号密码为1234567

例4:

前面说到这套程序里面验证的cookies,而且并没有设置所有页面的authorization权限,所以我们能不能伪造cookie呢。

23-26行判断this.uid的值来进行跳转,在16行定义了他的值,跟进UserHelper.GetUserId

public static int GetUserId
{
    get
    {
        if (Helper.IsUseAd && HttpContext.Current.Request.Cookies["userinfo"] == null)
        {
            UsersHelper.LoginAd(UserHelper.GetSamaccountName());
        }
        if (HttpContext.Current.Request.Cookies["userinfo"] != null)
        {
            return int.Parse(HttpContext.Current.Request.Cookies["userinfo"]["userid"]);
        }
        return -1;
    }
}

this.uid等于cookies中获取的userinfo的值,这一步可以伪造,接着我们看到30-33这里他设置了管理员的布尔值,跟进RoleHelper.IsAdmin

public static bool IsAdmin
{
    get
    {
        string name = "IsAdmin";
        string admin = RoleHelper.Admin;
        bool? flag = HttpContext.Current.Session[name] as bool?;
        if (flag == null)
        {
            flag = new bool?(UserHelper.IsInAnyRoles(admin));
            HttpContext.Current.Session[name] = flag;
        }
        return flag.Value;
    }
}

前面从session中获取,如果flagnull则从UserHelper.IsInAnyRoles(admin)获取。

跟进IsInAnyRoles

可以看到只要我们传入的cookiesroles的等于传入的数组值就返回true其中 public static string Admin = "administrators";,所以构造cookies:userinfo=userid=1&roles=administrators;

项目地址:https://github.com/aleenzz/.NET_study/tree/master/asp.net_bug
有问题还请大佬们指出QAQ


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