[安全入门教学]如何分析海洋CMS漏洞

看到freebuf上有一篇为《漏洞预警 | 海洋CMS(SEACMS)0day漏洞预警》的文章,展示了关于漏洞使用的POC,这里我们来完整的分析一下POC的原理以及漏洞成因。

我分析使用的版本是seacms6.53

其中post的数据如下:

searchtype=5&searchword={if{searchpage:year}&year=:e{searchpage:area}}&area=v{searchpage:letter}&letter=al{searchpage:lang}&yuyan=(join{searchpage:jq}&jq=($_P{searchpage:ver}&ver=OST[9]))&9[]=ph&9[]=pinfo();

关键的位置如下 search.php中:

在include/main.class.php parseIf()中,特殊构造的字段被送进了eval()中:

那接下来我们就看一看如何构造以及如何绕过各个过滤函数。

我们由上图可以看到,传入eval()的变量是$strIf,$strIf=$iar[1][$m]

那$iar又是由preg_match_all($labelRule,$content,$iar)获取

其中$labelRule = buildregx("{if:(.*?)}(.*?){end if}","is"),buildregx()在include/common.func.php中:

所以$labelRule = ‘/{if:(.*?)}(.*?){end if}/is’

因此根据preg_match_all()的用法,返回的数组中$ iar [0]保存完整模式的所有匹配, $ iar [1] 保存第一个子组的所有匹配,即{if:(.*?)}中的(.*?)的内容,$ iar [2] 保存第二个(.*?)指代的内容。所以我们的目标就是构造一个满足正则的参数。

我们再回到之前的search.php,可以看到,传入parseIf()的参数是$content,而$content,我们向上看可以看到这个变量经过了很多很多很多的变化。。。看的似乎要晕过去,不过我们还是要慢慢理一理。。。

我们可以看到$content经过了多次的str_replace(),也就是多次的替换:

看到多次的替换,我们可以想到什么呢,重复替换,什么意思呢,拿上图举例子,str_replace()替换的规则是将参数3中的参数1替换成参数2,因为语句是逐条执行的,所以如果我们在$page中包含下一条替换的内容{seacms:searchword},那么执行下一条语句的时候就会替换{seacms:searchword}这个位置,然后我们在$searchword中再包含下一条语句替换的{seacms:searchnum},以此类推,那么这样就可以达到一种拆分替换的方法,达到绕过关键词检测的目的。

然后我们就需要找到这些被替换的位置在哪里,通过全局搜索,我们会找到像{searchpage:year},{searchpage:area},{searchpage:letter}等包含这些要被替换的标签的页面是templets\default\html\cascade.html,但是在页面内{searchpage:page},{seacms:searchnum}等是不存在的,因此在选取被替换的变量要经过筛选。

因此我们需要使用页面cascade.html的话,阅读一下代码,大致推断出要满足$searchtype=5.

我看看到poc选取的第一个变量是$searchword,因为{seacms:searchword}是存在于页面中的。然后后面的变量选取的话,思路就是之前那样,只要按照顺序下来就可以了。代码顺序如下:

所以整个思路有了,那接下来就是看一下我们如果传入参数,会有哪些过滤。

还是在search.php页面中,上移我们会看到这些变量传入都经过RemoveXSS(),addslashes(), cn_substr()

在include/common.func.php中我们可以看到RemoveXSS()就是常见的XSS防御函数,把关键字段给过滤了。

addslashes()就是php自带的转义函数,就是对某些字符前加上反斜杠。

cn_substr()同样是在include/common.func.php中,对变量的长度做了限制,长度限制为20,

这几个函数就不贴了,因为有点长,想看内容的话可以自己打开源码看一看。

然后在\include\common.php中也有输入检查函数,差不多是全局的addslashes()。

那过滤手段我们知道了,就是全局的addslashes(),加上一些关键词过过滤,所以我们就要避开这些雷区,addslashes()一般是防注入的,但是RemoveXSS()却过滤了很多字段,那这时候,就要用上我们之前的那个连续替换的方法来绕过了。

