![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=1739346173-qmuVisrY5eErQOwm0M5liTujnOOnfJ88-0-ccd5b660a12017243e0bf16075d01410)
图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=1739346173-GfwwEupQjMr0YXQpLmklHWG00Qk7HVFK-0-7458805c668da08cefc5fcfd2cde14b9)
图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=1739346173-AfFwzNS3W3gd0wAML75sUFV1F3dOC1GD-0-43d2811f70f62b3b64d5385dffe14eeb)
图4-3 index.php源代码
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/4-4.jpg?sign=1739346173-5v11Ud2yo9KXvhr7rGyXJjzHSH6tI1M0-0-8067033fccae842ddaf8f364c7d829ea)
图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=1739346173-rdFj22zLGgVTMWwHTPoE3AtRmFouF1IE-0-e8ac9bcf3d52a377949a384fa753bf3c)
图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=1739346173-H00u59Zc7XMajOEaXA65MxsejYQ8Kdvk-0-d3e1b9d1063d754a2325ffb1a45de414)
图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=1739346173-wKDDjifIfDEo9fykqr050D0gUIoNuB0i-0-c0c09747297c555518f3cb78bb356854)
在显示的结果中可以看到版本号为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=1739346173-pXBAdrEjFq3aEuY04FE6gqoYF9rUYu6g-0-6f72eb6b3129e32e608ad7326fa569b5)
对应的原生SQL语句如下所示:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/065-2-i.jpg?sign=1739346173-1dXMZWR11Qj44a25x85xDMZlzSXXRz6T-0-d3565e8bb90b1dbc7f3f7e3c5c24b066)
●order()
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/065-3-i.jpg?sign=1739346173-eeqAhby2LuexfUMYvuBIRxE6gS8squIO-0-e82cd9f57cb42a762a8cf60c1ce4a253)
对应的原生SQL语句如下所示:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/065-4-i.jpg?sign=1739346173-6sD4eRxCMUKBF3ktfY36faZBCRAMlvk1-0-10c99bab6dae5021516be1be1e1219a6)
●field()
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/065-5-i.jpg?sign=1739346173-UdTTelrAVoEr6L7rKDmQrWUBS7Qj913K-0-b8f4d172721e911ebbb32248fdc15668)
对应的原生SQL语句如下所示:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-i.jpg?sign=1739346173-RzVEQkxhEpTIEIolFbpHtpHQ8PitIX11-0-ae2dfedf3724401c7d78636fecd1be2a)
●limit()
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-2-i.jpg?sign=1739346173-uWW1NZxMv2hEoK8ohWn1Nds16tTyDk4X-0-69e471febba0c8bf17c12ced44f95318)
对应的原生SQL语句如下所示:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-3-i.jpg?sign=1739346173-tPfPwsubNL2lA552QAX8zFO0ud3TUT8s-0-e287527b7d86c157842606a55d2fc00e)
可以构造如下写法让四个函数都用到:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-4-i.jpg?sign=1739346173-vHdhyyFGR9MKYs8YRC8LAJiPGh7gh2RG-0-e3bc9a04ffd6006148c6d4326323785d)
生成的SQL语句如下所示:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-5-i.jpg?sign=1739346173-aUm6EbMgOc6M6QcCsfIy7PCx0jmjIY1z-0-9cee76f4223c77aea911530685aa2aff)
提示:如果有不理解的地方,大部分原因都是你对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=1739346173-4ooC0OGk7JlhzNxustOKZ0u7bm1jWRMk-0-291f375c806f09fa35dc5366e62a1fcb)
就要给field传参:
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/066-7-i.jpg?sign=1739346173-ybNYwK08Lw8vZbrPFgEO6oVrAO8mhOYg-0-8eb21c9c76cebeb540f50081e451dcfd)
构造链接访问以下地址。
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/067-i.jpg?sign=1739346173-kC5Cu5RRwKFISqPbmmkGy46rupxO4Tsm-0-04e1b6d03962a9ae54e23ebebbc441ba)
在当前环境下,执行的语句如下。
![](https://epubservercos.yuewen.com/AAB7B5/21233234301205006/epubprivate/OEBPS/Images/067-2-i.jpg?sign=1739346173-wsGV1jDiaAwU3TA58K5nltBCzVwwWGHl-0-679df64d9abd54bab81edb57c9260de4)
注入的Payload被拼接闭合在原来的SQL语句中,导致显示了账号和密码的输出,证明了SQL注入漏洞的存在。