作为一个代码审计初学者,自上次对某.NET仓库管理系统的服务端的部分代码审计出未授权访问和RCE漏洞后,便一直想看看剩余代码念念不忘。总算是在出差完有时间看完了剩余的代码,但由于是服务端的代码,web页面的功能点全是查询数据库的操作,因此也只是审计出了和之前类似的RCE和一个SQL注入点。本文既是对剩余代码审计的过程记录,也是在初学代码审计的角度去做一些经验分享和总结。
在此之前我也一直在思索,对于一个没有开发经验的安服仔来说,如何利用有限学好代码审计呢?我尝试过通过深入学习去熟练掌握一门开发语言后开始审计,但由于工作时间问题,学习过程经常性中断。况且现在很多开发用的是很成熟的框架,学完代码还需要学习框架,本身并无多少耐心的我尝试几次后便放弃了;另外我也尝试过学习零基础代码审计的课程,但效果并不佳,很难将课程的内容真正消化并转化为自己的知识。但是随着AI的出现,很多的问题它能比传统搜索引擎更加快速且准确的回答我,由于其回答的正确率并不是百分百后,我与其的对话更像是一种探讨,于是便又了这次简单的代码审计的过程记录。
如果你和我一样也是代码审计的初学者的话,我建议审计方式还是通读代码,先通过不断的阅读代码让自己积累更多的经验和技巧。好了,话不多说,咱们书接上文,继续开始对剩余代码的审计!
传送门-某.NET仓库管理系统——WMS_APP服务端代码审计一
1、同样的RCE漏洞
上次通过对KeyValueJson.ashx页面的审计,发现了由于SQL server存储过程函数和参数值用户可控,以及通篇存在的未授权访问漏洞,从而造成了RCE。今天继续从下一个文件MoreOrderHandler.ashx开始。
首先在22行-24行,这里的代码和之前的文件开始有了不同,http请求数据不再从请求头中获取,而是改成了使用context.Request.QueryString从url中直接获取参数和对应的值。作者将原来从请求头中获取数据的代码注释掉了,可能开发人员也觉得从请求头中提交来的json格式文件,日志记录时数据并不好处理。
接着往下看26行-30行,是一个简单的条件语句,前面从url中获取到的值会赋值给集合KeyValue,这段代码会判断KeyValue值是否为空,也就是说请求的URL后必须得跟响应的参数,否则会返回参数为空。
从33行-57行,开始了对用户通过url提交的数据进行了逻辑处理,首先在前面定义了几个变量和数组,然后进入了逻辑判断:
第39行,通过foreach对KeyValue的键(也就是http请求通过url提交的参数)进行遍历,如果获取到的简直小写格式等于“proc",那么将其键值赋值给变量CCGC,其他的键值在47行进行了日志记录,而在48行则是在键名的名称前加上@并和值一起赋值给数组parameter。随后在57行,由sql.RunProDataSet()方法对CCGC和parameter进行处理。
看到这一步是不是很眼熟呢?在上篇文章中对KeyValueJson.ashx页面审计时,存储过程和对应的参数就是通过这样的逻辑判断被传递到RunProcedureDR()方法中去,只不过获取方式是通过请求头中的json格式数据,而在这个页面中换成了从url中的参数中获取。
那么继续看看RunProcedureDR()方法,是不是同样也能造成RCE。通过全局搜索,该方法也是在SqlRun.cs文件中。该方法接受两个参数,在注释中开发人员已经写的明明白白的了,字符串参数为存储过程名,数组参数为存储过程参数。该逻辑和KeyValueJson.ashx如出一辙,既然存储过程和参数都是通过url提交的,自然是可控的。
在实际环境中,使用存储过程xp_cmdshell尝试能否利用RCE漏洞,返回信息是列spid不属于表Table。说明了SQL语句确实是执行了的,但却没有返回相应的信息。
接着往下看代码,从第58行开始,代码又将RunProcedureDR()方法返回的数据进行了又一次的逻辑判断,目的是为了从返回的DataSet数据中去匹配字段spid,jwh,piaohao等内容,如果返回的数据中没有这些表字段,那么返回错误信息。
但实际上呢,在匹配之前SQL语句已经是执行了,只不过无法显示到返回内容中去,可以利用dnslog去尝试,同样造成了无回显的RCE漏洞!
这段代码的逻辑让人非常疑惑,开发人员的初衷显然是为了查询指定表(包含有spid,jwh,piaohao等字段的表)中的内容而设计的,且又没有将SQL语句写入代码,而是预编译在了特定的存储过程中,那么为什们在这里存储过程又是通过URL来传递呢?这样的写法除了造成RCE漏洞外似乎也没其他好处。
当我还在试图捋顺开发人员的代码逻辑时,下一个ashx文件SelectAndReturnJson.ashx就让我释然了,这里的数据处理逻辑除了没有对SQL语句的查询结果进行匹配外,处理过程可以说是一模一样,只不过这里用了 sql.RunProDataSet()方法,可这个方法还是根据存储过程名称和相应的参数来进行SQL语句操作的。
所以这里同样的造成了RCE漏洞并且是有回显的。
看到这儿已经能明白这个程序的开发人员的思路了,首先会按照功能点将所有的SQL查询语句提前编译为存储过程(可以有效避免SQL注入),然后根据不同的查询功能以存储过程和对应的参数为各方法的参数进行传递。而这些方法也大同小异,都是根据传递来的存储过程和对应参数进行SQL查询操作,只不过有的是分页返回,有的是全部返回,有的只返回一行。
那么按照这个思路,可以查询到其他页面SelectObjectReturnJson.ashx,UpdateDeleteReturnJson.ashx中,存储过程都是用户可以控制的,自然而然也造成了这两处的RCE,但是其他的很多页面,存储过程都是写死的。如果所有的存储过程都能像其他代码中那样不是由用户通过http请求传递,那能够由用户控制的参数也就是存储对象的参数值,至少不会被通过利用SQL sever自带的一些存储过程来实现RCE。
2、SQL注入漏洞
审计完上面的RCE漏洞后,基本上在ashx中能用到的一些方法函数基本了解的差不多, 所以后面的代码阅读速度了快了很多,基本上都是同样的根据存储对象和参数进行的查询操作。直到GetZdyData.ashx文件时,终于让我眼睛一亮。
http请求的数据还是通过url获取的,但是在处理获取到数据的这一步时,罕见的没有使用存储过程,而是直接使用了SQL语句。
在32-49行代码中,是为了提取url提交的参数中是否为showid字段,其余参数都在前面加@后赋值给parameter数组,这里的判断逻辑虽然和前面都是一样的,但这里的showid并不是存储过程,而是被传递到方法
RunTSqlScalar并带入了SQL查询语句并将查询结果再次赋值给变量sqlStr,这个变量再和parameter传递给方法RunSQLDR。也就是说这个处理过程中进行了两次SQL查询。
这种直接将SQL语句硬编码传递给变量sqlStr的方式,如果这段代码中如果没有对showid的值在传递之前进行验证过滤操作的话,再其他任何地方有过滤函数都已经没用了,这个参数会直接被RunTSqlScalar方法调用并执行,可惜的是这段代码中没有对showid这个参数值进行其他任何过滤操作,显然SQL注入是没跑了。
找到RunTSqlScalar方法查看,这个方法是获取SQL字段第一行第一字段的数值的查询操作,注释里强调了两次不要使用select,但在ashx代码中依然还是用了select。。。。。。
在实际环境中进行验证,这里需要注意的是,由于对http提交的数据进行逻辑判断时沿用了和存储过程查询一样的判断方法,因此通过url提交的参数除了有showid时,还需要随便构造一个任意参数,否则会报错。
总结
通过对本服务端程序的代码通读审计,很明显的能看到不管是未授权访问、RCE还是SQL注入漏洞,其产生原因都是开发人员对于用户可控的输入没有做任何限制和验证。虽然绝大多数SQL语句被编译成了存储过程从而避免了SQL注入(只有一处将带参数的SQL语句硬编码赋值给了变量,也不明白坚持了九十九步的程序猿大哥为什么再最后一步翻船了),但却忽略了SQL server数据库的特性,没有对这些自带的存储过程进行限制从而造成了更大的漏洞。
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)