但是由于20这个长度限制, 即使使用拼接的手段依然又限制,因此这里还使用了另外一个相对猥琐的思路。

POST的数据最后几个数据:

&yuyan=(join{searchpage:jq}&jq=($_P{searchpage:ver}&ver=OST[9]))&9[]=ph&9[]=pinfo();

这里拼接起来其实是$_POST[9],然后在$_POST[9][]中传入数据,而且最重要的是,我们可以在这里把想要执行的代码不停的拆分就可以了!我们传入对的数据被拆分后放在了$_POST[9]这个数组里,而&yuyan=(join{searchpage:jq},这里拼接上去的是join()函数,join()根据php手册中介绍是implode()的别名,作用是将一个一维数组的值转化为字符串,但是如果使用implode()长度将超过20,因为我们可以发现&yuyan使用join()长度这样刚好是20,完美的绕过,到达拼接我们后面上传的数组的目的。而这个9其实只是一个key值,是可以随意更改的,后面的&9[]=ph&9[]=pinfo();其实只要能绕过过滤函数,长度小于20,那就随便执行命令啦,这里直接改成&9[]=phpinfo();都是没有问题的!

这就是对该漏洞以及对应POC的分析过程,毕竟菜鸡刚上路,有啥不好不全的地方请狂吐槽!

原文发布于微信公众号 - 安恒网络空间安全讲武堂(gh_fa1e45032807)

原文发表时间:2017-10-20

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏李航的专栏

Shell 主要逻辑源码级分析:SHELL 运行流程 (1)

分享一下在学校的时候分析shell源码的一些收获,帮助大家了解shell的一个工作流程,从软件设计的角度,看看shell这样一个历史悠久的软件的一些设计优点和缺...

2.1K0
来自专栏LanceToBigData

struts2(三)之表单参数自动封装与参数类型自动转换

前言   对struts2的使用不外乎这几点,参数自动封装,拦截器的使用,数据校验,ognl表达(值栈和actionContext的讲解),struts2的标签...

24310
来自专栏用户2442861的专栏

《Java虚拟机原理图解》 1.1、class文件基本组织结构

http://blog.csdn.net/luanlouis/article/details/39892027

882
来自专栏CDA数据分析师

Python中eval带来的潜在风险,你知道吗?

00 前言 eval是Python用于执行python表达式的一个内置函数,使用eval,可以很方便的将字符串动态执行。比如下列代码: >>> eval("1+...

4948
来自专栏纯洁的微笑

jvm系列(一):java类的加载机制

1、什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Cl...

3846
来自专栏java一日一条

【Java并发编程】使用wait/notify/notifyAll实现线程间通信的几点重要说明

在 Java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信。在线程中调用 wait(...

883
来自专栏专注 Java 基础分享

虚拟机类加载机制

虚拟机把字节码文件从磁盘加载进内存的这个过程,我们可以粗糙的称之为「类加载」,因为「类加载」不仅仅是读取一段字节码文件那么简单,虚拟机还要进行必要的「验证」、「...

4657
来自专栏技术记录

Protobuf3语法详解

9315
来自专栏地方网络工作室的专栏

Shell 命令行,实现一个获取任意位数的随机密码的脚本

Shell 命令行,实现一个获取任意位数的随机密码的脚本 每次我们想要获得一个密码的时候都很头疼,于是我之前自己用nodejs写了一个 Shell 脚本。这两天...

2006
来自专栏欧阳大哥的轮子

深入解构iOS系统下的全局对象和初始化函数

事件源于接入了一个第三方库导致应用出现了大量的crash记录,很奇怪的是这么多的crash居然没有收到用户的反馈信息! 在这个过程中每个崩溃栈的信息都明确的指向...

1992

扫码关注云+社区

领取腾讯云代金券