分享一套.net系统代码的审计思路
先关注下web.config和global.asax,确认为.net mvc架构
拖进dnSpy反编译,发现代码被混淆了
使用de4dot反混淆拿到源码
由于是mvc模式,重点关注controller层,派生出一个基类控制器BaseController和其余另外三个控制器
发现BaseController需要鉴权,暂且搁置
转向关注存在潜在未授权漏洞的Login控制器,找到后端的登录校验方法GetLogin()
先简单过滤下输入,随后查询对应账套信息,如果账套信息都判断正确,代码第55行调用DAL层接口判断用户信息,如果结果集count大于0证明用户信息也正确
走到这里,代码逻辑看上去都还没什么问题,直到代码第62行。
系统使用了Forms Authentication作为鉴权模块,当客户端尝试未授权访问后台页面时,系统会将用户重定向到/Login/V_Login页面进行身份认证,若用户输入正确的账号密码通过了验证,系统会将认证凭据FormsAuthenticationTicket加密保存到客户端cookie中,随后客户端会自动携带认证cookie访问之前想要访问的资源。
跟进到代码第62行的FormsAuthen()方法
将传入的参数使用$符号拼接,在最后加上一个随机生成的guid即构成了认证cookie的原文ticket,最后使用FormsAuthentication.Encrypt()方法加密上述ticket生成认证cookie,最后代码第213行将这个cookie添加到响应包中,确认是用于生成认证cookie的方法,然而再看一下是从哪里调用的
当前类,也就是LoginController,而且是public修饰符,也就是说是Login控制器上的一个公共方法,又由于Login控制器是一个可以匿名访问的控制器,导致我们可以直接外部调用这个方法来生成认证cookie
翻翻MSDN文档确认一下
在控制器上,公共方法,没有[NoAction]注解,三个条件都满足了,并且FormsAuthen()方法的参数都是string类型,肯定是能通过模型绑定验证的
先验证一下能否外部调用,成功了,返回cookie值为空,因为还没有传入参数
改为POST请求,传入伪造的参数
成功返回了cookie,然后我们拿一个后台接口进行验证
数据库打开失败?之前登录抓包都没有报错,怎么现在开始报错了?这个"test"数据库似乎是我们之前伪造的一个参数名,难道后台控制器校验了这些参数?
回到后台控制器代码,发现会从基类控制器获取用户名和数据库名等信息进行查询
基类控制器会从凭据中获取这些信息保存到控制器成员变量,方便其子类控制器去使用
所以如果我们构造的参数是胡编的话,即使成功伪造了凭据,也是不能正常使用的
这就不得不分析下FormsAuthen()方法每个参数的含义了,通过结合源码对应的数据库,总结出一个表格
参数 | 含义 |
---|---|
id | 登录用户的编号id,超级用户为00001或00000 |
name | 登录用户的用户名,前端传入时需要url编码一下 |
editionName | 系统版本名 |
dBName | 所连接的数据库名 |
ztcode | 账套code |
pwd | 用户密码,后端未做校验,所以可以任意伪造,如123456 |
LimitGroupID | 菜单权限id,一般为空 |
LimitAutoSync | 未知参数,一般为空 |
systype | 系统类型,一般为1 |
但是仍有很多参数的值未知,比如数据库名、账套code等,如何获取它们又成了一个难题
运气不错,通过全局搜索参数名在Login控制器发现一个方法可以未授权获取
burp构造请求即可获取
最后,构造请求,将获取到的参数值填入后发包即可成功获取到认证cookie
浏览器插件改cookie,访问后台首页,成功进入,所有功能正常使用