初入代码审计之lmxcms代码执行
环境搭建
梦想 cms下载链接 http://www.lmxcms.com/down/
phpstudy 搭建,然后访问 /install
目录基本安装,搭建好后访问如下:
数据库配置如下:
工具扫描入口
工具先跑一手,seay 扫描结果:
昆仑镜扫描结果:
显示的结果还是挺多的, lmxcms 的 sql 注入和任意文件删除上传网上已经有很多师傅复现了,但是代码执行的较少并且也不是很全,那么就直接从这里入手开始审计。
代码执行审计
c\admin\AcquisiAction.class.php
看到上面工具显示存在 eval 函数,可能有命令执行,先看 c\admin\AcquisiAction.class.php
文件中的
看到是执行的 $data=$temdata['data']
,而 $temdata
变量和参数 cid 有关。先测试如果可以控制 $temdata
变量能否进行命令执行,
发现是可以的,那么调试看看,这里是怎么处理 cid 来得到的 $temdata
变量。登录进后台进行方法调用,先随便给 cid 传值
/admin.php?m=Acquisi&a=showCjData&id=1&lid=3&cid=2
调试发现这里就是通过去数据库的 lmx_cj_data 查找 id=cid 值得数据
然后返回数据就为 $temdata
变量(上面这些数据库数据是我直接写入数据库的)。然后只需要 $temdata
数组的 data 索引。
最后形成命令执行,
所以现在需要找的就是在哪里可以向数据库中的 lmx_cj_data 表的 data 字段写入恶意命令。
发现就是在采集模块处会把采集得到的数据插入到该表中,但是不知道是不是 php 版本问题,我的采集模块采集功能一直报错,按理说能控制采集结果并将其导入到数据库应该就能够进行命令执行(可以利用自己云服务器搭建恶意页面来被采集),那除了采集还有没有其他办法呢?
在后台发现一处可以直接执行 sql 语句的地方:
执行插入语句:
INSERT INTO `lmxcms`.`lmx_cj_data` (`id`, `lid`, `data`, `uid`, `url`, `time`) VALUES (3, 4, 'phpinfo()', 33, 'a', 5);
看到成功插入到了 lmx_cj_data
表中,
最后再访问
/admin.php?m=Acquisi&a=showCjData&id=1&lid=4&cid=3
就可以成功实现任意命令执行。
class\Action.class.php
这里先不用管怎么调用的,看到 method_exists($this,$a)
说明只能调用该对象有的方法。看了一下该对象的其他方法并没有什么可以利用的点,
所以这里并不能实现任意命令执行。
m\AcquisiModel.class.php
查看含有 eval 函数的代码,
如果数组参数存在对应索引值那么先利用 string::stripslashes
处理(去掉反斜杠),然后利用 eval 执行代码。看看哪些调用了 formatData
函数,
不难看到两个 $data
都是根据 lid 去数据库查到的对应的值,只是过程不一样。调试发现两个都是查询的 lmx_cj_list 表。
所以如果可以控制数据库中的 lmx_cj_list 表值的话,那么就能控制 formatData
函数参数。但是发现表中的 array 段值是固定的,那么再看看 remove_html ,
测试发现这里的 remove_html 只能在以下几个进行选择,并不能自己输入,
不过这都不影响,后台有执行sql语句的地方,我们直接在后台执行 sql 语句向数据库表中插入数据,
INSERT INTO `lmxcms`.`lmx_cj_list` (`lid`, `lname`, `uid`, `array`, `url_str`, `classid`, `lcontent`, `info_page_regular`, `content_url_box`, `content_url_regular`, `ztid`, `tagsname`, `str_y_replace`, `str_n_replace`, `str_remove`, `remove_html`, `list_dy_url`, `list_url_tem`, `pre_page`, `fix_page`) VALUES (3, 'ts', 3, 'phpinfo()', 'ss', 6, 's', 'a', 's', 's', 'a', 's', 's', 's', 's', 's', 's', 's', 's', 's');
向段名 array 或者是 remove_html 插入都可以,然后也能成功执行命令。
调用以下方法都可以造成代码执行
/admin.php?m=Acquisi&a=testcj&lid=1
/admin.php?m=Acquisi&a=startCaiji&lid=1
/admin.php?m=Acquisi&a=againCaiji&lid=1
继续看该文件其他的 eval 处,
看到注释的根据 id 入库一条采集数据,会先进行判断是否已经入库然后判断是否存在数据,这里的入库并不是指写入到数据库,而是说从数据库把数据显示到 web 页面上
然后又知道是否显示是根据数据中的 is_in 段值来判断的,所以直接添加一条未入库数据(因为我的采集模块有问题嘛)
查找哪里对方法 in_mysql_one
方法进行了调用。
发现有三处地方,分别对应三个功能:
- 入库全部未入库的信息
- 批量入库
- 根据 id 和 classid 入库一条采集数据
这里选择第三个功能,跟进到函数,没什么需要特别说明的,就是简单的处理参数进行入库
进行调用
?m=Acquisi&a=one_inMysql&id=4&classid=11
调试看到 $data
确实是数据库中未入库的数据信息
然后执行命令成功
另外两个功能模块也是相同的效果没什么好说的,数据库的数据同样利用 sql 语句进行插入即可。
plug\smarty\Smarty.class.php
定位到 eval 代码位置,
看看哪里进行的调用
最后一直溯源到达 plug\smarty\Smarty_Compiler.class.php
文件中的函数 _compile_tag
,
这个函数就是处理模板中 html 文件标签的,根据不同标签进行不同的处理,这里需要的是 insert 标签,这个标签调用的 _compile_insert_tag
可以一步一步到达 _eval
。不过注意到这里的 php 标签最后的代码:
return '<?php ' . $block[3] .' ?>';
$block[3]
可控,就是 php 标签中的内容,这里是不是也可以构成命令执行呢?测试一下
编辑首页 index.html 文件,添加
<{php}>
system('whoami');
<{/php}>
在 php 处下下断点,然后刷新首页,
看到 $block
经过处理后是一个数组,$block[3]
就是我们的命令,
最后成功执行命令,
在回到刚刚的 insert 标签中,发现我直接在 html 中添加的 inset 标签并不能到 case insert
,那么直接把命令搬到 case php
中,进行调试,测试发现参数需要时这种形式
home=aa
发现跟进到这里并不能进入函数 smarty_core_run_insert_handler
中
会把其直接当作字符串进行拼接,
根本就没有进入方法。
后续又从_eval 进行朔源了几个方法基本上都是这种情况。
plug\smarty\plugins\function.math.php
还是同样定位到存在 eval 函数的地方
这里直接看谁调用了方法
发现没被调用,那么这个地方是肯定执行不了了。
其他就没有存在 eval 函数并且里面存在可控变量的了。到此代码执行的代码审计也就算结束了,有四处地方可以进行代码执行。