本次审计为初学.NET审计,代码也并不是很多,因此采用通读的方式审计,由于本身并不会开发,所以代码基本都是边读边查(不得不说AI是真香)。
.NET程序审计一般需要注意的文件如下:
- .dll文件:动态链接库文件,这些文件包含了程序运行时需要的一些函数和数据,在程序运行时被加载到内存中,供程序调用。反编译为.cs源代码文件审计。
- .cs文件:在ASP.NET中,.cs文件通常包含C#代码,这些代码定义了网页或Web应用程序的逻辑和功能。
- .ashx文件:ashx文件是ASP.NET中的HTTP处理程序文件。它们用于处理HTTP请求并生成HTTP响应。ashx文件不包含任何与HTML标记相关的代码,而是专注于处理请求和生成响应的逻辑。ashx处理程序通常用于创建自定义的Web服务、生成动态图像或处理其他类型的HTTP请求。
- .aspx文件:ASP.NET Web应用程序中用于定义Web页面结构和布局的文件,它们通过结合HTML、客户端脚本和服务器端代码来提供动态、交互式的Web体验。
- 配置文件和其他静态资源文件。
代码结构如下,bin文件夹中是一些dll文件,可以通过ILSpy反编译,但这里还是先从通过用于处理HTTP请求的ashx文件开始:
GET_GN.ashx、FUHESAOMA.ashx-未授权访问漏洞
直接阅读第一个ashx文件GET_GN.ashx,主要逻辑是先定义了UP_MINGXI 类并用IHttpHandler接口的ProcessRequest方法处理客户端的HTTP请求。处理流程在第19行-52行的try中。
第21行定义了变量s并将从http请求头中的数据赋值给s,代码注释竟然是获得通过HTTP协议协议头传过来的的json数据。说实话从请求头中传输json格式的数据貌似很少见。
22行,对变量s又使用了GetParamJson方法进行了进一步处理,再URL解码后赋值给变量objStr。从命名上看这个方法应该是处理json数据的方法,全局搜索一下GetParamJson方法。可以看到这个方法是在Utils.cs文件中被声明的。
那么找到Utils.cs文件查看这个方法是做什么用的。从代码上看逻辑也非常简单(注释也说明了是获取json数据),使用HttpUtility.ParseQueryString方法解析获取到请求头所有数据,并将其赋值给变量rs,然后通过一个循环来查询,请求头中是否有键为param的值,如果有则将其的值赋值给objStr,如果没有则objStr值为空。
从上面代码就能看出GetParamJson方法实际上就是从HTTP请求头里去查找有没有名为param的请求头,并获取对应的值。整个逻辑相当简单粗暴,没有任何其他处置方法。
继续返回ashx文件,第22行代码的作用就是获取param请求头后的json数据并对其进行URL解码后赋值给objStr,23行是将结果记录到日志,24-28行用来验证从objStr是否为空。在实际环境中测试验证了请求头获取的过程。
确定了这个页面会处理哪个请求头中的数据,下一步就需要确认请求头中会传递哪些数据由GET_GN.ashx处理。接着往下看,29行-50行是对param请求头的值进行处理的逻辑。
从第33行, 当objStr值不为空时,通过JsonConvert.DeserializeObject<dynamic>将其反序列化,34行从反序列化结果中获取名为"type"的属性值,并将其赋值给type变量,最后将type又放入了parameter数组中,也就是说,param请求头中的json数据中得有名为type的参数。
37-39行就到了数据库操作的过程,代码创建了一个名为sql的SqlRun.DBAccess类的实例,传递的参数是SqlRun.DBAccess类里的sqlstr,下一行实例sql调用了方法RunProDataSet,并传递了两个参数APP_WMS_GETData和parameter(type的值)。那就继续查看RunProDataSet方法是干什么的,全局搜索方法名,在SqlRun.cs文件中,也可以看到很多地方都调用了这个方法。
查看该方法,接受两个参数cmdText和数组SqlParameter[] commandParameters,从逻辑上看就是连接数据库,并将执行结果存储到ds中,但并没有直接的SQL语句,string cmdText可能就是SQL命令或者数据库操作的函数,而再回头看GET_GN.ashx文件调用RunProDataSet方法传递的参数APP_WMS_GETData和parameter。parameter的值就是通过http请求头param提交的{"type":"valu"},APP_WMS_GETData是应用程序自定义的一个SQL命令或者函数。
但是在源代码里,始终没有找到关于APP_WMS_GETData的dll文件或者其他定义。再回头看看实际环境中的响应,当param中的参数不是type时,返回错误信息时APP_WMS_GETData过程或函数需要参数type,也说明了APP_WMS_GETData是封装好的SQL语句,根据不同的参数来查询并返回结果。
遗憾的是并没有找到它预编译后的文件,实际上这里已经排除了SQL注入的风险了,唯一可控的参数就是type的值。但这并不意味着此处没有任何风险,毕竟param的值是可控的并在传入参数后执行了SQL语句,也就意味这任意用户都有权限来进行有限的SQL查询。
实际上,在前面就看到调用RunProDataSet方法的页面很多,其中在FUHESAOMA.ashx页面,从请求头中获取了两个参数值,type和var。
此处根据type的值和var的值即可查看到用户具体信息。(由于没有找到APP_WMS_GETData更多的信息,所以关于type这类的值具体是什么东西只能从日志文件看了。)
从逻辑上来讲,这里的漏洞并不是SQL注入,由于web程序对请求头中没有任何token验证而执行了SQL语句,本质还是未授权访访问漏洞。
KeyValueJson.ashx-RCE漏洞
继续审计第二个ashx页面KeyValueJson.ashx,第一步从HTTP请求头中获取数据的过程是一样的,都是GetParamJson方法获取请求头param的值。所以直接看对于获取到的值是怎么处理的代码即可。
第34行代码,定义了变量procName和parameter,而parameter的值是通过ParamsHelper.ReturnParams方法获取,该方法还输出了procName的值。随后用RunProcedureDR方法传递了这两个参数。
全局搜索ReturnParams方法,在ParamsHelper.cs文件中。
进入ParamsHelper.cs文件,查询了一下SqlParameter[]也是为了防止SQL注入,在这里也不用多去研究,直接看有通过请求头param会传递哪些json并如何处理。首先该方法接受一个参数jsonStr并输出一个参数procName。
21-23行代码是对json数据进行了处理,让其存储到paramList列表,24-40行代码的逻辑也非常的简单粗暴,首先遍历json数据中有无名称为proc的参数,有了之后跳出当前循环,对下一个参数名称前面加一个@,遍历完成后将结果作为数组返回。如果了解SQL server存储过程的话,就已经能看明白,这个页面就是根据存储过程及对应的参数进行数据库操作。在第一个页面的代码审计中已经知道了一个存储过程名称叫APP_WMS_GETData,具有如type,var等的参数,利用这些参数值就能实现未授权访问。
但是问题就出现这里了。在这里存储过程是用户可控的,除了程序自定义的存储过程APP_WMS_GETData外,别忘了SQL server还有一些存储过程是自带的,其中最著名的就是xp_cmdshell ,当然除此之外还有很多可以查询数据库内容和系统信息的存储过程,其参数也是公开的。
- sp_databases:列出服务器上的所有数据库。
- sp_helpdb:报告有关指定数据库或所有数据库的信息。
- sp_renamedb:更改数据库的名称。
- sp_tables:返回当前环境下可查询的对象的列表。
- sp_columns:返回某个表列的信息。
- sp_help:查看某个表的所有信息。
- sp_helpconstraint:查看某个表的约束。
- sp_helpindex:查看某个表的索引。
- sp_stored_procedures:列出当前环境中的所有存储过程。
- sp_password:添加或修改登录帐户的密码。
- xp_sendmail:用于向指定的收件人发送邮件,并可以附加查询结果集。
- xp_startmail:启动SQL邮件客户端会话。
- xp_cmdshell:此存储过程能以操作系统命令行解释器的方式执行给定的命令字符串,并以文本行方式返回任何输出。这是一个非常强大的工具,但也带来了安全风险,因为它允许执行任何操作系统命令。在某些SQL Server配置中,为了安全起见,xp_cmdshell可能默认是禁用的。
- xp_availablemedia:查看系统上可用的磁盘驱动器的空间信息。
- xp_dirtree:查看某个目录下所有子目录的结构。
- xp_enumdsn:查看系统上设定好的ODBC数据源。
- xp_enumgroups:查看系统上的组信息。
- xp_getfiledetails:获取某个文件的属性。
- xp_makecab:将目标多个文件压缩到某个目标档案之内。
- xp_ntsec_enumdomains:列出服务器的机器名及其所在的NT域的名称。
使用sp_databases存储过程,无参数可查询所有数据库。
使用xp_cmdshell存储过程,参数为command_string,该参数的值就是需要执行的系统命令,RCE漏洞到手。