代码审计| 从今天起,做一个精致的多米咖!

0x00 背景

在看CNVD漏洞库的时候发现有师傅发了某cms前台SQL注入漏洞,通过查阅漏洞描述可知道存在问题的参数是cardpwd,便开始尝试对该版本的cms进行审计。 源码下载地址:https://pan.baidu.com/s/1jIMhDK6 漏洞来源地址:http://www.cnvd.org.cn/flaw/show/CNVD-2017-22079

0x01 审计过程

1.下载好源码后在本地部署,然后使用seay 源代码审计系统加载源码文件,查找关键字cardpwd得到如下信息,cardpwd参数是在member/mypay.php文件中以POST的方式接收使用的。

2.进入到member/mypay.php文件(26行和38行)在接收cardpwd参数的值之前还需要进行登录,并且满足$dm=='mypay'

3.继续跟进$dm并未发现变量被创建的位置,最后在/duomiphp/common.php(52行-55行)中发现接收了GET,POST以及COOKIE中的参数和值,并且创建相应的变量赋予接收到的值,此处可能还存在变量覆盖的问题,本文咱不讨论。

● 到这里我们知道需要访问这个功能需要满足两个条件: (1)注册并登录

(2)在GET,POST或COOKIE中提交dm=mypay

4.注册会员后访问站点的/member/mypay.php文件,http://127.0.0.1/dm132/member/mypay.php 简单输入1,2 进行请求,确定可以提交cardpwd参数。

5.继续阅读/member/mypay.php在43-63行之间是对cardkey,cardpwd的处理,并使用了正则[^0-9a-z@\._-]{1,}(union|sleep|benchmark|load_file|outfile)[^0-9a-z@\.-]{1,} 对数据进行检测,分析并对这个正则进行测试,发现使用/*!50000 xxxx*/便可以绕过。

6.本以为过滤规则是如此简单,经过测试发现还有其他过滤规则,继续往下分析发现/member/mypay.php在63行要执行SQL语句的时候还使用了GetOne的函数,定位到这个函数所在的位置/doumiphp/sql.class.php的277-300行,GetOne大致做了以下的事情,先清理掉字符串最后面的,和;然后拼接上limit 0,1;使查询的结果只返回一行。

正如代码上面所注释的“//执行一个SQL语句,返回前一条记录或仅返回一条记录”

7.拼接后在/doumiphp/sql.class.php在288行执行了SetQuery方法,290行执行了Execute方法,跟进Execute方法,在234行-269行发现了Execute方法的代码块,其中CheckSql是一个关键名称的方法,并且在上方有注释说用于SQL安全检查。

8.跟进CheckSql在/doumiphp/sql.class.php的537行-642行发现CheckSql方法的代码块,上面有提示 //SQL语句过滤程序,由80sec提供,这里作了适当的修改,经过测试这个过滤规则在598行过滤了/*而且是用硬匹配的方法,所以无法绕过,因此第5步的 /*!50000 xxxx*/的payload无法使用。

9.联合查询无法使用的情况下就想到的子查询的方法,经过测试无法绕过628行过滤的正则~\([^)]*?select~s进行子查询,无括号的子查询貌似是不存在的吧~ ?! 此时此刻已经是对这个过滤规则研究了一整天,后面也请教了几位师傅,也没有好的方法进行无括号的子查询,其实官方文档上也说了子查询必须有括号。

10.当没有思路或测试进行不下去的时候,就一定要回头看看一路走来所获得的信息,往往有小惊喜遗留在路上 By Thinking!继续看CNVD中的描述,系统未对变量进行过滤 我突然觉得我下载的是假的源码!!

这个CMS在/member/mypay.php页面提交任意充值卡号,在卡号密码处使用’or’ 1可以实现任意充值,使用报错注入可以获取user() 或version()的数据,但是无法进行子查询。

● 先在后台添加了充值卡。

● 在前台注册并登录后,将cardpwd的值设置为’or’ 1提交,便可以任意充值了,虽然这算一个漏洞,但是在我的观念中这种漏洞不太能说服我。

11.使用灰盒方式先测试下/member/mypay.php的cardpwd参数

被/member/mypay.php在43-63行之间的正则过滤了。

被/doumiphp/sql.class.php的537行-642行的CheckSql方法过滤了。

未进行'的闭合导致SQL语句报错了。

12.到这里发现了一个小细节,多了一个'的导致SQL语句报错了?!,在报错信息中发现了插入的cardpwd的值,而不是先提示被过滤了,所以此处肯定有问题,经过测试是第2种情况。

根据这个现象可以推测两种可能: ❂SQL语句在被过滤前就执行了(×)

❂多了'导致注入语句被绕过(√)

0x02 确定问题位置

1.确定位我使用的方法是在关键位置使用echo 将传入的cardpwd数据在处理过程中打印出来,首先是在使用/*! 50000union */的情况下提示:Safe Alert: Request Error step 2! 找到这个关键字所在的位置,/doumiphp/sql.class.php的635行,刚好在CheckSql方法内。

