.NET代码审计基础
首先,.net的站点在文件上传getshell时常见的绕过后缀包含:asp、aspx、ashx、soap、cer,在getshell后,网站源码一般存放在bin目录下,即网站在发布时将aspx文件进行编译后转换成的dll文件,dll可能会被混淆(如使用.NET Reactor进行混淆),可以利用工具进行反混淆,如de4dot。
1. 代码结构
在bin目录中可能存在大量dll文件,其中常见的可能有如下的一些dll
System.Web.*.dll 系列
例如:
System.Web.Mvc.dll(MVC 框架)
System.Web.Http.dll(Web API 相关)
System.Web.WebPages.dll(Web 页面框架)
这些 DLL 主要用于 ASP.NET Web 项目。
EntityFramework.SqlServer.dll&EntityFramework.dll
这些 DLL 主要负责数据库操作,常见于 ASP.NET 网站项目。
Microsoft.AspNet.Identity.*.dll 系列
这些 DLL 处理 ASP.NET 身份验证,通常用于 Web 登录系统。
然后网站源码dll一般全命名为大写字母,或者文件名带WebApp或Web等字样,或者带一些网站系统的名称特征等等(涉及到真实业务系统的名字*打码处理),如:
HYHT.IT**.WebApp.dll
MH.EC*.PHM_WEB.dll
MH.EC**.Backend.dll
Fu***********Plt.Business.Web.dll
然后便可以使用dnSpy此类工具直接反编译,dnSpy会自动将所有依赖的dll引入并反编译,避免代码出现错误。
同时,以同样前缀开头的可能还会出现一些这样的dll:
HYHT.IT**.Data.dll
HYHT.IT**.Domain.dll
HYHT.IT**.Entity.dll
HYHT.IT**.Enum.dll
HYHT.IT**.Message.dll
MH.N**S.Common.dll
Fu***********Plt.Business.Infrastructure.dll
Fu***********Plt.Business.Domain.dll
类似Java,刚刚的Web.dll中的源码基本为业务层的代码,也就是Controller等等,然后这些就是一些实体、枚举类、业务逻辑层、数据库操作层等等的代码。
2. 反编译
因此我们如果拿到shell后想“备份”一下源码,往往只需要网站的业务dll即可,这部分文件通常大小只有几百kb大小,其他杂七杂八的dll则不需要,其类似其他语言中的公共包,在程序运行时需要时再下载即可。
在有网站主要的dll源码文件后,使用dnspy反编译如下:
此时,点击主要的web业务dll会看到,里面一般都会有一个xxx.xxx.Web,下拉后可以看到Startup类,承担启动任务
然后可以点击引用下拉按钮,其中就是所有引用到的dll
如上图所示,当前Web.dll则主要引用到了Domain.dll和Infrastructure.dll,也就是当前的web.dll中的Controller会在接收到用户请求后再分别去到接口层和主要实现层去完成对应的业务过程,那么就可以参考这里去看看自己主要还缺失了什么dll,缺了就去“备份”回来。
3. 代码审计
然后就可以开始看控制器层的代码,也就是Controller了
点开某个Controller,比如FileInfoController,如下:
可以看到在代码头部存在一个路由注解:[Route("api/[controller]/[action]")],这是一个路由模板,[ApiController] 标明这是一个API控制器,通过Chat GPT解释,我觉得其中比较重要一点的:
自动请求验证 (Model Validation):
如果请求的数据不符合模型要求 (例如 Required 的请求参数或内容缺失),ASP.NET Core 会自动返回 400 Bad Request,而不需要手动检查 ModelState.IsValid。
简化参数绑定:
允许 API 直接从请求体 ([FromBody])、URL 查询参数 ([FromQuery])、路由 ([FromRoute]) 解析参数,而不需要显式声明。
自动推断路由URI:
如果 Controller 上没有 [Route],ASP.NET Core 会自动推导出 默认的路由前缀
关于自动推断路由,比如UsersController,那么可以直接访问/users,如果写了[Route("api/[controller]")],或者[Route("api/[controller]/[action]")]这些占位符,那么也是可以自动推断路由,比如这里,接着看代码:
这里则会自动推断uri为:
/api/FileInfo/UploadFileOne
也就是说自动推断路由会自动的去掉类名中的Controller、Async等,取前面的类名为uri路径
接着可以看到在其方法内部有很多<>4__this字样,这是由于编译器会将异步方法转换成状态机代码,当编译器看到 async/await 关键字时,会生成在代码中看到的状态机代码。这些生成的代码包含了<>t__builder、<>4__this 等看起来奇怪的变量名,所以目前我们只能通过自己的判断去分析其具体的调用。
不过,使用dotPeek来反编译,可以看到完整的源代码,可以看到UploadFileOneAsync和UploadFileAsync方法的完整代码都分别反编译出来了:
在代码中,还可以看到fileService的接口,其为UploadFileAsync中的文件上传的具体实现逻辑
点击IUploadFileService,可以看到这个接口就是在FullLifecyclePlt.Business.Domain.dll中定义的,跳转到接口定义的地方:
按住ctrl查找用法
可以看到在FullLifecyclePlt.Business.Infrastructure.dll中找到了其具体的实现
回到Controller中继续看代码,UploadFileAsync和UploadFileOneAsync方法都主要在这两个地方分别进行了获取文件名后缀和上传的操作
跟进GetFileExtensionFromContentType,可以看到文件后缀名的检测存在白名单,先获取form表单中文件的contentType,再根据contentType获取对应的白名单后缀,如果匹配的话则返回contentType对应的文件后缀,如果不匹配的话则返回空
但是这里存在一个问题,其仅仅是返回了contentType对应的后缀,而并没有拿他和用户上传的后缀进行比对,因此只要Content-Type伪造对了就可以了……,这里依然是任意文件格式都可以上传,再然后UploadFileAsync和UploadFileOneAsync会分别进行文件写入的过程,前者是调用UploadFileSerive,后者则直接进行了写入。
poc:
4. 小结
过程没有复杂的绕过等等,主要在于对.NET源码审计的一次基础学习,了解怎么对.NET的源码进行审计,也能偶尔在需要的场景下能够有个基本的白盒测试的能力。
监制丨船长、铁子
策划丨Cupid
美工丨molin
原文始发于微信公众号(千寻安服):.NET代码审计基础
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论