1.关于VirtualPathProvider
这其实是ASP.NET提供的一种特殊功能,中文名叫虚拟文件,顾名思义,这不是一个真实的文件,但是却能在WEB上访问到这个文件,一听这个性质其实就挺内存马的,来学习一波。那么要怎么利用它做一个内存马呢?首先要实现VirtualPathProvider这个抽象类
里面有挺多方法的,这里先学两个,分别是FileExists()、GetFile(),前者主要是用来配置虚拟文件对应的URI的命中规则,后者主要是配置这个虚拟文件的内容,其中GetFile()需要返回VirtualFile类型
跟进一下VirtualFile类
有一个Open()方法需要实现,注意到这个方法返回一个Stream,其实就对应我们虚拟文件的内容,因此可以重写Open()返回恶意代码。
从上面这些代码可以先想到两种实现VirtualFile内存马的方式。第一,既然FileExists()在虚拟文件的生命周期中会被自动触发,那么我们可以在其中实现恶意代码,我们还可以研究一下有没有其它类似的方法起到这样的效果。第二,我们可以通过GetFile()返回一个恶意的VirtualFile类,在这个类的Open()方法中返回恶意文件内容,也可以实现内存马。
2.实现恶意VirtualPathProvider类并注册
这里我们先来学习一下第二种,通过GetFile()方法返回虚拟文件内容的思路,参考yzddmr6师傅的代码,关键其实是这几段:
SamplePathProvider类是VirtualPathProvider的实现类,其中实现GetFile()方法。当判断一个路径是虚拟文件路径时,返回SampleVirtualFile类的对象(这是VirtualFile类的实现类)
SampleVirtualFile类里记得实现Open方法,返回文件内容:
这样就写好了一个恶意的虚拟文件类SamplePathProvider(),其构造方法接受虚拟文件路径和内容,触发虚拟路径后会运行到SampleVirtualFile类中的Open方法返回恶意代码,这样就相当于一个内存马了。然后怎么注册它呢?答案是调用
HostingEnvironment.RegisterVirtualPathProvider() //看名字就知道是用于注册的
这里还有个问题就是VirtualPathProvider继承了MarshalByRefObject,之前在学习.NET Remoting和ObjRef链的时候了解过这个类,里面有一个独特的租约机制,用于控制实例的生存期,需要对我们做好的恶意虚拟文件对象调用一下InitializeLifetimeService()方法,让内存马持久存活。
运行上述代码注入内存马后,访问/test.aspx地址:
用哥斯拉连接一下:
这就说明我们的虚拟文件内存马注入成功了。可以看到这种内存马还是非常方便的,我个人非常喜欢这种内存马,足够简单也足够可靠,可以把你想要的几乎任何aspx文件直接打成内存马,只需要改动base64编码的内容即可。
3.使用FileExists方法创造内存马
实际上也不一定完全要按照上面的那个流程去创造,前面提到过FileExists()用于配置虚拟文件URI的命中规则,等于说在虚拟文件生命周期中会被自动触发,像这样的方法都是可以在里面实现恶意代码令其成为内存马的,并且这些方法里拿到httpcontext对象也比较容易,获取请求和设置响应都比较方便。比如这里实现FileExists,在其中调用恶意代码,参考:
https://exp10it.io/2024/02/asp.net-%E5%86%85%E5%AD%98%E9%A9%AC/#getcachekeyfileexists
注入就还是老样子:
由于这个FileExists()的特性,当你访问一个存在or不存在的路径时都会触发它,所以效果类似于JAVA那边注入了一个uri规则为/*的Filter型内存马,随便访问一个路径,加上cmd参数执行命令即可
4.使用GetCacheKey方法创造内存马
除了FileExists,虚拟文件生命周期中还会触发GetCacheKey方法,也可以在这里实现恶意代码。并且这个方法优先于FileExists触发。不过得访问已存在的文件时才能触发GetCacheKey。
注入完后,访问一个不存在的路径就没法触发GetCacheKey()
但是,目标已有的一些路由规则,或者是目标真实存在的aspx之类的文件,都可以触发,比如我这里用已有的路由规则去触发:
再比如随便找一个可访问的ASP.NET相关文件,我这里创建一个空的aaa.aspx文件
依然可以成功访问到
5.绕过预编译限制
前面我们学习了注册虚拟文件的关键方法是
RegisterVirtualPathProvider()
而它又是靠
RegisterVirtualPathProviderInternal()
方法实现注册的
要走到
RegisterVirtualPathProviderInternal()
可以注意到有一个if()判断,只有
BuildManager.IsPrecompiledApp
值为false,才可以正常注册虚拟文件。
这个BuildManager.IsPrecompiledApp是什么呢?其实这个就是用来判断是否开启预编译的,如果开启了它的值就会变成true,虚拟文件就无法正常注册了。
解决方法也很简单,调用RegisterVirtualPathProvider()之前把它的值强行改成false即可。
6.绕过FriendlyUrls
这个技术可以把ASP.NET的Uri风格改为类似RestFul的风格,比如我有一个test.aspx文件,可以直接用test去访问。这个会影响基于VirtualFile(GetFile)的虚拟文件内存马。
题外话,不知为啥感觉ASP.NET的开发者们特别执于实现这种效果,除了FriendlyUrls,还有一个在web.config里可以开启的配置项ExtensionlessUrlHandler,也可以把URL去掉aspx变成类似RestFul风格(会造成一些鉴权绕过问题)
FriendlyUrls实现的方法其实也是往RouteTable.Routes里插入一个特殊的Routes规则,绕过FriendlyUrls的方法也是靠反射,先从RouteTable.Routes里拿到对应的对象,然后改掉其配置项里的AutoRedirectMode
7.参考
https://yzddmr6.com/posts/asp-net-memory-shell-virtualpath/#%E7%BB%95%E8%BF%87%E9%A2%84%E7%BC%96%E8%AF%91
https://exp10it.io/2024/02/asp.net-%E5%86%85%E5%AD%98%E9%A9%AC/#%E7%BB%95%E8%BF%87-friendlyurls