2.在561-586行有个 //完整的SQL检查之前一直都在关注过滤规则,并没有在意这个代码块,因为这个CMS都是明文传输,密码处可能会有一些关键字符会触发SQL检测的规则,为了避免这种情况,开发人员便写了这个代码块,用于将SQL语句中两个单引号包裹的数据进行替换处理。

3.直接在589行处插入echo $clean;将经过这个代码块的数据打印出来,发现确实将单引号内的字符变成了$s$。

4.在640行处插入echo $db_string;将通过检测的数据打印出来。

开发人员为了避免类似密码处的关键字符被过滤,而设计的方法反而让过滤规则绕过成为了可能。 数据跟踪:cardpwd->$pwd->GetOne()->Execute()->CheckSql()->$clean->$db_string $clean->$db_string 的过程是先经过把数据进行处理(//完整的SQL检查)再赋给$clean,然后把$clean传到各种SQL注入检测规则中,全部通过后返回原始数$db_string,幸运的是在各种过滤规则中没有过滤 `,’,” 号。

因此仅需要利用//完整的SQL检查中的被单引号包裹的字符会被替换为$s$这个功能。

0x03 构造PAYLOAD

要利用单引号将字符包裹且不影响SQL语义,单引号就必需要被转义!

转义单引号的方法:

●SQL注释法:

1./*'*/

2./*!60000'*/

●反引号方法:

1.` ’ `

●双引号方法:

1." ' "

因此可以构造如下PAYLOAD:

利用双引号和反引号:

bypass’or”‘“or extractvalue(1,(select group_concat(0x3a,name,0x3a,password) from duomi_admin`’`))or ‘1

方便阅读将处理后的结果打印出来:

利用两个双引号:

bypass’or”‘“or extractvalue(1,(select group_concat(0x3a,name,0x3a,password) from duomi_admin))or “’”=

方便阅读将处理后的结果打印出来:

0x04 小小总结

其实这个漏洞整个思路还是很清晰对的审计起来也不困难,但是因为一开始将重点放在了过滤规则的绕过上面,导致花费太多精力在分析正则上,所以当过滤规则有些时候强绕绕不过,或许可以看下代码的其他上下文相关信息,这次的较为灵活的利用//完整的SQL检查绕过了全局的SQL安全检测,本篇绕过了全局的检测,凡是使用到了CheckSql()方法的位置都存在这个问题,最后感谢师傅们的各种协助和讨论。

原文发布于微信公众号 - 漏斗社区(newdooneSec)

原文发表时间:2017-12-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏黑白安全

PHP代码审计入门之路

虽然市面上的代码审计的文章已经一大把了,但是还是决定重复造轮子,打算作为一个系列来写的,近年越来越多的安全研究人员投入到php应用的漏洞挖掘,相对应的代码安全问...

11720
来自专栏杨建荣的学习笔记

MySQL频繁停库的问题分析(r12笔记第33天)

最近也抽空帮一些网友解决一些问题,有些是Oracle,有些是MySQL,有时候虽然忙忙乎乎,但是解决问题之后还是很有成就感的。 今天来说一个蛮有意思的问...

575100
来自专栏搜云库

Spring Boot 中使用 MongoDB 增删改查

本文快速入门,MongoDB 结合SpringBoot starter-data-mongodb 进行增删改查

62370
来自专栏一枝花算不算浪漫

[Java面试七]Mybatis总结以及在面试中的一些问题.

505140
来自专栏大史住在大前端

一统江湖的大前端(4)shell.js——穿上马甲我照样认识你

码农界存在着无数条鄙视链,linux使用者对windows的鄙视便是其中之一,cli使用者对GUI用户的嘲讽也是如此,在这样一个讲究逼格的时代,如果你的桌面上没...

24250
来自专栏漏斗社区

玩得一手好注入之order by排序篇

看了之前Gr36_前辈在先知上的议题,其中有提到排序注入,最近经常遇到这样的问题,所以先总结下order by 排序注入的知识。 环境信息 测试环境:操作...

40060
来自专栏码农阿宇

ElasticSearch入门 附.Net Core例子

Elasticsearch是基于Lucene的搜索引擎。它提供了一个分布式,支持多租户的全文搜索引擎,它具有HTTP Web界面和无模式JSON文档。 Elas...

74110
来自专栏技术小讲堂

LINQ to SQL(4):OR设计器

在我们使用LINQ to SQL的时候,需要大量的使用OR设计器,虽然我们手工写代码也是完全可以实现的,但是OR设计器是非常强大的工具,我想有了它,没有几个人会...

308100
来自专栏数据库新发现

Php部分常见问题总结

Php部分常见问题总结 作者:未知 来源:未知 1:为什么我得不到变量

16220
来自专栏Spark学习技巧

初识PB级数据分析利器Prestodb

初始PB级数据分析利器Prestodb 什么是prestodb prestodb整体架构 物理执行计划 什么是prestodb prestodb,是facebo...

68850

扫码关注云+社区

领取腾讯云代金券