4.2 GET型SQL注入防御脚本绕过案例剖析
在公司企业门户群体当中,使用CMS程序的用户不少,信息安全隐患也随着用户的数量而变化。本节将结合实际案例进行源代码剖析,以常见的GET类型注入展开分析。
4.2.1 复现条件
环境:Windows 7+phpStudy 2018+PHP 5.4.45+Apache。
程序框架:damicms 2014。
条件:需要登录。
特点:属于GET型注入,且存在防御脚本、防御方法。
4.2.2 复现漏洞
先注册一个账号:账号名为test0,密码为test0。
登录进行场景复现,访问链接http://localhost/test0/index.php?s=/api/ajax_arclist/model/article/field/username,userpwd from dami_member%23(注意:要写成%23而不写成#,因为进行get请求时,可控参数进入服务器端时会自动进行一次urldecode解码,如图4-1所示)。
图4-1 系统所产生的报错带有账号和密码
从图4-1中可以看到账号、密码已经显示出来,证明漏洞存在。
接下来进行漏洞利用、代码剖析。在剖析之前,先要了解一下此程序的URL路由,便于URL链接构造。
4.2.3 URL链接构造
首先进入个人资料处,可以看到注册、登录、个人资料的链接,如下所示。
●注册:http://localhost/test0/index.php?s=/Member/register.html。
●登录:http://localhost/test0/index.php?s=/Member/login.html。
●个人资料:http://localhost/test0/index.php?s=/Member/main.html。
通过以上三个链接可以得知,目标程序为单一入口文件访问模式,URL路由的映射采用的写法是“网站域名/index.php?s=/控制器名/方法名.html”,在这里看到html后缀,基本可以判断目标程序采用的是伪静态规则,但是也不绝对,可以尝试去掉后缀,查看链接是否可用,来验证是否为伪静态规则。
猜测带参数的URL链接构成如下所示。
网站域名/index.php?s=/控制器/方法/参数名/参数值.html
或
网站域名/index.php?s=/控制器/方法/参数名/参数值
现在我们已经大概知道了链接是如何构造的。
接下来查看目录结构,如图4-2所示,发现目录命名结构有点像使用了ThinkPHP框架。打开/Core/Core.php,查看注释,得知是ThinkPHP框架。
提示:在拿到一款CMS源代码之后,如果你知道该源代码采用的开发框架名称或版本,可公开搜索该框架的使用手册,直接查看手册如何设置URL路由映射相关知识点。
假设不知目标程序用的是什么框架,也没有对应的开发手册进行分析,也可以猜测到哪个目录有什么作用功能。
●Admin:后台功能目录、后台的相关类方法、配置文件基本都在此。
●Core:核心目录。
●install:安装程序的目录。
●Public:一般css、js文件、图片文件、编辑器插件、字体等,都会放在这里面公用。
●Trade:查看该目录下的文件名,查看文件里的注释,得知这是接口文件目录。
●Web:前台程序功能目录。
●Runtime:缓存目录,一般缓存的内容都会生成在这里面。
●伪静态文件:对程序做伪静态的配置。
图4-2 CMS系统源码的目录结构
前台的控制器就在目录/Web/Lib/Action/控制器名+Action.class.php中。后台控制器同上,只是将Web改成了Admin,也就是/Admin/Lib/Action/控制器名+Action.class.php,这样就可以通过构造URL找到路由映射的代码位置。
下面根据漏洞复现的场景链接寻找出现漏洞的代码进行复现剖析。
4.2.4 漏洞利用代码剖析
代码的剖析最终要进行不断的验证与分析,下面的分析会更详细。
1.查看入口文件是否引入了防御脚本
先看一下网站源码index.php,如图4-3所示,看到第13行引入了php_safe.php。打开此脚本,查看php_safe.php内的代码,如图4-4所示。第2行是//Code By Safe3,推测是360的防御脚本或者改造了的防御脚本。从第24~33行可以看到,不管是GET、POST还是cookie方式传送的参数都要进行过滤。
图4-3 index.php源代码
图4-4 防御脚本的代码
2.通过漏洞连接定位漏洞位置
通过以上分析,根据漏洞链接可以定位到漏洞位置在\Web\Lib\Action\ApiAction.class.php的ajax_arclist方法中,如图4-5所示。
提示:一般情况下,我们把自己写的类里面的方法叫作方法,PHP自带的方法叫作函数。
图4-5 ajax_arclist方法
3.分析漏洞源码
可以看到在ajax_arclist方法的入口处,有很多$_REQUEST来接收参数(_REQUEST[ ]具有$_POST[ ] $_GET[ ]的功能,但是$_REQUEST[ ]执行得比较慢。通过post和get方法提交的所有数据都可以用$_REQUEST获得),不要因此而迷失了双眼,先找到有SQL语句的代码,在第60行、第64行、第70行。
以上三个SQL语句中,可控的参数$where 、$order、 $num、$field分别在第46~48行和第54行被带入inject_check方法。使用phpStorm编辑器,按住Ctrl键,将光标移动至inject_check上点击鼠标左键,选择/Core/Common/functions.php,定位到第988行的inject_check方法,如图4-6所示。
从图4-6中看到eregi方法被划掉了,这说明该方法被弃用,PHP 5.3x不再支持eregi。这里可以选择忽略,也可以选择不忽略。为什么可以这样选择呢?因为之前在查看网站源码index.php的时候知道本程序调用了防御脚本,GET方式传参的值都会被检测。另外,这也与你运行该程序时用的PHP版本相关。
图4-6 inject_check方法
回到上述三个SQL语句,可以看到可控参数被分别带入了where()、order()、field()、limit()方法中。由于本程序使用的是ThinkPHP框架,因此这四种方法的使用可查看ThinkPHP的手册。由于ThinkPHP版本很多,最好先打印一下ThinkPHP的版本,在ajax_arclist方法开始处输入如下代码:
在显示的结果中可以看到版本号为2.1。
提示:这里也可以用断点调试,可根据使用习惯而定。
然后去查看ThinkPHP 2.1版的手册。因为笔者没有找到2.1版的手册,所以这里查看的是3.2版的手册,这两个版本间改动不大,所以查看3.2版的手册也是可以的。四个参数说明如下:
●where()
对应的原生SQL语句如下所示:
●order()
对应的原生SQL语句如下所示:
●field()
对应的原生SQL语句如下所示:
●limit()
对应的原生SQL语句如下所示:
可以构造如下写法让四个函数都用到:
生成的SQL语句如下所示:
提示:如果有不理解的地方,大部分原因都是你对PHP相关主流框架或者PHP原生的增删改查不是很了解。可以简单学习一下PHP的网站开发基础知识,再重新来看本书里的内容,你会觉得更轻松。
通过分析发现,在where位置、limit位置和order位置,如果构造可控参数的值为恶意语句的话,都可能会涉及php_safe.php文件中安全防御所使用的危险关键词。这里我们不去研究如何绕过这个防御脚本来进行注入,而是在现有的漏洞环境中分析漏洞产生的原因。但是在field位置,就可以直接构造出用于查询其他表中字段的攻击语句。回到ajax_arclist的开头往下走(参见图4-5),看如何执行第71行的语句。
在第35行我们看到了exit() (提示:终止语句意味着不往下继续执行),如果要绕过,给变量$model随便赋值一个存在白名单的字符串,就可以绕过并继续执行下面的代码,这里给$model赋值“article”。继续往下,在第37行判断是否有传递的表名前缀,如果有就与表名拼接;如果没有就继续往下执行。在第58行判断是否有传参,如果有传参page值就执行第64行;如果不传参$page就执行第70行。
4.2.5 Payload构造思路
想要知道账号和密码,就要查询dami_member表中的username、userpwd两个字段。dami_member是当前CMS所使用的数据库中的用户表,username和userpwd是当前用户表中的数据字段。想要执行如下语句:
就要给field传参:
构造链接访问以下地址。
在当前环境下,执行的语句如下。
注入的Payload被拼接闭合在原来的SQL语句中,导致显示了账号和密码的输出,证明了SQL注入漏洞的存在。