![Web代码安全漏洞深度剖析](https://wfqqreader-1252317822.image.myqcloud.com/cover/20/40868020/b_40868020.jpg)
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所示)。
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/4-1.jpg?sign=1738935150-L9kwE3K0R2NTjQnSNDw19CXauQJ1hcu7-0-2e3bd42d828159429857a33522c0b6d8)
图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:缓存目录,一般缓存的内容都会生成在这里面。
●伪静态文件:对程序做伪静态的配置。
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/4-2.jpg?sign=1738935150-GKCNinAR6scH9BlFdOIpADZq502FinLG-0-d3d2895fce08d0c11a2f3cf0c333b684)
图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方式传送的参数都要进行过滤。
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/4-3.jpg?sign=1738935150-Ts2ZHU5eZh64ycEJpFL85aTaOyVlzmr2-0-99d703a9cf4c1b1c0449c48f659ec284)
图4-3 index.php源代码
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/4-4.jpg?sign=1738935150-FXsCYu3daXtsDe9gwg3QglPywXB4WS12-0-835d66d6d6d56b25e6a1b041a9164918)
图4-4 防御脚本的代码
2.通过漏洞连接定位漏洞位置
通过以上分析,根据漏洞链接可以定位到漏洞位置在\Web\Lib\Action\ApiAction.class.php的ajax_arclist方法中,如图4-5所示。
提示:一般情况下,我们把自己写的类里面的方法叫作方法,PHP自带的方法叫作函数。
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/4-5.jpg?sign=1738935150-763n3TrbfWVPt5OpgYQALnJNubqBaytu-0-60860e7167f87f691d7e3ef24bf0c0d9)
图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版本相关。
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/4-6.jpg?sign=1738935150-8lChmg9PNJVLgHZd9QfdC1Hht5OUDWPg-0-9b7599856e23b187bc524d0e9ea6f40b)
图4-6 inject_check方法
回到上述三个SQL语句,可以看到可控参数被分别带入了where()、order()、field()、limit()方法中。由于本程序使用的是ThinkPHP框架,因此这四种方法的使用可查看ThinkPHP的手册。由于ThinkPHP版本很多,最好先打印一下ThinkPHP的版本,在ajax_arclist方法开始处输入如下代码:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/65.jpg?sign=1738935150-OrLc41wIsNL4qSt1TmKxhB8NDsPpCBJr-0-1fc9c8ff81a2c20f8352fb8c764ec2cc)
在显示的结果中可以看到版本号为2.1。
提示:这里也可以用断点调试,可根据使用习惯而定。
然后去查看ThinkPHP 2.1版的手册。因为笔者没有找到2.1版的手册,所以这里查看的是3.2版的手册,这两个版本间改动不大,所以查看3.2版的手册也是可以的。四个参数说明如下:
●where()
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/065-i.jpg?sign=1738935150-KldN6QHZz0jKe714c8lZMYEje1dANl9v-0-c7cce5bb39828a8c9b8282e45f0d2088)
对应的原生SQL语句如下所示:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/065-2-i.jpg?sign=1738935150-OLBalv44p68QZtE9RKgtfxmA5pCWOXbH-0-2861ae4dae7a0f327645b21eb04a9fb1)
●order()
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/065-3-i.jpg?sign=1738935150-PRlcw0UjB0r0RItXzF5Rz85N1nV4ovIm-0-69bcaaa4461b8723b0d1f9afc22ab485)
对应的原生SQL语句如下所示:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/065-4-i.jpg?sign=1738935150-H0xzqTrenkTbvi0YFsEREQL3mV6XeOgi-0-8cadfb5500bcf3a67379219cd85028c5)
●field()
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/065-5-i.jpg?sign=1738935150-t7rllfuDuoC4RO4FHKOV9hO7I57WBWzw-0-e31aa09b1c339d086ebd41cc7df56bf1)
对应的原生SQL语句如下所示:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-i.jpg?sign=1738935150-S4Bub6YcBuILUZpX4B9oOOMJimHoBn7j-0-dbadf54d02ea7a16c563c59732011697)
●limit()
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-2-i.jpg?sign=1738935150-u8tzCNj91WYnGEYKTjUags3Tstqoxsgd-0-82ecf047472a28aff12fe9037e937ac3)
对应的原生SQL语句如下所示:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-3-i.jpg?sign=1738935150-4l0K6lT9sMvuhSmfK3w744c2k8Y3lyD1-0-c2f8608e3d8731d2a54598b880c8936c)
可以构造如下写法让四个函数都用到:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-4-i.jpg?sign=1738935150-A7WV03VpaRsqFPWNbvHsCex8TVsR293M-0-d36a235b9dc5c5d4f1097d8d57d6af7b)
生成的SQL语句如下所示:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-5-i.jpg?sign=1738935150-9QgnnbrGhXqW8rnvlwT74PFAtgYLL6sM-0-c459568751c9a212b96ef484d4f04d0a)
提示:如果有不理解的地方,大部分原因都是你对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是当前用户表中的数据字段。想要执行如下语句:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-6-i.jpg?sign=1738935150-Haiwq1QEKfRqCqXYAQ9we9bUSwrb3Moa-0-e02acf6ddf426d2810ada26c2a6ddc2a)
就要给field传参:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-7-i.jpg?sign=1738935150-1avkAfygb21yym0c2fg4dKsU6IAN97pU-0-87b7cca981e33470032b3bf9d6b68f44)
构造链接访问以下地址。
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/067-i.jpg?sign=1738935150-IvP9GTtCrUR4iw79w5fn8iYtAZVzGkoo-0-3ef6d09e8cf734408aac944789f92c3b)
在当前环境下,执行的语句如下。
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/067-2-i.jpg?sign=1738935150-U4i4BHtyBspfvHZog6yaGGzjCLcd38Xl-0-de800315f9a042daa47000117769e842)
注入的Payload被拼接闭合在原来的SQL语句中,导致显示了账号和密码的输出,证明了SQL注入漏洞的存在。