一次完整的代码审计
发个去年的存稿
前言:因为本人一直活跃于教育 SRC,所以本次针对的系统也是各大高校所使用的 系统。系统名称: 某一卡通门户系统
正文: 0x01: NET 平台下的源码挖掘
个人非常喜欢针对于.NET 和 JAVA 平台所开发的程序做测试,相反,对 于 php 就是一窍不通了。。。。 在针对 NET 平台,因为大部分使用的都是 IIS 中间件,对大小写不敏感, 所以在生成字典的时候也会方便很多。 由于 NET 平台的特性,大部分程序的源代码都会打包成程序集,存储在根 目录下的 bin 目录。这导致了部分运维喜欢备份 bin 目录,且将备份过后的 文件存储于网站根目录下,若攻击者使用事先准备好的字典,就可以轻而易举 的获取到源代码。
示例: 使用御剑批量扫描资产
得到的数据还是挺可观的,下载过后的文件可以直接使用 dnSpy 进行逆向,查看 源代码
0x02:.NET 代码审计之- 文件上传
在某个站点下获取到一卡通的 bin 目录。使用 dnSpy 进行了逆向。
初步分析了两个文件,WebController 为主页的控制器。
ManagerController 为管理页的控制器 两个页面的路由规则如下: WebController: 控制器名/方法名 ManagerController: Manager/控制器名/方法名
一般的 Manager 控制器下面的都会有登录过滤,可以后面再看。 这里先看 WebController 下面的功能
有很多控制器,看命名方式,还是能确定不少功能的。
其实,在开始审计前,我都喜欢看一下 Filter 过滤器,里面的功能。 在过滤器中,有一个 SqlFitler
大概功能就是捕捉 get 和 post 请求里面的敏感参数。比如 单引号’
就会直接拦截。。。。,那么就没有必要去挖 SQL 了。虽然这里也可以绕过,但 是 SQL 注入太麻烦了。一般都喜欢放到最后再挖。
期 间 在 NoBaseController 控 制 器 下 面 发 现 了 一 处 文 件 上 传 操,upshallfile 方法中,定义了一处文件上传功能。具体展现在第 59 行,进行 了文件存储操作,期间并未进行任何文件类型效验操作。
流程分析:
方法中,第 5,6,7 行代码声明了三个字符类型的变量。该变量的值从 http 请求头的属性获取
text 变量获取请求头中的 path 属性,默认值为 “~/”。
text2 变量 获取请求头中的 sign 属性。
text3 变量获取请求头中的 time 属性。 第 10 行-17 行,判断 text2 是否为空。如果为空。返回签名失败
第 18 行--27 行。进行了一个效验操作
时间类型变量 d 的值为 text3 变量转换成时间的内容。
时间类型变量 d2 的值为 当前时间转换成 yyyyMMddHHmmssff 格式的内容 那么这里可以得知: text3 的内容是由请求头中的 time 属性决定。
time 属性 传递内容必须为时间且为 yyyyMMddHHmmssff 格式。
第 20 行: 如果 当前时间 减去 传递进来的时间 大于 10 则返回签名超时
这里只需要大于 10 就可以。那么可以直接定义传入时间为 2099 年。这样就一直 可以使用。
第 28-38 行,则是对文件效验码的操作。
29行中的 声明了一个strMd 变量 其 值为 进行md5加密后的内容。
要加密的内容如下:
file.FileName (文件名称) :service.asmx
text : 由http请求头中的path属性决定
text3: 由http请求头中的time属性决定
Synjones为进行md5加密所附带的salt。
将以上内容加密后。与 text2 变量进行对比。如果不相等。那么返回签名校验失败。
如果相等则进入59行的文件存储操作。
已知text2变量的内容由http请求头中的sign属性决定
那么只需要将定义好的内容进行加密然后赋值给sign。就可以绕过了。
这里可以直接把InterFaceMd5Helper.GetStrMd5 这个方法拖出来到本地调用进行加密操作。
其中
第59行调用text变量。
也就是说text为文件存储路径。
那么构造加解密方法:
得到Sign的值。
构造POC:
POST /NoBase/upshallfile HTTP/1.1
Content-Type: multipart/form-data; boundary="6e9cb0ae-23eb-49bf-92d6-16dcbb95bd8a"
time: 2099070800284040
sign: B041E90676E936521F2B967770314C56
path: ~/
0x03: 任意账户登录
在LoginController 控制器 下面的QrCodeLogin方法中,其功能为扫码登录。该功能最终效验不是传统的账号密码验证,而是账号加密过后的内容。当攻击者掌握加密规则后,可构造参数结构,导致任意账户登录。
流程分析:
QrCodeLogin方法下面的操作。要分为两个结构。
第一段结构:
第1行-44行: 账户效验。
第45行-84行: 将账户带入,请求终端查询。
这里要注意: 45行以后的操作就不再由程序接管了。具体可以看GetTsmCommon 方法.
所以这里只分析45行之前的操作。
第7行实例化了一个对象。这个对象是空的。无任何内容。后面是用来存储用户信息的。
第10行接收POST请求传递进来的参数 account 并将内容赋值给text变量
整个方法。只接收这一个参数。所以,只需要追踪哪里调用了account就可以了。
第11行-17行中进行了判空操作,如果account内容为空。则返回账号为空。
第19行-25行进行了解密操作。如果解密失败则返回账户解密失败。
可以看到 第18行
string text2 = DesEncryptHelper.Decrypt(text);
进行了解密操作。追踪这个方法。
DesEncryptHelper类中,包含了解密和加密的方法。所以后续可以直接拉出来调用。
第60行中可以看到。
调用了Decrypt 解密方法 并传递了一个Key 为SYNJONES 。
那么等会加密的时候。也需要带入这个Key。
回到QrCodeLogin方法
第26 行 - 33 行。
分别对解密后的内容进行了切片操作。
第26行: 以 “_” 为分隔符进行拆分。取第一个内容的值 赋值给text3
第30行: 以 “_” 为分隔符进行拆分。取第二个内容的值 赋值给 value
第34 行 :声明时间变量d , 值为 转换成时间格式的变量value 。
那么这里已知value是时间
第35行: 声明时间变量d2 值为当前时间
第36行: d2-d 当前时间减去传入时间。如果大于120,则返回超时。
若符合。则执行下面的操作。
第45行。将参数带入了Jsonrequest变量中。第53行进行了一次外部请求。这里我不需要过多深入。因为下面的操作已经无法人为控制。这里只需要知道,text3的内容是账号。
且传入进去的账号必须存在。
那么最终加密的格式为
账号_时间 时间为:"yyyy-MM-dd HH:mm:ss" 格式
将加解方法单独拖取出来调用。
构造加解密方法:
构造POC:
POST /Login/QrCodeLogin HTTP/1.1
Host: *******
account=B7D7D43C8166BCB4540FF2464842485E3383D6CA04041C7F
成功登录
tql;
大佬,你的网站被wappalyzer收录了
@hello >
大佬tql,膜
6666
海神
永远滴神大师傅 可以指导一下你是怎么学习.net 和java 的麽